From 1fcabca441015ce6220f6ba34ae942799042df6e Mon Sep 17 00:00:00 2001 From: lgw <15035660024@163.com> Date: Sun, 10 Sep 2023 21:02:31 +0800 Subject: [PATCH] add environ funcs --- Cargo.lock | 94 ++++++++++- Makefile | 4 + api/arceos_posix_api/src/lib.rs | 4 + apps/c/memtest/expect_trace.out | 2 + apps/c/memtest/memtest.c | 6 + crates/dtb/Cargo.toml | 14 ++ crates/dtb/src/lib.rs | 114 ++++++++++++++ modules/axhal/Cargo.toml | 2 + modules/axhal/src/lib.rs | 2 + .../src/platform/aarch64_qemu_virt/mod.rs | 3 + .../src/platform/riscv64_qemu_virt/mod.rs | 3 + modules/axhal/src/platform/x86_pc/mod.rs | 13 +- modules/axruntime/Cargo.toml | 3 +- modules/axruntime/src/env.rs | 54 +++++++ modules/axruntime/src/lib.rs | 43 +++++ scripts/make/qemu.mk | 3 +- ulib/axlibc/c/env.c | 31 ---- ulib/axlibc/src/env.rs | 147 ++++++++++++++++++ ulib/axlibc/src/lib.rs | 6 +- 19 files changed, 509 insertions(+), 39 deletions(-) create mode 100644 crates/dtb/Cargo.toml create mode 100644 crates/dtb/src/lib.rs create mode 100644 modules/axruntime/src/env.rs delete mode 100644 ulib/axlibc/c/env.c create mode 100644 ulib/axlibc/src/env.rs diff --git a/Cargo.lock b/Cargo.lock index b305bb432..a52d9b255 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,6 +375,7 @@ dependencies = [ "bitflags 2.4.0", "cfg-if", "crate_interface", + "dtb", "dw_apb_uart", "handler_table", "kernel_guard", @@ -458,6 +459,7 @@ dependencies = [ "axnet", "axtask", "crate_interface", + "dtb", "kernel_guard", "lazy_init", "percpu", @@ -821,7 +823,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.9.0", "scopeguard", ] @@ -914,6 +916,14 @@ dependencies = [ "virtio-drivers", ] +[[package]] +name = "dtb" +version = "0.1.0" +dependencies = [ + "fdt-rs", + "lazy_init", +] + [[package]] name = "dw_apb_uart" version = "0.1.0" @@ -960,6 +970,12 @@ dependencies = [ "void", ] +[[package]] +name = "endian-type-rs" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76" + [[package]] name = "equivalent" version = "1.0.1" @@ -987,6 +1003,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fatfs" version = "0.4.0" @@ -996,6 +1018,22 @@ dependencies = [ "log", ] +[[package]] +name = "fdt-rs" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b" +dependencies = [ + "endian-type-rs", + "fallible-iterator", + "memoffset 0.5.6", + "num-derive", + "num-traits", + "rustc_version 0.2.3", + "static_assertions", + "unsafe_unwrap", +] + [[package]] name = "flatten_objects" version = "0.1.0" @@ -1062,7 +1100,7 @@ checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ "atomic-polyfill", "hash32", - "rustc_version", + "rustc_version 0.4.0", "spin 0.9.8", "stable_deref_trait", ] @@ -1234,6 +1272,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -1284,6 +1331,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -1584,13 +1642,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.18", ] [[package]] @@ -1658,12 +1725,27 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.185" @@ -1881,6 +1963,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unsafe_unwrap" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Makefile b/Makefile index b35159658..0d5ba77e0 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,10 @@ NET_DEV ?= user IP ?= 10.0.2.15 GW ?= 10.0.2.2 +#args and env +ARGS ?= +ENVS ?= + # App type ifeq ($(wildcard $(APP)),) $(error Application path "$(APP)" is not valid) diff --git a/api/arceos_posix_api/src/lib.rs b/api/arceos_posix_api/src/lib.rs index 1e0e126e9..42f2c043d 100644 --- a/api/arceos_posix_api/src/lib.rs +++ b/api/arceos_posix_api/src/lib.rs @@ -16,6 +16,9 @@ extern crate axruntime; #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "alloc")] +pub use axruntime::{environ, OUR_ENVIRON, environ_iter}; + #[macro_use] mod utils; @@ -60,3 +63,4 @@ pub use imp::pthread::mutex::{ }; #[cfg(feature = "multitask")] pub use imp::pthread::{sys_pthread_create, sys_pthread_exit, sys_pthread_join, sys_pthread_self}; + diff --git a/apps/c/memtest/expect_trace.out b/apps/c/memtest/expect_trace.out index 04aaf13aa..690064e49 100644 --- a/apps/c/memtest/expect_trace.out +++ b/apps/c/memtest/expect_trace.out @@ -40,4 +40,6 @@ allocated addr=0x[0-9a-f]\{16\} allocated addr=0x[0-9a-f]\{16\} allocated addr=0x[0-9a-f]\{16\} Memory tests run OK! +Running environ tests... +Environ tests run OK! Shutting down... diff --git a/apps/c/memtest/memtest.c b/apps/c/memtest/memtest.c index 78cef5ca2..b1020cc8d 100644 --- a/apps/c/memtest/memtest.c +++ b/apps/c/memtest/memtest.c @@ -23,5 +23,11 @@ int main() } free(p); puts("Memory tests run OK!"); + puts("Running environ tests..."); + char *env1 = "env1", *ex1 = "ex1", *ex2 = "ex_2"; + if(setenv(env1, ex1, 1) || strcmp(ex1, getenv(env1))) puts("set new env is wrong"); + if(setenv(env1, ex2, 1) || strcmp(ex2, getenv(env1))) puts("set old env is wrong"); + if(setenv(env1, ex1, 0) || strcmp(ex2, getenv(env1))) puts("override the old env is wrong"); + puts("Environ tests run OK!"); return 0; } diff --git a/crates/dtb/Cargo.toml b/crates/dtb/Cargo.toml new file mode 100644 index 000000000..54e10b658 --- /dev/null +++ b/crates/dtb/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "dtb" +version = "0.1.0" +edition = "2021" +authors = ["Leping Wang "] +description = "Device tree basic operations" +license = "GPL-3.0-or-later OR Apache-2.0" +homepage = "https://github.com/rcore-os/arceos" +repository = "https://github.com/rcore-os/arceos/tree/main/crates/dtb" +documentation = "https://rcore-os.github.io/arceos/dtb/index.html" + +[dependencies] +fdt-rs = { version = "0.4.3", default-features = false } +lazy_init = { path = "../../crates/lazy_init" } \ No newline at end of file diff --git a/crates/dtb/src/lib.rs b/crates/dtb/src/lib.rs new file mode 100644 index 000000000..551b49f6f --- /dev/null +++ b/crates/dtb/src/lib.rs @@ -0,0 +1,114 @@ +//! Some useful interfaces for device tree. + +#![no_std] +use fdt_rs::{ + base::{DevTree, DevTreeNode, DevTreeProp}, + prelude::{FallibleIterator, PropReader}, +}; +use lazy_init::LazyInit; + +/// The device Tree. +static TREE: LazyInit = LazyInit::new(); + +/// Init device tree from given address. +/// # Safety +/// +/// Callers of this method the must guarantee the following: +/// +/// - The passed address is 32-bit aligned. +pub unsafe fn init(dtb: *const u8) { + TREE.init_by(DevTree::from_raw_pointer(dtb).unwrap()); +} + +/// A node on the device tree. +pub struct DeviceNode<'a>(DevTreeNode<'a, 'static>); + +/// A prop of a node. +pub struct DeviceProp<'a>(DevTreeProp<'a, 'static>); + +impl<'a> DeviceNode<'a> { + /// Find a node's prop with given name(may not exist). + pub fn find_prop(&'a self, name: &str) -> Option> { + self.0 + .props() + .filter(|p| p.name().map(|s| s == name)) + .next() + .unwrap() + .map(DeviceProp) + } + + /// Find a node's prop with given name(must exist). + pub fn prop(&'a self, name: &str) -> DeviceProp<'a> { + self.find_prop(name).unwrap() + } +} + +impl<'a> DeviceProp<'a> { + /// Assume the prop is a u32 array. Get an element. + pub fn u32(&self, index: usize) -> u32 { + self.0.u32(index).unwrap() + } + + /// Assume the prop is a u64 array. Get an element. + pub fn u64(&self, index: usize) -> u64 { + self.0.u64(index).unwrap() + } + + /// Assume the prop is a str. Get the whole str. + pub fn str(&self) -> &'static str { + self.0.str().unwrap() + } +} + +/// Find the first node with given compatible(may not exist). +pub fn compatible_node(compatible: &str) -> Option { + TREE.compatible_nodes(compatible) + .next() + .unwrap() + .map(DeviceNode) +} + +/// Find the first node with given name(may not exist). +pub fn get_node(name: &str) -> Option { + TREE.nodes() + .filter(|n| n.name().map(|s| s == name)) + .next() + .unwrap() + .map(DeviceNode) +} + +/// Do something for all devices with given type. +pub fn devices(device_type: &str, f: F) +where + F: Fn(DeviceNode), +{ + TREE.nodes() + .filter_map(|n| { + let n = DeviceNode(n); + Ok( + if n.find_prop("device_type").map(|p| p.str()) == Some(device_type) { + Some(n) + } else { + None + }, + ) + }) + .for_each(|n| { + f(n); + Ok(()) + }) + .unwrap(); +} + +/// Do something for all nodes with given compatible. +pub fn compatible_nodes(compatible: &str, mut f: F) +where + F: FnMut(DeviceNode), +{ + TREE.compatible_nodes(compatible) + .for_each(|n| { + f(DeviceNode(n)); + Ok(()) + }) + .unwrap(); +} diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index f29ecde07..ec2a26c3f 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -46,6 +46,7 @@ raw-cpuid = "11.0" [target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] riscv = "0.10" sbi-rt = { version = "0.0.2", features = ["legacy"] } +dtb = {path = "../../crates/dtb" } [target.'cfg(target_arch = "aarch64")'.dependencies] aarch64-cpu = "9.3" @@ -53,6 +54,7 @@ tock-registers = "0.8" arm_gic = { path = "../../crates/arm_gic" } arm_pl011 = { path = "../../crates/arm_pl011" } dw_apb_uart = { path = "../../crates/dw_apb_uart" } +dtb = {path = "../../crates/dtb" } [build-dependencies] axconfig = { path = "../axconfig" } diff --git a/modules/axhal/src/lib.rs b/modules/axhal/src/lib.rs index 9fd27a1b1..b6b451e4a 100644 --- a/modules/axhal/src/lib.rs +++ b/modules/axhal/src/lib.rs @@ -79,3 +79,5 @@ pub use self::platform::platform_init; #[cfg(feature = "smp")] pub use self::platform::platform_init_secondary; + +pub static mut COMLINE_BUF:[u8; 256] = [0; 256]; \ No newline at end of file diff --git a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs index 2a452fa08..f77a716e1 100644 --- a/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/aarch64_qemu_virt/mod.rs @@ -31,6 +31,9 @@ pub(crate) unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); crate::arch::set_exception_vector_base(exception_vector_base as usize); crate::arch::write_page_table_root0(0.into()); // disable low address access + unsafe { + dtb::init(crate::mem::phys_to_virt(dtb.into()).as_ptr()); + } crate::cpu::init_primary(cpu_id); super::aarch64_common::pl011::init_early(); super::aarch64_common::generic_timer::init_early(); diff --git a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs index 3805c07de..c7cd40e1f 100644 --- a/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs +++ b/modules/axhal/src/platform/riscv64_qemu_virt/mod.rs @@ -22,6 +22,9 @@ unsafe extern "C" fn rust_entry(cpu_id: usize, dtb: usize) { crate::mem::clear_bss(); crate::cpu::init_primary(cpu_id); crate::arch::set_trap_vector_base(trap_vector_base as usize); + unsafe { + dtb::init(crate::mem::phys_to_virt(dtb.into()).as_ptr()); + } rust_main(cpu_id, dtb); } diff --git a/modules/axhal/src/platform/x86_pc/mod.rs b/modules/axhal/src/platform/x86_pc/mod.rs index ba3ce4363..d01f4afb7 100644 --- a/modules/axhal/src/platform/x86_pc/mod.rs +++ b/modules/axhal/src/platform/x86_pc/mod.rs @@ -32,7 +32,8 @@ fn current_cpu_id() -> usize { } } -unsafe extern "C" fn rust_entry(magic: usize, _mbi: usize) { +use crate::COMLINE_BUF; +unsafe extern "C" fn rust_entry(magic: usize, mbi: usize) { // TODO: handle multiboot info if magic == self::boot::MULTIBOOT_BOOTLOADER_MAGIC { crate::mem::clear_bss(); @@ -40,6 +41,16 @@ unsafe extern "C" fn rust_entry(magic: usize, _mbi: usize) { self::uart16550::init(); self::dtables::init_primary(); self::time::init_early(); + let mbi = mbi as *const u32; + let flag = mbi.read(); + if (flag & (1 << 2)) > 0 { + let cmdline = *mbi.add(4) as *const u8; // cmdline的物理地址 + let mut len = 0; + while cmdline.add(len).read() != 0 { + COMLINE_BUF[len] = cmdline.add(len).read(); + len += 1; + } + } rust_main(current_cpu_id(), 0); } } diff --git a/modules/axruntime/Cargo.toml b/modules/axruntime/Cargo.toml index ccd000b1d..b71945fb6 100644 --- a/modules/axruntime/Cargo.toml +++ b/modules/axruntime/Cargo.toml @@ -15,7 +15,7 @@ default = [] smp = ["axhal/smp"] irq = ["axhal/irq", "axtask?/irq", "percpu", "kernel_guard"] tls = ["axhal/tls", "axtask?/tls"] -alloc = ["axalloc"] +alloc = ["axalloc", "dtb"] paging = ["axhal/paging", "lazy_init"] multitask = ["axtask/multitask"] @@ -38,3 +38,4 @@ crate_interface = { path = "../../crates/crate_interface" } percpu = { path = "../../crates/percpu", optional = true } kernel_guard = { path = "../../crates/kernel_guard", optional = true } lazy_init = { path = "../../crates/lazy_init", optional = true } +dtb = { path = "../../crates/dtb", optional = true} diff --git a/modules/axruntime/src/env.rs b/modules/axruntime/src/env.rs new file mode 100644 index 000000000..2323db420 --- /dev/null +++ b/modules/axruntime/src/env.rs @@ -0,0 +1,54 @@ +extern crate alloc; +use alloc::vec::Vec; +use core::{ptr, usize}; +use core::ffi::c_char; + +// environ implementation +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut environ: *mut *mut c_char = ptr::null_mut(); // 不可为空 +pub static mut OUR_ENVIRON: Vec<*mut c_char> = Vec::new(); + +pub fn environ_iter() -> impl Iterator + 'static { + unsafe { + let mut ptrs = environ; + core::iter::from_fn(move || { + let ptr = ptrs.read(); + if ptr.is_null() { + None + } else { + ptrs = ptrs.add(1); + Some(ptr) + } + }) + } +} + +#[allow(dead_code)] +struct MemoryControlBlock { + size: usize, +} +const CTRL_BLK_SIZE: usize = core::mem::size_of::(); + +pub(crate) unsafe fn boot_add_environ( + env: &str, +) { + let ptr = env.as_ptr() as *const i8; + let size = env.len() + 1; // 算上/0 + if size == 1 { + return; + } + let layout = core::alloc::Layout::from_size_align(size + CTRL_BLK_SIZE, 8).unwrap(); + + // allocate for buf to meet free function + let alloc_ptr = alloc::alloc::alloc(layout).cast::(); + assert!(!alloc_ptr.is_null(), "alloc failed"); + alloc_ptr.write(MemoryControlBlock { size }); + let buf = alloc_ptr.add(1) as *mut c_char; + + for i in 0..size-1 { + core::ptr::write(buf.add(i), *ptr.add(i)); + } + core::ptr::write(buf.add(size - 1), 0); + OUR_ENVIRON.push(buf); +} \ No newline at end of file diff --git a/modules/axruntime/src/lib.rs b/modules/axruntime/src/lib.rs index a62c3055c..0d3882e48 100644 --- a/modules/axruntime/src/lib.rs +++ b/modules/axruntime/src/lib.rs @@ -32,6 +32,15 @@ mod mp; #[cfg(feature = "smp")] pub use self::mp::rust_main_secondary; +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "alloc")] +mod env; +#[cfg(feature = "alloc")] +pub use self::env::{environ, OUR_ENVIRON, environ_iter}; +#[cfg(feature = "alloc")] +use self::env::boot_add_environ; + const LOGO: &str = r#" d8888 .d88888b. .d8888b. d88888 d88P" "Y88b d88P Y88b @@ -189,6 +198,40 @@ pub extern "C" fn rust_main(cpu_id: usize, dtb: usize) -> ! { core::hint::spin_loop(); } + // environ initialization + #[cfg(feature = "alloc")] + unsafe { + use alloc::vec::Vec; + let mut boot_str = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + let cmdline_buf: &[u8] = &axhal::COMLINE_BUF; + let mut len = 0; + for c in cmdline_buf.iter() { + if *c == 0 { + break; + } + len += 1; + } + core::str::from_utf8(&cmdline_buf[..len]).unwrap() + } else { + dtb::get_node("chosen").unwrap().prop("bootargs").str() + }; + (_, boot_str) = match boot_str.split_once(';') { + Some((a, b)) => (a, b), + None => ("", "") + }; + let (args, envs) = match boot_str.split_once(';') { + Some((a, e)) => (a, e), + None => ("", ""), + }; + let envs: Vec<&str> = envs.split(" ").collect(); + let _args: Vec<&str> = args.split(" ").collect(); + for i in envs { + boot_add_environ(i); + } + OUR_ENVIRON.push(core::ptr::null_mut()); + environ = OUR_ENVIRON.as_mut_ptr(); + } + unsafe { main() }; #[cfg(feature = "multitask")] diff --git a/scripts/make/qemu.mk b/scripts/make/qemu.mk index 3ed432614..87d52f6d0 100644 --- a/scripts/make/qemu.mk +++ b/scripts/make/qemu.mk @@ -24,7 +24,8 @@ qemu_args-aarch64 := \ -machine virt \ -kernel $(OUT_BIN) -qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH)) +qemu_args-y := -m 128M -smp $(SMP) $(qemu_args-$(ARCH)) \ + -append ";$(ARGS);$(ENVS)" qemu_args-$(BLK) += \ -device virtio-blk-$(vdev-suffix),drive=disk0 \ diff --git a/ulib/axlibc/c/env.c b/ulib/axlibc/c/env.c deleted file mode 100644 index a0505a9ee..000000000 --- a/ulib/axlibc/c/env.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include -#include - -char *environ_[2] = {"dummy", NULL}; -char **environ = (char **)environ_; - -char *getenv(const char *name) -{ - size_t l = strchrnul(name, '=') - name; - if (l && !name[l] && environ) - for (char **e = environ; *e; e++) - if (!strncmp(name, *e, l) && l[*e] == '=') - return *e + l + 1; - return 0; -} - -// TODO -int setenv(const char *__name, const char *__value, int __replace) -{ - unimplemented(); - return 0; -} - -// TODO -int unsetenv(const char *__name) -{ - unimplemented(); - return 0; -} diff --git a/ulib/axlibc/src/env.rs b/ulib/axlibc/src/env.rs new file mode 100644 index 000000000..bb79255c8 --- /dev/null +++ b/ulib/axlibc/src/env.rs @@ -0,0 +1,147 @@ +use arceos_posix_api::{OUR_ENVIRON, environ, environ_iter}; +use core::ffi::{c_char, c_int, c_void}; + +use crate::malloc::{malloc, free}; + +unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> { + for (i, mut item) in environ_iter().enumerate() { + let mut search = search; + loop { + // search中的环境变量名是否到头 + let end_of_query = *search == 0 || *search == b'=' as c_char; + assert_ne!(*item, 0, "environ has an item without value"); + if *item == b'=' as c_char || end_of_query { + if *item == b'=' as c_char && end_of_query { + // Both keys env here + return Some((i, item.add(1))); + } else { + break; + } + } + + if *item != *search { + break; + } + + item = item.add(1); + search = search.add(1); + } + } + None +} + +unsafe fn put_new_env(insert: *mut c_char) { + // XXX: Another problem is that `environ` can be set to any pointer, which means there is a + // chance of a memory leak. But we can check if it was the same as before, like musl does. + if environ == OUR_ENVIRON.as_mut_ptr() { + *OUR_ENVIRON.last_mut().unwrap() = insert; + OUR_ENVIRON.push(core::ptr::null_mut()); + // Likely a no-op but is needed due to Stacked Borrows. + environ = OUR_ENVIRON.as_mut_ptr(); + } else { + OUR_ENVIRON.clear(); + OUR_ENVIRON.extend(environ_iter()); + OUR_ENVIRON.push(insert); + OUR_ENVIRON.push(core::ptr::null_mut()); + environ = OUR_ENVIRON.as_mut_ptr(); + } +} + +unsafe fn strlen(s: *const c_char) -> usize { + strnlen(s, usize::MAX) +} + +unsafe fn strnlen(s: *const c_char, size: usize) -> usize { + let mut i = 0; + while i < size { + if *s.add(i) == 0 { + break; + } + i += 1; + } + i +} + +unsafe fn copy_kv( + existing: *mut c_char, + key: *const c_char, + value: *const c_char, + key_len: usize, + value_len: usize, +) { + core::ptr::copy_nonoverlapping(key, existing, key_len); + core::ptr::write(existing.add(key_len), b'=' as c_char); + core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len); + core::ptr::write(existing.add(key_len + 1 + value_len), 0); +} + +#[no_mangle] +pub unsafe extern "C" fn setenv( + key: *const c_char, + value: *const c_char, + overwrite: c_int, +) -> c_int { + let key_len = strlen(key); + let value_len = strlen(value); // 不包括空字符 + // 如果环境变量表中存在该环境变量 + if let Some((i, existing)) = find_env(key) { + if overwrite == 0 { + return 0; + } + + let existing_len = strlen(existing); + // 如果现有环境变量值长度大于新值长度,则复用该位置 + if existing_len >= value_len { + // Reuse existing element's allocation + core::ptr::copy_nonoverlapping(value, existing, value_len); + //TODO: fill to end with zeroes + // 补\0 + core::ptr::write(existing.add(value_len), 0); + } else { + // Reuse environ slot, but allocate a new pointer. + let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char; + copy_kv(ptr, key, value, key_len, value_len); + environ.add(i).write(ptr); + } + } else { + // Expand environ and allocate a new pointer. + let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char; + copy_kv(ptr, key, value, key_len, value_len); + put_new_env(ptr); + } + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int { + if let Some((i, _)) = find_env(key) { + if environ == OUR_ENVIRON.as_mut_ptr() { + // No need to worry about updating the pointer, this does not + // reallocate in any way. And the final null is already shifted back. + let rm = OUR_ENVIRON.remove(i); + free(rm as *mut c_void); + // My UB paranoia. + environ = OUR_ENVIRON.as_mut_ptr(); + } else { + let len = OUR_ENVIRON.len(); + for _ in 0..len { + let rm = OUR_ENVIRON.pop().unwrap(); + free(rm as *mut c_void); + } + OUR_ENVIRON.extend( + environ_iter() + .enumerate() + .filter(|&(j, _)| j != i) + .map(|(_, v)| v), + ); + OUR_ENVIRON.push(core::ptr::null_mut()); + environ = OUR_ENVIRON.as_mut_ptr(); + } + } + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char { + find_env(name).map(|val| val.1).unwrap_or(core::ptr::null_mut()) +} diff --git a/ulib/axlibc/src/lib.rs b/ulib/axlibc/src/lib.rs index 7bb8cc80c..f561d796b 100644 --- a/ulib/axlibc/src/lib.rs +++ b/ulib/axlibc/src/lib.rs @@ -34,7 +34,8 @@ #[cfg(feature = "alloc")] extern crate alloc; - +#[cfg(feature = "alloc")] +mod env; #[path = "."] mod ctypes { #[rustfmt::skip] @@ -95,7 +96,8 @@ pub use self::unistd::{abort, exit, getpid}; pub use self::malloc::{free, malloc}; #[cfg(feature = "alloc")] pub use self::strftime::strftime; - +#[cfg(feature = "alloc")] +pub use self::env::{getenv, setenv, unsetenv}; #[cfg(feature = "fd")] pub use self::fd_ops::{ax_fcntl, close, dup, dup2, dup3};