kernel/abi/xv6/riscv64/
mod.rs

1#[macro_use]
2mod macros;
3mod file;
4pub mod fs;
5mod pipe;
6mod proc;
7
8// pub mod drivers;
9
10use alloc::{
11    boxed::Box,
12    string::{String, ToString},
13    sync::Arc,
14    vec::Vec,
15};
16use core::sync::atomic::Ordering;
17use file::{sys_dup, sys_exec, sys_mknod, sys_open, sys_write};
18use hashbrown::HashMap;
19use proc::{sys_exit, sys_fork, sys_getpid, sys_sleep, sys_wait};
20
21use crate::{
22    abi::{
23        AbiModule,
24        xv6::riscv64::{
25            file::{sys_close, sys_fstat, sys_link, sys_mkdir, sys_read, sys_unlink},
26            pipe::sys_pipe,
27            proc::{sys_chdir, sys_sbrk},
28        },
29    },
30    arch::{self, IntRegisters},
31    early_initcall,
32    fs::{
33        FileSystemError, FileSystemErrorKind, SeekFrom, VfsManager, drivers::overlayfs::OverlayFS,
34    },
35    register_abi,
36    task::elf_loader::load_elf_into_task,
37    vm::setup_user_stack,
38};
39
40const MAX_FDS: usize = 1024; // Maximum number of file descriptors
41
42#[derive(Clone)]
43pub struct Xv6Riscv64Abi {
44    /// Task namespace for xv6 process management
45    namespace: Arc<crate::task::namespace::TaskNamespace>,
46    /// File descriptor to handle mapping (fd -> handle)
47    fd_to_handle: HashMap<usize, u32>,
48    /// Free file descriptor list for O(1) allocation/deallocation
49    free_fds: Vec<usize>,
50}
51
52impl Default for Xv6Riscv64Abi {
53    fn default() -> Self {
54        // Initialize free_fds with all available file descriptors (0 to MAX_FDS-1)
55        // Pop from the end so fd 0, 1, 2 are allocated first
56        let mut free_fds: Vec<usize> = (0..MAX_FDS).collect();
57        free_fds.reverse(); // Reverse so fd 0 is at the end and allocated first
58
59        // Use root namespace by default for cross-ABI task visibility
60        // Separate namespaces can be created explicitly when needed (e.g., containers, cgroups)
61        let namespace = crate::task::namespace::get_root_namespace().clone();
62
63        Self {
64            namespace,
65            fd_to_handle: HashMap::new(), // Empty HashMap
66            free_fds,
67        }
68    }
69}
70
71impl Xv6Riscv64Abi {
72    /// Allocate a new file descriptor and map it to a handle
73    pub fn allocate_fd(&mut self, handle: u32) -> Result<usize, &'static str> {
74        let fd = if let Some(freed_fd) = self.free_fds.pop() {
75            // Reuse a previously freed file descriptor (O(1))
76            freed_fd
77        } else {
78            // No more file descriptors available
79            return Err("Too many open files");
80        };
81
82        self.fd_to_handle.insert(fd, handle);
83        Ok(fd)
84    }
85
86    /// Get handle from file descriptor
87    pub fn get_handle(&self, fd: usize) -> Option<u32> {
88        if fd < MAX_FDS {
89            self.fd_to_handle.get(&fd).copied()
90        } else {
91            None
92        }
93    }
94
95    /// Remove file descriptor mapping
96    pub fn remove_fd(&mut self, fd: usize) -> Option<u32> {
97        if fd < MAX_FDS {
98            if let Some(handle) = self.fd_to_handle.remove(&fd) {
99                // Add the freed fd back to the free list for reuse (O(1))
100                self.free_fds.push(fd);
101                Some(handle)
102            } else {
103                None
104            }
105        } else {
106            None
107        }
108    }
109
110    /// Find file descriptor by handle (linear search)
111    pub fn find_fd_by_handle(&self, handle: u32) -> Option<usize> {
112        for (&fd, &mapped_handle) in self.fd_to_handle.iter() {
113            if mapped_handle == handle {
114                return Some(fd);
115            }
116        }
117        None
118    }
119
120    /// Remove handle mapping (requires linear search)
121    pub fn remove_handle(&mut self, handle: u32) -> Option<usize> {
122        if let Some(fd) = self.find_fd_by_handle(handle) {
123            self.fd_to_handle.remove(&fd);
124            self.free_fds.push(fd);
125            Some(fd)
126        } else {
127            None
128        }
129    }
130
131    /// Initialize standard file descriptors (stdin, stdout, stderr)
132    pub fn init_std_fds(&mut self, stdin_handle: u32, stdout_handle: u32, stderr_handle: u32) {
133        // XV6 convention: fd 0 = stdin, fd 1 = stdout, fd 2 = stderr
134        self.fd_to_handle.insert(0, stdin_handle);
135        self.fd_to_handle.insert(1, stdout_handle);
136        self.fd_to_handle.insert(2, stderr_handle);
137
138        // Remove std fds from free list
139        self.free_fds.retain(|&fd| fd != 0 && fd != 1 && fd != 2);
140    }
141
142    /// Get total number of allocated file descriptors
143    pub fn fd_count(&self) -> usize {
144        self.fd_to_handle.len()
145    }
146
147    /// Get the list of allocated file descriptors (for debugging)
148    pub fn allocated_fds(&self) -> Vec<usize> {
149        self.fd_to_handle.keys().copied().collect()
150    }
151}
152
153impl AbiModule for Xv6Riscv64Abi {
154    fn name() -> &'static str {
155        "xv6-riscv64"
156    }
157
158    fn get_name(&self) -> alloc::string::String {
159        Self::name().to_string()
160    }
161
162    fn clone_boxed(&self) -> alloc::boxed::Box<dyn AbiModule + Send + Sync> {
163        Box::new(self.clone()) // Xv6Riscv64Abi is Copy, so we can dereference and copy
164    }
165
166    fn handle_syscall(
167        &mut self,
168        trapframe: &mut crate::arch::Trapframe,
169    ) -> Result<usize, &'static str> {
170        syscall_handler(self, trapframe)
171    }
172
173    fn can_execute_binary(
174        &self,
175        file_object: &crate::object::KernelObject,
176        file_path: &str,
177        current_abi: Option<&(dyn AbiModule + Send + Sync)>,
178    ) -> Option<u8> {
179        // Stage 1: Basic format validation (following implementation guidelines)
180        let magic_score = match file_object.as_file() {
181            Some(file_obj) => {
182                // Check ELF magic bytes (XV6 uses ELF format)
183                let mut magic_buffer = [0u8; 4];
184                file_obj.seek(SeekFrom::Start(0)).ok(); // Reset to start
185                match file_obj.read(&mut magic_buffer) {
186                    Ok(bytes_read) if bytes_read >= 4 => {
187                        if magic_buffer == [0x7F, b'E', b'L', b'F'] {
188                            25 // Basic ELF format compatibility (slightly lower than Scarlet)
189                        } else {
190                            return None; // Not an ELF file, cannot execute
191                        }
192                    }
193                    _ => return None, // Read failed, cannot determine
194                }
195            }
196            None => return None, // Not a file object
197        };
198
199        let mut confidence = magic_score;
200
201        // Stage 2: Entry point validation (placeholder - could check ELF header)
202        // TODO: Add ELF header parsing to validate entry point for XV6 compatibility
203        confidence += 10;
204
205        // Stage 3: File path hints - XV6 specific patterns
206        if file_path.contains("xv6") || file_path.ends_with(".xv6") {
207            confidence += 20; // Strong XV6 indicator
208        } else if file_path.ends_with(".elf") {
209            confidence += 5; // General ELF compatibility
210        }
211
212        // Stage 4: ABI inheritance bonus - moderate priority for same ABI
213        if let Some(abi) = current_abi {
214            if abi.get_name() == self.get_name() {
215                confidence += 15; // Moderate inheritance bonus for XV6
216            }
217        }
218
219        Some(confidence.min(100)) // Standard 0-100 confidence range
220    }
221
222    fn execute_binary(
223        &self,
224        file_object: &crate::object::KernelObject,
225        argv: &[&str],
226        _envp: &[&str],
227        task: &crate::task::Task,
228        trapframe: &mut crate::arch::Trapframe,
229    ) -> Result<(), &'static str> {
230        match file_object.as_file() {
231            Some(file_obj) => {
232                // Reset task state for XV6 execution
233                task.text_size.store(0, Ordering::SeqCst);
234                task.data_size.store(0, Ordering::SeqCst);
235                task.stack_size.store(0, Ordering::SeqCst);
236                task.brk
237                    .store(usize::MAX, core::sync::atomic::Ordering::SeqCst);
238
239                // Load ELF using XV6-compatible method
240                match load_elf_into_task(file_obj, task) {
241                    Ok(entry_point) => {
242                        // Set the name
243                        *task.name.write() =
244                            argv.get(0).map_or("xv6".to_string(), |s| s.to_string());
245                        // Clear page table entries
246                        let idx =
247                            arch::vm::get_root_pagetable_ptr(task.vm_manager.get_asid()).unwrap();
248                        let root_page_table = arch::vm::get_pagetable(idx).unwrap();
249                        root_page_table.unmap_all();
250                        // Setup the trapframe
251                        arch::vm::setup_trampoline_for_user(&task.vm_manager);
252                        // Setup the stack
253                        let (_, stack_top) = setup_user_stack(task);
254                        let mut stack_pointer = stack_top as usize;
255
256                        let mut arg_ptrs: Vec<u64> = Vec::new();
257                        for arg in argv.iter() {
258                            let arg_bytes = arg.as_bytes();
259                            stack_pointer -= arg_bytes.len() + 1; // +1 for null terminator
260                            stack_pointer -= stack_pointer % 16; // Align to 16 bytes
261
262                            unsafe {
263                                let translated_stack_pointer =
264                                    task.vm_manager.translate_vaddr(stack_pointer).unwrap();
265                                let stack_slice = core::slice::from_raw_parts_mut(
266                                    translated_stack_pointer as *mut u8,
267                                    arg_bytes.len() + 1,
268                                );
269                                stack_slice[..arg_bytes.len()].copy_from_slice(arg_bytes);
270                                stack_slice[arg_bytes.len()] = 0; // Null terminator
271                            }
272
273                            arg_ptrs.push(stack_pointer as u64); // Store the address of the argument
274                        }
275
276                        let argc = arg_ptrs.len();
277
278                        stack_pointer -= argc * 8;
279                        stack_pointer -= stack_pointer % 16; // Align to 16 bytes
280
281                        // Push the addresses of the arguments onto the stack
282                        unsafe {
283                            let translated_stack_pointer =
284                                task.vm_manager.translate_vaddr(stack_pointer).unwrap() as *mut u64;
285                            for (i, &arg_ptr) in arg_ptrs.iter().enumerate() {
286                                *(translated_stack_pointer.add(i)) = arg_ptr;
287                            }
288                        }
289
290                        // Set the new entry point for the task
291                        task.set_entry_point(entry_point as usize);
292
293                        // Reset task's registers (except for those needed for arguments)
294                        task.vcpu.lock().iregs = IntRegisters::new();
295                        // Set the stack pointer
296                        task.vcpu.lock().set_sp(stack_pointer);
297                        task.vcpu.lock().iregs.reg[11] = stack_pointer as usize; // Set the return value (a0) to 0 in the new proc
298                        task.vcpu.lock().iregs.reg[10] = argc; // Set argc in a0
299
300                        // Switch to the new task
301                        task.vcpu.lock().switch(trapframe);
302                        Ok(())
303                    }
304                    Err(_e) => Err("Failed to load XV6 ELF binary"),
305                }
306            }
307            None => Err("Invalid file object type for XV6 binary execution"),
308        }
309    }
310
311    fn get_default_cwd(&self) -> &str {
312        "/" // XV6 uses root as default working directory
313    }
314
315    fn setup_overlay_environment(
316        &self,
317        target_vfs: &Arc<VfsManager>,
318        base_vfs: &Arc<VfsManager>,
319        system_path: &str,
320        config_path: &str,
321    ) -> Result<(), &'static str> {
322        // crate::println!("Setting up XV6 overlay environment with system path: {} and config path: {}", system_path, config_path);
323        // XV6 ABI uses overlay mount with system XV6 tools and config persistence
324        let lower_vfs_list = alloc::vec![(base_vfs, system_path)];
325        let upper_vfs = base_vfs;
326        let fs = match OverlayFS::new_from_paths_and_vfs(
327            Some((upper_vfs, config_path)),
328            lower_vfs_list,
329            "/",
330        ) {
331            Ok(fs) => fs,
332            Err(e) => {
333                crate::println!(
334                    "Failed to create overlay filesystem for XV6 ABI: {}",
335                    e.message
336                );
337                return Err("Failed to create XV6 overlay environment");
338            }
339        };
340        match target_vfs.mount(fs, "/", 0) {
341            Ok(()) => Ok(()),
342            Err(e) => {
343                crate::println!(
344                    "Failed to create cross-VFS overlay for XV6 ABI: {}",
345                    e.message
346                );
347                Err("Failed to create XV6 overlay environment")
348            }
349        }
350    }
351
352    fn setup_shared_resources(
353        &self,
354        target_vfs: &Arc<VfsManager>,
355        base_vfs: &Arc<VfsManager>,
356    ) -> Result<(), &'static str> {
357        // crate::println!("Setting up XV6 shared resources with base VFS");
358        // XV6 shared resource setup: bind mount common directories and Scarlet gateway
359        match create_dir_if_not_exists(target_vfs, "/home") {
360            Ok(()) => {}
361            Err(e) => {
362                // crate::println!("Failed to create /home directory for XV6: {}", e.message);
363                return Err("Failed to create /home directory for XV6");
364            }
365        }
366
367        match target_vfs.bind_mount_from(base_vfs, "/home", "/home") {
368            Ok(()) => {}
369            Err(e) => {
370                // crate::println!("Failed to bind mount /home for XV6: {}", e.message);
371            }
372        }
373
374        match create_dir_if_not_exists(target_vfs, "/data") {
375            Ok(()) => {}
376            Err(e) => {
377                crate::println!("Failed to create /data directory for XV6: {}", e.message);
378                return Err("Failed to create /data directory for XV6");
379            }
380        }
381
382        match target_vfs.bind_mount_from(base_vfs, "/data/shared", "/data/shared") {
383            Ok(()) => {}
384            Err(e) => {
385                // crate::println!("Failed to bind mount /data/shared for XV6: {}", e.message);
386            }
387        }
388
389        // Setup gateway to native Scarlet environment (read-only for security)
390        match create_dir_if_not_exists(target_vfs, "/scarlet") {
391            Ok(()) => {}
392            Err(e) => {
393                crate::println!("Failed to create /scarlet directory for XV6: {}", e.message);
394                return Err("Failed to create /scarlet directory for XV6");
395            }
396        }
397        match target_vfs.bind_mount_from(base_vfs, "/", "/scarlet") {
398            Ok(()) => Ok(()),
399            Err(e) => {
400                crate::println!(
401                    "Failed to bind mount native Scarlet root to /scarlet for XV6: {}",
402                    e.message
403                );
404                return Err("Failed to bind mount native Scarlet root to /scarlet for XV6");
405            }
406        }
407    }
408
409    fn initialize_from_existing_handles(
410        &mut self,
411        task: &crate::task::Task,
412    ) -> Result<(), &'static str> {
413        task.handle_table.close_all();
414        Ok(())
415    }
416
417    fn choose_load_address(
418        &self,
419        _elf_type: u16,
420        _target: crate::task::elf_loader::LoadTarget,
421    ) -> Option<u64> {
422        // xv6 ABI does not support dynamic linking - all binaries should be static
423        // Return None to use kernel default (which will only work for static ELF files)
424        None
425    }
426
427    fn get_interpreter_path(&self, _requested_interpreter: &str) -> String {
428        // xv6 ABI does not support dynamic linking
429        // This should never be called since xv6 binaries should not have PT_INTERP
430        // But if it happens, we'll return an error path
431        "/dev/null".to_string() // Invalid path to ensure failure
432    }
433
434    fn get_task_namespace(&self) -> Arc<crate::task::namespace::TaskNamespace> {
435        self.namespace.clone()
436    }
437}
438
439syscall_table! {
440    Invalid = 0 => |_abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi, _trapframe: &mut crate::arch::Trapframe| {
441        0
442    },
443    Fork = 1 => sys_fork,
444    Exit = 2 => sys_exit,
445    Wait = 3 => sys_wait,
446    Pipe = 4 => sys_pipe,
447    Read = 5 => sys_read,
448    //    Kill = 6 => sys_kill,
449    Exec = 7 => sys_exec,
450    Fstat = 8 => sys_fstat,
451    Chdir = 9 => sys_chdir,
452    Dup = 10 => sys_dup,
453    Getpid = 11 => sys_getpid,
454    Sbrk = 12 => sys_sbrk,
455    Sleep = 13 => sys_sleep,
456    // Uptime = 14 => sys_uptime,
457    Open = 15 => sys_open,
458    Write = 16 => sys_write,
459    Mknod = 17 => sys_mknod,
460    Unlink = 18 => sys_unlink,
461    Link = 19 => sys_link,
462    Mkdir = 20 => sys_mkdir,
463    Close = 21 => sys_close,
464}
465
466fn create_dir_if_not_exists(vfs: &Arc<VfsManager>, path: &str) -> Result<(), FileSystemError> {
467    match vfs.create_dir(path) {
468        Ok(()) => Ok(()),
469        Err(e) => {
470            if e.kind == FileSystemErrorKind::AlreadyExists {
471                Ok(()) // Directory already exists, nothing to do
472            } else {
473                Err(e) // Some other error occurred
474            }
475        }
476    }
477}
478
479fn register_xv6_abi() {
480    register_abi!(Xv6Riscv64Abi);
481}
482
483early_initcall!(register_xv6_abi);