diff options
Diffstat (limited to 'shell/src/lib.rs')
| -rw-r--r-- | shell/src/lib.rs | 211 |
1 files changed, 202 insertions, 9 deletions
diff --git a/shell/src/lib.rs b/shell/src/lib.rs index 5248ea9..11f5786 100644 --- a/shell/src/lib.rs +++ b/shell/src/lib.rs | |||
| @@ -28,6 +28,7 @@ use alloc::collections::VecDeque; | |||
| 28 | use alloc::string::{String, ToString}; | 28 | use alloc::string::{String, ToString}; |
| 29 | use alloc::vec::Vec; | 29 | use alloc::vec::Vec; |
| 30 | use rsnix::tty::{self, LineInput}; | 30 | use rsnix::tty::{self, LineInput}; |
| 31 | use alloc::boxed::Box; | ||
| 31 | use rsnix::{gop_print, gop_println}; | 32 | use rsnix::{gop_print, gop_println}; |
| 32 | 33 | ||
| 33 | /// Run the home-terminal shell (task 0). The home terminal is an | 34 | /// Run the home-terminal shell (task 0). The home terminal is an |
| @@ -952,14 +953,205 @@ fn abi_exit(code: i32) -> ! { | |||
| 952 | rsnix::sched::abort_current("exit") | 953 | rsnix::sched::abort_current("exit") |
| 953 | } | 954 | } |
| 954 | 955 | ||
| 955 | static EXPORTS_V1: rsapi::Exports = rsapi::Exports { | 956 | |
| 956 | magic: rsapi::EXPORTS_MAGIC, | 957 | /// Directory iteration cache: one slot per simultaneous fs_dir_next. |
| 957 | version: rsapi::ABI_VERSION, | 958 | struct DirIter { |
| 958 | put_line: abi_put_line, | 959 | entries: Vec<(String, rsnix::fs::FileType, u64)>, |
| 959 | get_line: abi_get_line, | 960 | pos: usize, |
| 960 | log_line: abi_log_line, | 961 | } |
| 961 | exit: abi_exit, | 962 | static mut DIR_CACHE: [Option<DirIter>; 16] = [const { None }; 16]; |
| 962 | }; | 963 | |
| 964 | fn cache_key(h: i32) -> usize { ((h as usize) & 0x7FFFFFFF) % 16 } | ||
| 965 | |||
| 966 | /// ABI: resolve a path to a handle (-1 on error). | ||
| 967 | fn abi_fs_resolve(path: rsapi::Line) -> rsapi::FsHandle { | ||
| 968 | match rsnix::fs::resolve(path.as_str()) { | ||
| 969 | Some(h) => h as i32, | ||
| 970 | None => -1, | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | /// ABI: iterate directory children one at a time. | ||
| 975 | fn abi_fs_dir_next(handle: rsapi::FsHandle, out: &mut rsapi::DirEntry) -> bool { | ||
| 976 | let key = cache_key(handle); | ||
| 977 | unsafe { | ||
| 978 | if DIR_CACHE[key].is_none() { | ||
| 979 | let h = handle as usize; | ||
| 980 | let kids = rsnix::fs::children(h); | ||
| 981 | DIR_CACHE[key] = Some(DirIter { entries: kids, pos: 0 }); | ||
| 982 | } | ||
| 983 | let iter = DIR_CACHE[key].as_mut().unwrap(); | ||
| 984 | if iter.pos < iter.entries.len() { | ||
| 985 | let (ref name, kind, size) = iter.entries[iter.pos]; | ||
| 986 | out.name = rsapi::Line { ptr: name.as_ptr(), len: name.len() }; | ||
| 987 | out.is_dir = kind == rsnix::fs::FileType::Dir; | ||
| 988 | out.size = size; | ||
| 989 | iter.pos += 1; | ||
| 990 | true | ||
| 991 | } else { | ||
| 992 | DIR_CACHE[key] = None; | ||
| 993 | false | ||
| 994 | } | ||
| 995 | } | ||
| 996 | } | ||
| 997 | |||
| 998 | /// ABI: create a file or directory at path. | ||
| 999 | fn abi_fs_create(path: rsapi::Line, is_dir: bool) -> i32 { | ||
| 1000 | let p = path.as_str(); | ||
| 1001 | if is_dir { | ||
| 1002 | match rsnix::fs::mkdir(p) { | ||
| 1003 | Ok(()) => 0, | ||
| 1004 | Err(()) => -1, | ||
| 1005 | } | ||
| 1006 | } else { | ||
| 1007 | match rsnix::fs::ensure_file(p) { | ||
| 1008 | Ok(_) => 0, | ||
| 1009 | Err(()) => -1, | ||
| 1010 | } | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | /// ABI: remove a file or empty directory. | ||
| 1015 | fn abi_fs_remove(path: rsapi::Line) -> i32 { | ||
| 1016 | match rsnix::fs::remove_path(path.as_str()) { | ||
| 1017 | Ok(()) => 0, | ||
| 1018 | Err(()) => -1, | ||
| 1019 | } | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | /// ABI: read bytes from a handle at a position. | ||
| 1023 | fn abi_fs_read(handle: rsapi::FsHandle, buf: *mut u8, pos: u64, len: u64) -> i64 { | ||
| 1024 | let h = handle as usize; | ||
| 1025 | let mut tmp = [0u8; 512]; | ||
| 1026 | let mut total = 0i64; | ||
| 1027 | let mut off = pos; | ||
| 1028 | let mut rem = len as usize; | ||
| 1029 | while rem > 0 { | ||
| 1030 | let chunk = rem.min(512); | ||
| 1031 | match rsnix::fs::read(h, &mut tmp[..chunk], off) { | ||
| 1032 | Ok(0) | Err(_) => break, | ||
| 1033 | Ok(n) => { | ||
| 1034 | unsafe { core::ptr::copy_nonoverlapping(tmp.as_ptr(), buf.add(total as usize), n); } | ||
| 1035 | total += n as i64; | ||
| 1036 | off += n as u64; | ||
| 1037 | rem -= n; | ||
| 1038 | } | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | total | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | /// ABI: write bytes to a handle at a position. | ||
| 1045 | fn abi_fs_write(handle: rsapi::FsHandle, buf: *const u8, pos: u64, len: u64) -> i64 { | ||
| 1046 | let h = handle as usize; | ||
| 1047 | if len == 0 { return 0; } | ||
| 1048 | let slice = unsafe { core::slice::from_raw_parts(buf, len as usize) }; | ||
| 1049 | match rsnix::fs::write(h, slice, pos) { | ||
| 1050 | Ok(n) => n as i64, | ||
| 1051 | Err(()) => -1, | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | /// ABI: get file size. | ||
| 1056 | fn abi_fs_size(handle: rsapi::FsHandle) -> u64 { | ||
| 1057 | rsnix::fs::size(handle as usize) | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /// ABI: close handle (currently no-op). | ||
| 1061 | fn abi_fs_close(_handle: rsapi::FsHandle) {} | ||
| 1062 | |||
| 1063 | /// ABI: current real-time clock. | ||
| 1064 | fn abi_rtc_now(out: &mut rsapi::RtcTime) { | ||
| 1065 | let t = rsnix::rtc::now(); | ||
| 1066 | out.year = t.year; | ||
| 1067 | out.month = t.month; | ||
| 1068 | out.day = t.day; | ||
| 1069 | out.hour = t.hour; | ||
| 1070 | out.minute = t.minute; | ||
| 1071 | out.second = t.second; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | /// ABI: memory statistics. | ||
| 1075 | fn abi_mem_stats(out: &mut rsapi::MemStats) { | ||
| 1076 | let s = rsnix::memory::heap_stats(); | ||
| 1077 | out.heap_total = s.heap_size as u64; | ||
| 1078 | out.heap_free = s.free_bytes as u64; | ||
| 1079 | let ps = rsnix::memory::page_stats(); | ||
| 1080 | out.pages_total = ps.span_pages as u64; | ||
| 1081 | out.pages_free = ps.free_pages as u64; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /// ABI: enumerate PCI devices. | ||
| 1085 | fn abi_pci_probe() -> u32 { | ||
| 1086 | rsnix::pci::probe().len() as u32 | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | /// ABI: get one PCI device by index. | ||
| 1090 | fn abi_pci_device(index: u32, out: &mut rsapi::PciDevice) -> bool { | ||
| 1091 | let devs = rsnix::pci::probe(); | ||
| 1092 | match devs.get(index as usize) { | ||
| 1093 | Some(d) => { | ||
| 1094 | out.bus = d.bus; | ||
| 1095 | out.device = d.device; | ||
| 1096 | out.function = d.func; | ||
| 1097 | out.class = d.class; | ||
| 1098 | out.subclass = d.subclass; | ||
| 1099 | out.vendor = d.vendor; | ||
| 1100 | out.device_id = d.device_id; | ||
| 1101 | true | ||
| 1102 | } | ||
| 1103 | None => false, | ||
| 1104 | } | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | /// ABI: dump kernel log ring into buffer. | ||
| 1108 | fn abi_klog_dump(buf: *mut u8, cap: u64) -> u64 { | ||
| 1109 | let s = rsnix::klog::dump(); | ||
| 1110 | let bytes = s.as_bytes(); | ||
| 1111 | let n = (bytes.len() as u64).min(cap) as usize; | ||
| 1112 | unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, n); } | ||
| 1113 | n as u64 | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | /// ABI: kernel version info. | ||
| 1117 | fn abi_kernel_version(out: &mut rsapi::KernelVersion) { | ||
| 1118 | let name = concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION")); | ||
| 1119 | let mut it = env!("CARGO_PKG_VERSION").split('.'); | ||
| 1120 | let major: u16 = it.next().and_then(|s| s.parse().ok()).unwrap_or(0); | ||
| 1121 | let minor: u16 = it.next().and_then(|s| s.parse().ok()).unwrap_or(0); | ||
| 1122 | let patch: u16 = it.next().and_then(|s| s.parse().ok()).unwrap_or(0); | ||
| 1123 | out.name = rsapi::Line { ptr: name.as_ptr(), len: name.len() }; | ||
| 1124 | out.major = major; | ||
| 1125 | out.minor = minor; | ||
| 1126 | out.patch = patch; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | /// Construct the Exports table for this invocation. v2 fields are | ||
| 1130 | /// populated at runtime (binary callbacks → shell → kernel). | ||
| 1131 | fn build_exports() -> rsapi::Exports { | ||
| 1132 | rsapi::Exports { | ||
| 1133 | magic: rsapi::EXPORTS_MAGIC, | ||
| 1134 | version: rsapi::ABI_VERSION, | ||
| 1135 | put_line: abi_put_line, | ||
| 1136 | get_line: abi_get_line, | ||
| 1137 | log_line: abi_log_line, | ||
| 1138 | exit: abi_exit, | ||
| 1139 | fs_resolve: abi_fs_resolve, | ||
| 1140 | fs_dir_next: abi_fs_dir_next, | ||
| 1141 | fs_create: abi_fs_create, | ||
| 1142 | fs_remove: abi_fs_remove, | ||
| 1143 | fs_read: abi_fs_read, | ||
| 1144 | fs_write: abi_fs_write, | ||
| 1145 | fs_size: abi_fs_size, | ||
| 1146 | fs_close: abi_fs_close, | ||
| 1147 | rtc_now: abi_rtc_now, | ||
| 1148 | mem_stats: abi_mem_stats, | ||
| 1149 | pci_probe: abi_pci_probe, | ||
| 1150 | pci_device: abi_pci_device, | ||
| 1151 | klog_dump: abi_klog_dump, | ||
| 1152 | kernel_version: abi_kernel_version, | ||
| 1153 | } | ||
| 1154 | } | ||
| 963 | 1155 | ||
| 964 | /// Run `cmd` as an external PE32+ binary from /bin/cli/. Returns false | 1156 | /// Run `cmd` as an external PE32+ binary from /bin/cli/. Returns false |
| 965 | /// when no external binary resolved (the caller falls back to built-ins). | 1157 | /// when no external binary resolved (the caller falls back to built-ins). |
| @@ -1026,7 +1218,8 @@ fn exec_external(p: &Parsed) -> bool { | |||
| 1026 | 1218 | ||
| 1027 | let mut cmd_done = false; | 1219 | let mut cmd_done = false; |
| 1028 | let mut exit_code: i64 = 0; | 1220 | let mut exit_code: i64 = 0; |
| 1029 | match rsnix::exec::spawn(image, &EXPORTS_V1, &argv, &name) { | 1221 | let exports = Box::leak(Box::new(build_exports())); |
| 1222 | match rsnix::exec::spawn(image, exports, &argv, &name) { | ||
| 1030 | Ok(_id) => { | 1223 | Ok(_id) => { |
| 1031 | // Pumping mode: assemble canonical lines from our queue into | 1224 | // Pumping mode: assemble canonical lines from our queue into |
| 1032 | // the channel while the command runs. The WM pump delivers | 1225 | // the channel while the command runs. The WM pump delivers |
