diff --git a/Cargo.lock b/Cargo.lock index a6aac01077..ec8c340bfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6700,6 +6700,7 @@ dependencies = [ name = "rdrive" version = "0.21.0" dependencies = [ + "acpi", "fdt-edit", "fdt-raw", "log", @@ -6713,6 +6714,7 @@ dependencies = [ "rdrive-macros", "spin 0.12.0", "thiserror 2.0.18", + "x86", ] [[package]] @@ -7845,6 +7847,8 @@ dependencies = [ "spin 0.12.0", "thiserror 2.0.18", "tock-registers 0.10.1", + "x2apic", + "x86", ] [[package]] diff --git a/apps/starry/claw-code/build-x86_64-unknown-none.toml b/apps/starry/claw-code/build-x86_64-unknown-none.toml index 1128aa2eb9..69285236dd 100644 --- a/apps/starry/claw-code/build-x86_64-unknown-none.toml +++ b/apps/starry/claw-code/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = {} log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/codex-cli/build-x86_64-unknown-none.toml b/apps/starry/codex-cli/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/codex-cli/build-x86_64-unknown-none.toml +++ b/apps/starry/codex-cli/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/deepseek-tui/build-x86_64-unknown-none.toml b/apps/starry/deepseek-tui/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/deepseek-tui/build-x86_64-unknown-none.toml +++ b/apps/starry/deepseek-tui/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/diffutils/build-x86_64-unknown-none.toml b/apps/starry/diffutils/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/diffutils/build-x86_64-unknown-none.toml +++ b/apps/starry/diffutils/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/gcc/build-x86_64-unknown-none.toml b/apps/starry/gcc/build-x86_64-unknown-none.toml index 3cc59bbc04..3e7b8895e7 100644 --- a/apps/starry/gcc/build-x86_64-unknown-none.toml +++ b/apps/starry/gcc/build-x86_64-unknown-none.toml @@ -2,9 +2,7 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", "ax-driver/virtio-blk", "ax-driver/virtio-net", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/git/build-x86_64-unknown-none.toml b/apps/starry/git/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/git/build-x86_64-unknown-none.toml +++ b/apps/starry/git/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/jcode/build-x86_64-unknown-none.toml b/apps/starry/jcode/build-x86_64-unknown-none.toml index 3cc59bbc04..3e7b8895e7 100644 --- a/apps/starry/jcode/build-x86_64-unknown-none.toml +++ b/apps/starry/jcode/build-x86_64-unknown-none.toml @@ -2,9 +2,7 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", "ax-driver/virtio-blk", "ax-driver/virtio-net", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/mosquitto/build-x86_64-unknown-none.toml b/apps/starry/mosquitto/build-x86_64-unknown-none.toml index d7897a895e..3173417cb3 100644 --- a/apps/starry/mosquitto/build-x86_64-unknown-none.toml +++ b/apps/starry/mosquitto/build-x86_64-unknown-none.toml @@ -2,10 +2,7 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/nginx/build-x86_64-unknown-none.toml b/apps/starry/nginx/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/nginx/build-x86_64-unknown-none.toml +++ b/apps/starry/nginx/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/openrc/build-x86_64-unknown-none.toml b/apps/starry/openrc/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/openrc/build-x86_64-unknown-none.toml +++ b/apps/starry/openrc/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/openssh/build-x86_64-unknown-none.toml b/apps/starry/openssh/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/openssh/build-x86_64-unknown-none.toml +++ b/apps/starry/openssh/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/picoclaw-cli/build-x86_64-unknown-none.toml b/apps/starry/picoclaw-cli/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/picoclaw-cli/build-x86_64-unknown-none.toml +++ b/apps/starry/picoclaw-cli/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/pip/build-x86_64-unknown-none.toml b/apps/starry/pip/build-x86_64-unknown-none.toml index d7897a895e..3173417cb3 100644 --- a/apps/starry/pip/build-x86_64-unknown-none.toml +++ b/apps/starry/pip/build-x86_64-unknown-none.toml @@ -2,10 +2,7 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/postgresql/build-x86_64-unknown-none.toml b/apps/starry/postgresql/build-x86_64-unknown-none.toml index 98182f89d0..fc678caaa9 100644 --- a/apps/starry/postgresql/build-x86_64-unknown-none.toml +++ b/apps/starry/postgresql/build-x86_64-unknown-none.toml @@ -1,15 +1,11 @@ target = "x86_64-unknown-none" -axconfig_overrides = ["plat.phys-memory-size=0x4000_0000"] env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/qemu/build-x86_64-unknown-none.toml b/apps/starry/qemu/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/qemu/build-x86_64-unknown-none.toml +++ b/apps/starry/qemu/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/qemu/memtrack-backtrace/build-x86_64-unknown-none.toml b/apps/starry/qemu/memtrack-backtrace/build-x86_64-unknown-none.toml index cb589fbdad..1c91478352 100644 --- a/apps/starry/qemu/memtrack-backtrace/build-x86_64-unknown-none.toml +++ b/apps/starry/qemu/memtrack-backtrace/build-x86_64-unknown-none.toml @@ -2,9 +2,6 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2", BACKTRACE = "y" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", @@ -12,4 +9,4 @@ features = [ "ax-driver/virtio-socket", "starry-kernel/memtrack", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/qemu/nvme/build-x86_64-unknown-none.toml b/apps/starry/qemu/nvme/build-x86_64-unknown-none.toml index 53ede56fd3..93bd6e1924 100644 --- a/apps/starry/qemu/nvme/build-x86_64-unknown-none.toml +++ b/apps/starry/qemu/nvme/build-x86_64-unknown-none.toml @@ -2,12 +2,9 @@ target = "x86_64-unknown-none" env = {} log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/nvme", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/redis/build-x86_64-unknown-none.toml b/apps/starry/redis/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/redis/build-x86_64-unknown-none.toml +++ b/apps/starry/redis/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/stress/git-rename/build-x86_64-unknown-none.toml b/apps/starry/stress/git-rename/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/stress/git-rename/build-x86_64-unknown-none.toml +++ b/apps/starry/stress/git-rename/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/stress/git/build-x86_64-unknown-none.toml b/apps/starry/stress/git/build-x86_64-unknown-none.toml index 48f79a5df5..fc678caaa9 100644 --- a/apps/starry/stress/git/build-x86_64-unknown-none.toml +++ b/apps/starry/stress/git/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/stress/sqlite3-deep/build-x86_64-unknown-none.toml b/apps/starry/stress/sqlite3-deep/build-x86_64-unknown-none.toml index 4bcdb62052..f62d08b0b4 100644 --- a/apps/starry/stress/sqlite3-deep/build-x86_64-unknown-none.toml +++ b/apps/starry/stress/sqlite3-deep/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket" ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/stress/sqlite3-smoke/build-x86_64-unknown-none.toml b/apps/starry/stress/sqlite3-smoke/build-x86_64-unknown-none.toml index 4bcdb62052..f62d08b0b4 100644 --- a/apps/starry/stress/sqlite3-smoke/build-x86_64-unknown-none.toml +++ b/apps/starry/stress/sqlite3-smoke/build-x86_64-unknown-none.toml @@ -2,13 +2,10 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket" ] -plat_dyn = false +plat_dyn = true diff --git a/apps/starry/stress/stress-ng-0/build-x86_64-unknown-none.toml b/apps/starry/stress/stress-ng-0/build-x86_64-unknown-none.toml index eff192ee4b..fc678caaa9 100644 --- a/apps/starry/stress/stress-ng-0/build-x86_64-unknown-none.toml +++ b/apps/starry/stress/stress-ng-0/build-x86_64-unknown-none.toml @@ -1,15 +1,11 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } -axconfig_overrides = ["plat.phys-memory-size=0x4000_0000"] log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/components/axcpu/src/exception_table.rs b/components/axcpu/src/exception_table.rs index aa475cc2d7..15efdccfb6 100644 --- a/components/axcpu/src/exception_table.rs +++ b/components/axcpu/src/exception_table.rs @@ -3,23 +3,38 @@ use crate::TrapFrame; #[repr(C)] #[derive(Debug, PartialEq, Eq)] struct ExceptionTableEntry { - #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + ))] from: i32, - #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] + #[cfg(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + ))] to: i32, - #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + )))] from: usize, - #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + )))] to: usize, } impl ExceptionTableEntry { #[inline] fn source_addr(&self) -> usize { - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] { - let base = (&self.from as *const i32) as isize; - (base + self.from as isize) as usize + field_relative_addr(&self.from) } #[cfg(target_arch = "riscv64")] @@ -28,7 +43,11 @@ impl ExceptionTableEntry { (base + self.from as isize) as usize } - #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + )))] { self.from } @@ -36,10 +55,9 @@ impl ExceptionTableEntry { #[inline] fn to_addr(&self) -> usize { - #[cfg(target_arch = "aarch64")] + #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] { - let base = (&self.to as *const i32) as isize; - (base + self.to as isize) as usize + field_relative_addr(&self.to) } #[cfg(target_arch = "riscv64")] @@ -48,13 +66,24 @@ impl ExceptionTableEntry { (base + self.to as isize) as usize } - #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" + )))] { self.to } } } +#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] +#[inline] +fn field_relative_addr(offset: &i32) -> usize { + let base = (offset as *const i32) as isize; + (base + *offset as isize) as usize +} + unsafe extern "C" { static _ex_table_start: [ExceptionTableEntry; 0]; static _ex_table_end: [ExceptionTableEntry; 0]; @@ -70,24 +99,44 @@ impl TrapFrame { .offset_from_unsigned(_ex_table_start.as_ptr()), ) }; - match entries.binary_search_by_key(&self.ip(), ExceptionTableEntry::source_addr) { - Ok(entry) => { - self.set_ip(entries[entry].to_addr()); - true + #[cfg(target_arch = "x86_64")] + { + match entries + .iter() + .find(|entry| entry.source_addr() == self.ip()) + { + Some(entry) => { + self.set_ip(entry.to_addr()); + true + } + None => false, + } + } + + #[cfg(not(target_arch = "x86_64"))] + { + match entries.binary_search_by_key(&self.ip(), ExceptionTableEntry::source_addr) { + Ok(entry) => { + self.set_ip(entries[entry].to_addr()); + true + } + Err(_) => false, } - Err(_) => false, } } } pub(crate) fn init_exception_table() { - let ex_table = unsafe { - core::slice::from_raw_parts_mut( - _ex_table_start.as_ptr().cast_mut(), - _ex_table_end - .as_ptr() - .offset_from_unsigned(_ex_table_start.as_ptr()), - ) - }; - ex_table.sort_unstable_by_key(ExceptionTableEntry::source_addr); + #[cfg(not(target_arch = "x86_64"))] + { + let ex_table = unsafe { + core::slice::from_raw_parts_mut( + _ex_table_start.as_ptr().cast_mut(), + _ex_table_end + .as_ptr() + .offset_from_unsigned(_ex_table_start.as_ptr()), + ) + }; + ex_table.sort_unstable_by_key(ExceptionTableEntry::source_addr); + } } diff --git a/components/axcpu/src/x86_64/gdt.rs b/components/axcpu/src/x86_64/gdt.rs index 93138c4c55..bedee5be68 100644 --- a/components/axcpu/src/x86_64/gdt.rs +++ b/components/axcpu/src/x86_64/gdt.rs @@ -1,7 +1,7 @@ use x86_64::{ PrivilegeLevel, instructions::tables::load_tss, - registers::segmentation::{CS, Segment, SegmentSelector}, + registers::segmentation::{CS, DS, ES, SS, Segment, SegmentSelector}, structures::{ gdt::{Descriptor, GlobalDescriptorTable}, tss::TaskStateSegment, @@ -36,6 +36,9 @@ pub(super) fn init() { gdt.load(); unsafe { CS::set_reg(KCODE64); + DS::set_reg(KDATA); + ES::set_reg(KDATA); + SS::set_reg(KDATA); load_tss(tss); } } diff --git a/components/axcpu/src/x86_64/idt.rs b/components/axcpu/src/x86_64/idt.rs index 24e0d3a603..df71731279 100644 --- a/components/axcpu/src/x86_64/idt.rs +++ b/components/axcpu/src/x86_64/idt.rs @@ -13,7 +13,7 @@ pub(super) fn init() { IDT.call_once(|| { unsafe extern "C" { #[link_name = "trap_handler_table"] - static ENTRIES: [VirtAddr; NUM_INT]; + static ENTRIES: [i32; NUM_INT]; } let mut table = InterruptDescriptorTable::new(); let entries = unsafe { @@ -21,8 +21,11 @@ pub(super) fn init() { &mut table, ) }; - for i in 0..NUM_INT { - let opt = unsafe { entries[i].set_handler_addr(ENTRIES[i]) }; + let base = unsafe { ENTRIES.as_ptr() } as isize; + for (i, entry) in entries.iter_mut().enumerate() { + let offset = unsafe { *ENTRIES.as_ptr().add(i) } as isize; + let handler = VirtAddr::new((base + offset) as u64); + let opt = unsafe { entry.set_handler_addr(handler) }; if i == 0x3 || i == 0x80 { // enable user space breakpoints and legacy int 0x80 syscall opt.set_privilege_level(x86_64::PrivilegeLevel::Ring3); diff --git a/components/axcpu/src/x86_64/trap.S b/components/axcpu/src/x86_64/trap.S index 5538ff474c..252bc66eea 100644 --- a/components/axcpu/src/x86_64/trap.S +++ b/components/axcpu/src/x86_64/trap.S @@ -19,10 +19,11 @@ .endm .macro DEF_TABLE_ENTRY, i - .quad .Ltrap_handler_\i + .long .Ltrap_handler_\i - trap_handler_table .endm .section .rodata +.balign 4 .global trap_handler_table trap_handler_table: .set i, 0 diff --git a/components/axcpu/src/x86_64/user_copy.S b/components/axcpu/src/x86_64/user_copy.S index 6b748a75d3..69ed72409a 100644 --- a/components/axcpu/src/x86_64/user_copy.S +++ b/components/axcpu/src/x86_64/user_copy.S @@ -1,8 +1,8 @@ .macro _asm_extable, from, to .pushsection __ex_table, "a" - .balign 8 - .quad \from - .quad \to + .balign 4 + .long \from - . + .long \to - . .popsection .endm diff --git a/components/someboot/src/acpi/earlycon.rs b/components/someboot/src/acpi/earlycon.rs index 2a27dc90b4..0da436d982 100644 --- a/components/someboot/src/acpi/earlycon.rs +++ b/components/someboot/src/acpi/earlycon.rs @@ -1,6 +1,6 @@ use core::{cell::UnsafeCell, ptr::NonNull}; -use acpi::{AcpiError, Handler, PhysicalMapping, sdt::spcr::Spcr}; +use acpi::{AcpiError, Handler, PhysicalMapping, address::AddressSpace, sdt::spcr::Spcr}; use some_serial::{ns16550::Ns16550, *}; use crate::{console::Con, mem::_fixmap_io}; @@ -36,20 +36,45 @@ fn deal_with_spsr(spsr: &PhysicalMapping) -> Option<()> { if let Some(freq) = spsr.uart_clock_frequency() { clock = freq.into(); } - let vaddr; + let mut vaddr = None; + let mut is_mmio = false; match spsr.interface_type() { acpi::sdt::spcr::SpcrInterfaceType::Full16550 | acpi::sdt::spcr::SpcrInterfaceType::Generic16550 => { - vaddr = _fixmap_io(base_address.address as _); - - let mut uart = Ns16550::new_mmio( - NonNull::new(vaddr).unwrap(), - clock, - base_address.access_size as _, - ); - let tx = uart.take_tx().unwrap(); - set_sender(tx); + match base_address.address_space { + AddressSpace::SystemIo => { + #[cfg(target_arch = "x86_64")] + { + let mut uart = Ns16550::new_port(base_address.address as u16, clock); + uart.open(); + let tx = uart.take_tx().unwrap(); + set_sender(tx); + } + #[cfg(not(target_arch = "x86_64"))] + { + println!("SPCR I/O port early console is only supported on x86_64."); + return None; + } + } + AddressSpace::SystemMemory => { + let mapped = _fixmap_io(base_address.address as _); + vaddr = Some(mapped); + is_mmio = true; + let mut uart = Ns16550::new_mmio( + NonNull::new(mapped).unwrap(), + clock, + base_address.access_size as _, + ); + uart.open(); + let tx = uart.take_tx().unwrap(); + set_sender(tx); + } + space => { + println!("Unsupported SPCR address space `{space:?}` for early console."); + return None; + } + }; } t => { println!("Unsupported SPCR interface type `{t:?}` for early console."); @@ -60,10 +85,17 @@ fn deal_with_spsr(spsr: &PhysicalMapping) -> Option<()> { unsafe { crate::console::set_out(&SENDER) }; unsafe { crate::console::DEBUG_BASE = base_address.address as usize; - crate::console::DEBUG_IS_MMIO = true; + crate::console::DEBUG_IS_MMIO = is_mmio; } - println!("Early console initialized at vaddr {:#x}", vaddr as usize); + if let Some(vaddr) = vaddr { + println!("Early console initialized at vaddr {:#x}", vaddr as usize); + } else { + println!( + "Early console initialized at I/O port {:#x}", + base_address.address + ); + } Some(()) } diff --git a/components/someboot/src/arch/x86_64/addrspace.rs b/components/someboot/src/arch/x86_64/addrspace.rs index f5f5a30a09..5b5b4cf3b3 100644 --- a/components/someboot/src/arch/x86_64/addrspace.rs +++ b/components/someboot/src/arch/x86_64/addrspace.rs @@ -3,3 +3,4 @@ include!(concat!(env!("OUT_DIR"), "/defines.rs")); pub const KERNEL_BASE: usize = VM_LOAD_ADDRESS; pub const PERCPU_BASE: usize = 0xffff_ff00_0000_0000; pub const KERNEL_SPACE_BASE: usize = 0xffff_8000_0000_0000; +pub const PHYS_VIRT_OFFSET: usize = KERNEL_SPACE_BASE; diff --git a/components/someboot/src/arch/x86_64/console.rs b/components/someboot/src/arch/x86_64/console.rs index a43237d26e..44181ea163 100644 --- a/components/someboot/src/arch/x86_64/console.rs +++ b/components/someboot/src/arch/x86_64/console.rs @@ -1,3 +1,35 @@ pub struct Console; -impl crate::console::ArchConsoleOps for Console {} +const COM1_PORT: u16 = 0x3f8; +const COM1_CLOCK_HZ: u32 = 1_843_200; + +impl crate::console::ArchConsoleOps for Console { + fn init() -> bool { + use some_serial::InterfaceRaw; + + let mut uart = some_serial::ns16550::Ns16550::new_port(COM1_PORT, COM1_CLOCK_HZ); + uart.open(); + + let Some(tx) = uart.take_tx() else { + return false; + }; + let Some(rx) = uart.take_rx() else { + return false; + }; + + crate::console::set_earlycon_sender(tx); + crate::console::set_earlycon_reciever(rx); + true + } + + fn read_byte() -> Option { + unsafe { + let status = x86::io::inb(COM1_PORT + 5); + if status & 1 == 0 { + None + } else { + Some(x86::io::inb(COM1_PORT)) + } + } + } +} diff --git a/components/someboot/src/arch/x86_64/entry.rs b/components/someboot/src/arch/x86_64/entry.rs index 8c55b57857..d07b002a9c 100644 --- a/components/someboot/src/arch/x86_64/entry.rs +++ b/components/someboot/src/arch/x86_64/entry.rs @@ -38,7 +38,6 @@ pub extern "C" fn kernel_entry( } pub(crate) fn mmu_entry() -> ! { - super::relocate::reset(); super::trap::setup(); super::trap::init_local(); crate::prime_entry() @@ -47,7 +46,7 @@ pub(crate) fn mmu_entry() -> ! { pub(crate) unsafe extern "C" fn _secondary_entry(arg: usize) -> ! { let cpu_meta = unsafe { &*(crate::mem::phys_to_virt(arg) as *const PerCpuMeta) }; super::power::notify_ap_started(cpu_meta.cpu_id); - crate::entry::secondary_entry(cpu_meta); + crate::entry::secondary_entry(arg); loop { core::hint::spin_loop(); } diff --git a/components/someboot/src/arch/x86_64/link.ld b/components/someboot/src/arch/x86_64/link.ld index 82e39a6bfe..fdfb00558c 100644 --- a/components/someboot/src/arch/x86_64/link.ld +++ b/components/someboot/src/arch/x86_64/link.ld @@ -1,7 +1,7 @@ PECOFF_FILE_ALIGN = 0x200; PROVIDE(PAGE_SIZE = 0x1000); -PROVIDE(STACK_SIZE = 0x4000); +PROVIDE(STACK_SIZE = 0x40000); VM_LOAD_ADDRESS = ${kernel_load_vaddr}; OUTPUT_ARCH(i386:x86-64) @@ -9,6 +9,7 @@ ENTRY(_head) PHDRS { text PT_LOAD FLAGS(7); note PT_NOTE FLAGS(4); + tls PT_TLS FLAGS(4); } SECTIONS @@ -41,6 +42,7 @@ SECTIONS *(.data .data.*) *(.init.data) *(.sdata .sdata.*) + *(.got .got.*) . = ALIGN(PAGE_SIZE); __percpu_start = .; @@ -56,27 +58,27 @@ SECTIONS *(.sdata) . = ALIGN(PAGE_SIZE); } - _edata = .; - __kernel_load_end = .; - __bss_start = .; - - .tdata : { + .tdata : ALIGN(16) { _stdata = .; *(.tdata .tdata.*) _etdata = .; - } + } :text :tls + + _edata = .; + __kernel_load_end = .; + __bss_start = .; - .tbss : { + .tbss : ALIGN(16) { _stbss = .; *(.tbss .tbss.*) *(.init.bss) *(.tcommon) _etbss = .; - } + } :text :tls .sbss : { *(.dynsbss) *(.sbss) *(.scommon) - } + } :text .bss : { *(.dynbss) *(.bss .bss.*) @@ -86,7 +88,7 @@ SECTIONS __cpu0_stack = .; . += STACK_SIZE; __cpu0_stack_top = .; - } + } :text . = ALIGN(PAGE_SIZE); __bss_stop = .; _end = .; diff --git a/components/someboot/src/arch/x86_64/mod.rs b/components/someboot/src/arch/x86_64/mod.rs index 07596a962c..a1ce5fae97 100644 --- a/components/someboot/src/arch/x86_64/mod.rs +++ b/components/someboot/src/arch/x86_64/mod.rs @@ -17,7 +17,11 @@ pub(crate) use entry::_secondary_entry; pub use paging::Entry; pub use relocate::relocate; -use crate::{ArchTrait, DCacheOp, mem::PageTableInfo, power::CpuOnError}; +use crate::{ + ArchTrait, DCacheOp, + mem::{PageTableInfo, mmu}, + power::CpuOnError, +}; pub struct Arch; @@ -26,11 +30,15 @@ impl ArchTrait for Arch { type Console = console::Console; fn _va(paddr: usize) -> *mut u8 { - paddr as *mut u8 + if mmu::is_mmu_enabled() { + paddr.wrapping_add(addrspace::PHYS_VIRT_OFFSET) as *mut u8 + } else { + paddr as *mut u8 + } } fn _io(paddr: usize) -> *mut u8 { - paddr as *mut u8 + Self::_va(paddr) } fn _percpu(paddr: usize) -> *mut u8 { @@ -45,9 +53,16 @@ impl ArchTrait for Arch { } fn jump_to(entry: usize, sp: usize) -> ! { + // `jmp` does not leave a return address for the kernel's top frame. + // Reserve one zero word so frame-pointer unwinders stop cleanly. + let sp = sp - core::mem::size_of::(); + unsafe { + (sp as *mut usize).write(0); + } unsafe { core::arch::asm!( "mov rsp, {sp}", + "xor rbp, rbp", "jmp {entry}", sp = in(reg) sp, entry = in(reg) entry, @@ -92,19 +107,11 @@ impl ArchTrait for Arch { fn set_user_page_table(_val: PageTableInfo) {} fn shutdown() -> ! { - // unsafe { - // x86::irq::disable(); - // // QEMU ACPI poweroff ports (q35/i440fx). - // x86::io::outw(0x604, 0x2000); - // x86::io::outw(0xb004, 0x2000); - // } - - if crate::efi_stub::is_uefi_available() { - crate::efi_stub::reset( - crate::efi_stub::ResetType::SHUTDOWN, - crate::efi_stub::Status::SUCCESS, - None, - ); + unsafe { + x86::irq::disable(); + // QEMU ACPI poweroff ports (q35/i440fx). + x86::io::outw(0x604, 0x2000); + x86::io::outw(0xb004, 0x2000); } loop { diff --git a/components/someboot/src/arch/x86_64/paging.rs b/components/someboot/src/arch/x86_64/paging.rs index 181fa943b5..5c2e67897d 100644 --- a/components/someboot/src/arch/x86_64/paging.rs +++ b/components/someboot/src/arch/x86_64/paging.rs @@ -9,7 +9,7 @@ use x86::{ }; use crate::{ - arch::addrspace::{KERNEL_BASE, PERCPU_BASE}, + arch::addrspace::{KERNEL_BASE, PERCPU_BASE, PHYS_VIRT_OFFSET}, console::print_mapping, mem::{__kimage_va, __percpu, PageTableInfo, page_size}, }; @@ -127,8 +127,10 @@ pub fn enable_mmu() -> ! { let meta = crate::smp::cpu_meta(crate::smp::early_current_cpu_idx()).unwrap(); let v_sp = meta.stack_top_virt; let v_entry = __kimage_va(super::entry::mmu_entry as *const () as usize) as usize; + println!("x86_64 switching CR3 and resetting relocations before high-half jump"); crate::mem::mmu::set_mmu_enabled(); + super::relocate::reset(); unsafe { asm!( @@ -181,6 +183,17 @@ fn setup_page_table() -> anyhow::Result<()> { allow_huge: true, flush: false, })?; + + let direct_vaddr = region.physical_start.wrapping_add(PHYS_VIRT_OFFSET); + print_mapping(name, direct_vaddr, region.physical_start, size); + table.map(&MapConfig { + vaddr: direct_vaddr.into(), + paddr: region.physical_start.into(), + size, + pte, + allow_huge: true, + flush: false, + })?; } let lapic_base = (unsafe { rdmsr(x86::msr::IA32_APIC_BASE) } as usize) & !(page_size() - 1); @@ -190,6 +203,7 @@ fn setup_page_table() -> anyhow::Result<()> { (start..end).contains(&lapic_base) }); if !lapic_mapped { + let lapic_vaddr = lapic_base.wrapping_add(PHYS_VIRT_OFFSET); print_mapping("LAPIC", lapic_base, lapic_base, page_size()); table.map(&MapConfig { vaddr: lapic_base.into(), @@ -207,6 +221,24 @@ fn setup_page_table() -> anyhow::Result<()> { allow_huge: false, flush: false, })?; + + print_mapping("LAPIC", lapic_vaddr, lapic_base, page_size()); + table.map(&MapConfig { + vaddr: lapic_vaddr.into(), + paddr: lapic_base.into(), + size: page_size(), + pte: PteConfig { + valid: true, + read: true, + writable: true, + executable: false, + global: true, + mem_attr: MemAttributes::Device, + ..Default::default() + }, + allow_huge: false, + flush: false, + })?; } let ap_trampoline = super::power::AP_TRAMPOLINE_PADDR; @@ -282,8 +314,8 @@ fn setup_page_table() -> anyhow::Result<()> { let root = table.root_paddr(); crate::mem::mmu::set_boot_table(table); - enable_page_features(); super::trap::set_cr3(root); + enable_page_features(); Ok(()) } @@ -317,6 +349,8 @@ pub fn virt_to_phys(vaddr: *const u8) -> usize { vaddr - PERCPU_BASE } else if vaddr >= KERNEL_BASE { crate::mem::__kimage_va_to_pa(vaddr as *const u8) + } else if vaddr >= PHYS_VIRT_OFFSET { + vaddr - PHYS_VIRT_OFFSET } else { vaddr } diff --git a/components/someboot/src/arch/x86_64/power.rs b/components/someboot/src/arch/x86_64/power.rs index 504e1b3846..f3911da7c0 100644 --- a/components/someboot/src/arch/x86_64/power.rs +++ b/components/someboot/src/arch/x86_64/power.rs @@ -51,8 +51,9 @@ __x86_ap_trampoline_start: lgdt (__x86_ap_gdt_ptr - __x86_ap_trampoline_start) + # Enable PAE plus OS-managed FXSAVE/SSE state before entering long mode. movl %cr4, %eax - orl $0x20, %eax + orl $0x620, %eax movl %eax, %cr4 movl (__x86_ap_trampoline_cr3 - __x86_ap_trampoline_start), %eax @@ -63,8 +64,10 @@ __x86_ap_trampoline_start: orl $0x00000100, %eax wrmsr + # Clear EM/TS and enable protected mode, paging, MP, and native FP errors. movl %cr0, %eax - orl $0x80000001, %eax + andl $0xfffffff3, %eax + orl $0x80000023, %eax movl %eax, %cr0 ljmpl *(__x86_ap_ljmp_ptr - __x86_ap_trampoline_start) @@ -145,10 +148,10 @@ pub(crate) fn cpu_on(apic_id: usize, entry: usize, arg: usize) -> Result<(), Cpu } let apic_id = u8::try_from(apic_id).map_err(|_| CpuOnError::InvalidParameters)?; let meta = unsafe { &*(phys_to_virt(arg) as *const PerCpuMeta) }; - if meta.primary_table_paddr > u32::MAX as usize { + if meta.boot_table_paddr > u32::MAX as usize { return Err(CpuOnError::Other(anyhow::anyhow!( "x86 AP startup requires <4G CR3, got {:#x}", - meta.primary_table_paddr + meta.boot_table_paddr ))); } @@ -156,7 +159,7 @@ pub(crate) fn cpu_on(apic_id: usize, entry: usize, arg: usize) -> Result<(), Cpu AP_BOOTED_ID.store(usize::MAX, Ordering::Release); let entry_virt = crate::mem::__kimage_va(entry) as usize; prepare_trampoline( - meta.primary_table_paddr as u64, + meta.boot_table_paddr as u64, meta.stack_top_virt as u64, arg as u64, entry_virt as u64, @@ -213,7 +216,7 @@ fn prepare_trampoline(cr3: u64, stack: u64, arg: u64, entry: u64) { let len = src_end as usize - src_start as usize; assert!(len <= AP_TRAMPOLINE_SIZE); - let dst = AP_TRAMPOLINE_PADDR as *mut u8; + let dst = phys_to_virt(AP_TRAMPOLINE_PADDR); unsafe { core::ptr::copy_nonoverlapping(src_start, dst, len); } @@ -274,19 +277,20 @@ unsafe fn write_u64(base: *mut u8, offset: usize, value: u64) { } } -fn lapic_base() -> usize { - (unsafe { rdmsr(IA32_APIC_BASE) } as usize) & !(crate::mem::page_size() - 1) +fn lapic_base() -> *mut u8 { + let base = (unsafe { rdmsr(IA32_APIC_BASE) } as usize) & !(crate::mem::page_size() - 1); + phys_to_virt(base) } unsafe fn lapic_write(offset: u32, value: u32) { - let ptr = (lapic_base() + offset as usize) as *mut u32; + let ptr = unsafe { lapic_base().add(offset as usize) }.cast::(); unsafe { ptr.write_volatile(value); } } unsafe fn lapic_read(offset: u32) -> u32 { - let ptr = (lapic_base() + offset as usize) as *const u32; + let ptr = unsafe { lapic_base().add(offset as usize) }.cast::(); unsafe { ptr.read_volatile() } } diff --git a/components/someboot/src/arch/x86_64/trap.rs b/components/someboot/src/arch/x86_64/trap.rs index 79f649b564..b67e902b1a 100644 --- a/components/someboot/src/arch/x86_64/trap.rs +++ b/components/someboot/src/arch/x86_64/trap.rs @@ -15,7 +15,10 @@ use x86::{ }; use super::irq::{LAPIC_SPURIOUS_VECTOR, LAPIC_TIMER_VECTOR}; -use crate::{irq, mem::page_size}; +use crate::{ + irq, + mem::{page_size, phys_to_virt}, +}; const IA32_EFER: u32 = 0xc000_0080; const IA32_EFER_NXE: u64 = 1 << 11; @@ -374,7 +377,7 @@ fn write_lapic_reg(offset: u32, value: u32) { fn lapic_ptr(offset: u32) -> *mut u32 { let base = unsafe { rdmsr(msr::IA32_APIC_BASE) & LAPIC_BASE_MASK } as usize; - (base + offset as usize) as *mut u32 + unsafe { phys_to_virt(base).add(offset as usize) }.cast() } fn enable_nxe() { diff --git a/components/someboot/src/efi_stub/mod.rs b/components/someboot/src/efi_stub/mod.rs index f9e8b7e288..8606d80712 100644 --- a/components/someboot/src/efi_stub/mod.rs +++ b/components/someboot/src/efi_stub/mod.rs @@ -2,6 +2,9 @@ use core::arch::naked_asm; use core::{fmt::Write, ptr::null, sync::atomic::AtomicBool}; +pub use uefi::Status; +#[cfg(target_arch = "loongarch64")] +pub use uefi::runtime::ResetType; use uefi::{ Result, boot::{self, MemoryDescriptor, MemoryType}, @@ -12,7 +15,6 @@ use uefi::{ system::with_config_table, table::{self, cfg::ConfigTableEntry}, }; -pub use uefi::{Status, runtime::ResetType}; use crate::{ ArchTrait, @@ -191,10 +193,12 @@ fn find_acpi_rsdp() { }) } +#[cfg(target_arch = "loongarch64")] pub fn is_uefi_available() -> bool { uefi::table::system_table_raw().is_some() } +#[cfg(target_arch = "loongarch64")] pub fn reset(reset_type: ResetType, status: Status, data: Option<&[u8]>) -> ! { info!("Resetting system via UEFI..."); uefi::runtime::reset(reset_type, status, data) diff --git a/components/someboot/src/entry/mod.rs b/components/someboot/src/entry/mod.rs index 6c9db1d312..ee76402e15 100644 --- a/components/someboot/src/entry/mod.rs +++ b/components/someboot/src/entry/mod.rs @@ -30,11 +30,10 @@ pub fn primary_init_early(params: PrimaryCpuInitInfo) { crate::mem::early_init(); } -pub(crate) fn secondary_entry(cpu_meta: &PerCpuMeta) { +pub(crate) fn secondary_entry(cpu_meta_paddr: usize) { crate::arch::Arch::per_cpu_trap_init(false); let cpu_meta = unsafe { - let phys = cpu_meta as *const _ as usize; - let virt = crate::mem::phys_to_virt(phys); + let virt = crate::mem::phys_to_virt(cpu_meta_paddr); &*(virt as *const crate::smp::PerCpuMeta) }; diff --git a/components/someboot/src/mem/mod.rs b/components/someboot/src/mem/mod.rs index cd5f7fe50e..c45cae4ecf 100644 --- a/components/someboot/src/mem/mod.rs +++ b/components/someboot/src/mem/mod.rs @@ -27,7 +27,9 @@ static mut KIMAGE_START: Option = None; /// Load address of the kernel end static mut KIMAGE_END: PhysAddr = PhysAddr::new(0); -pub type MemoryMap = heapless::Vec; +const MEMORY_MAP_CAPACITY: usize = 512; + +pub type MemoryMap = heapless::Vec; pub(crate) fn setup_entry( kernel_start: PhysAddr, diff --git a/drivers/ax-driver/src/net/binding.rs b/drivers/ax-driver/src/net/binding.rs index 10f36efa07..8783bbe48b 100644 --- a/drivers/ax-driver/src/net/binding.rs +++ b/drivers/ax-driver/src/net/binding.rs @@ -42,32 +42,119 @@ impl DriverGeneric for PlatformNetDevice { } pub fn pci_legacy_irq(endpoint: &EndpointRc) -> Option { - #[cfg(all( - plat_dyn, - target_os = "none", - any( - feature = "intel-net", - feature = "ixgbe", - feature = "realtek-rtl8125", - feature = "virtio-net", - feature = "xhci-pci", - ) - ))] - { - let interrupt_pin = endpoint.interrupt_pin(); - if interrupt_pin != 0 { - match crate::pci::fdt_irq_for_endpoint(endpoint.address(), interrupt_pin) { - Ok(Some(irq)) => return Some(irq), - Ok(None) => {} - Err(err) => log::warn!( - "failed to resolve FDT IRQ for net endpoint {}: {err}", - endpoint.address() - ), + pci_irq_candidates( + endpoint.address(), + endpoint.interrupt_pin(), + endpoint.interrupt_line(), + || { + #[cfg(all( + plat_dyn, + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) + ))] + { + let interrupt_pin = endpoint.interrupt_pin(); + if interrupt_pin != 0 { + match crate::pci::acpi_irq_for_endpoint(endpoint.address(), interrupt_pin) { + Ok(Some(irq)) => return Some(irq), + Ok(None) => {} + Err(err) => log::warn!( + "failed to resolve ACPI IRQ for net endpoint {}: {err}", + endpoint.address() + ), + } + } } - } + None + }, + || { + #[cfg(all( + plat_dyn, + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) + ))] + { + let interrupt_pin = endpoint.interrupt_pin(); + if interrupt_pin != 0 { + match crate::pci::fdt_irq_for_endpoint(endpoint.address(), interrupt_pin) { + Ok(Some(irq)) => return Some(irq), + Ok(None) => {} + Err(err) => log::warn!( + "failed to resolve FDT IRQ for net endpoint {}: {err}", + endpoint.address() + ), + } + } + } + None + }, + ) +} + +fn pci_irq_candidates( + address: rdrive::probe::pci::PciAddress, + interrupt_pin: u8, + interrupt_line: u8, + acpi: impl FnOnce() -> Option, + fdt: impl FnOnce() -> Option, +) -> Option { + if let Some(irq) = acpi() { + return Some(irq); + } + + if let Some(irq) = fdt() { + return Some(irq); + } + + if let Some(irq) = crate::pci::legacy_irq_for_endpoint(address, interrupt_pin) { + return Some(irq); } - crate::pci::endpoint_legacy_irq(endpoint) + if interrupt_line == 0 || interrupt_line == u8::MAX { + return None; + } + Some(crate::pci::legacy_line_to_irq(interrupt_line)) +} + +#[cfg(test)] +mod tests { + use rdrive::probe::pci::PciAddress; + + use super::pci_irq_candidates; + + #[test] + fn pci_irq_resolution_prefers_acpi_then_fdt_then_fallback_line() { + let address = PciAddress::new(0, 0, 3, 0); + + assert_eq!( + pci_irq_candidates(address, 1, 9, || Some(0x31), || Some(0x40)), + Some(0x31) + ); + assert_eq!( + pci_irq_candidates(address, 1, 9, || None, || Some(0x40)), + Some(0x40) + ); + let fallback_irq = pci_irq_candidates(address, 1, 9, || None, || None); + if cfg!(all(target_arch = "x86_64", plat_dyn)) { + assert_eq!(fallback_irq, Some(0x39)); + } else if cfg!(target_arch = "x86_64") { + assert_eq!(fallback_irq, Some(0x29)); + } else { + assert_eq!(fallback_irq, Some(9)); + } + } } pub trait PlatformDeviceNet { diff --git a/drivers/ax-driver/src/pci/acpi.rs b/drivers/ax-driver/src/pci/acpi.rs new file mode 100644 index 0000000000..39afe553e7 --- /dev/null +++ b/drivers/ax-driver/src/pci/acpi.rs @@ -0,0 +1,109 @@ +extern crate alloc; + +#[cfg(all( + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) +))] +use alloc::format; + +use log::debug; +#[cfg(all( + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) +))] +use rdrive::probe::pci::PciAddress; +use rdrive::{ + PlatformDevice, + probe::{ + OnProbeError, + acpi::{AcpiId, AcpiInfo}, + }, +}; + +crate::model_register!( + name: "ACPI Generic PCIe Controller Driver", + level: ProbeLevel::PostKernel, + priority: ProbePriority::DEFAULT, + probe_kinds: &[ + ProbeKind::Acpi { + ids: &[AcpiId { + hid: "PNP0A08", + cids: &["PNP0A03"], + }], + on_probe: probe_acpi_ecam + } + ], +); + +fn probe_acpi_ecam(info: AcpiInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { + let mut registered = false; + for region in info.root.pci_ecam_regions() { + debug!("ACPI MCFG PCI ECAM region: {region:?}"); + super::register_ecam_controller( + PlatformDevice { + descriptor: plat_dev.descriptor.clone(), + }, + region.base_address as usize, + region.size(), + None, + None, + )?; + registered = true; + } + + if registered { + Ok(()) + } else { + Err(OnProbeError::NotMatch) + } +} + +#[cfg(all( + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) +))] +pub(crate) fn acpi_irq_for_endpoint( + address: PciAddress, + interrupt_pin: u8, +) -> Result, OnProbeError> { + let Some(result) = + rdrive::probe::acpi::with_acpi(|acpi| acpi.pci_irq_for_endpoint(address, interrupt_pin)) + else { + return Ok(None); + }; + result + .map(|route| { + route.map(|route| { + log::info!( + "ACPI PCI INTx route: endpoint {} pin {} -> GSI {} IOAPIC {} input {} vector \ + {:#x}", + address, + interrupt_pin, + route.gsi, + route.io_apic_id, + route.io_apic_input, + route.vector + ); + route.vector + }) + }) + .map_err(|err| OnProbeError::other(format!("{err}"))) +} diff --git a/drivers/ax-driver/src/pci/mod.rs b/drivers/ax-driver/src/pci/mod.rs index f6a9e0eafb..911593d19d 100644 --- a/drivers/ax-driver/src/pci/mod.rs +++ b/drivers/ax-driver/src/pci/mod.rs @@ -29,6 +29,8 @@ use virtio_drivers::transport::{ #[cfg(virtio_dev)] use crate::virtio::VirtIoHalImpl; +#[cfg(plat_dyn)] +mod acpi; #[cfg(plat_dyn)] mod fdt; #[cfg(all( @@ -42,6 +44,18 @@ mod fdt; feature = "xhci-pci", ) ))] +pub(crate) use acpi::acpi_irq_for_endpoint; +#[cfg(all( + plat_dyn, + target_os = "none", + any( + feature = "intel-net", + feature = "ixgbe", + feature = "realtek-rtl8125", + feature = "virtio-net", + feature = "xhci-pci", + ) +))] pub(crate) use fdt::fdt_irq_for_endpoint; const MAX_PCIE_LEGACY_IRQS: usize = 8; @@ -219,21 +233,38 @@ pub fn endpoint_legacy_irq(endpoint: &rdrive::probe::pci::EndpointRc) -> Option< if line == 0 || line == u8::MAX { return None; } - Some(pci_legacy_line_to_irq(line)) + Some(legacy_line_to_irq(line)) } -const fn pci_legacy_line_to_irq(line: u8) -> usize { - const PCI_IRQ_BASE: usize = if cfg!(target_arch = "x86_64") || cfg!(target_arch = "riscv64") { - if cfg!(target_arch = "x86_64") { - 0x20 - } else { - 0 - } +pub(crate) const fn legacy_line_to_irq(line: u8) -> usize { + legacy_line_to_irq_for_platform(line, cfg!(target_arch = "x86_64"), cfg!(plat_dyn)) +} + +const fn legacy_line_to_irq_for_platform(line: u8, is_x86_64: bool, is_plat_dyn: bool) -> usize { + let base = if is_x86_64 { + if is_plat_dyn { 0x30 } else { 0x20 } } else { 0 }; - PCI_IRQ_BASE + line as usize + base + line as usize +} + +#[cfg(test)] +mod tests { + use super::legacy_line_to_irq_for_platform; + + #[test] + fn x86_64_legacy_line_uses_dynamic_ioapic_base_on_plat_dyn() { + assert_eq!(legacy_line_to_irq_for_platform(9, true, false), 0x29); + assert_eq!(legacy_line_to_irq_for_platform(9, true, true), 0x39); + } + + #[test] + fn non_x86_64_legacy_line_remains_raw_irq() { + assert_eq!(legacy_line_to_irq_for_platform(9, false, false), 9); + assert_eq!(legacy_line_to_irq_for_platform(9, false, true), 9); + } } pub fn register_legacy_irq_route(bus_start: u8, bus_end: u8, irq: usize) { diff --git a/drivers/ax-driver/src/usb/mod.rs b/drivers/ax-driver/src/usb/mod.rs index 9977cb1e49..801c57cd6a 100644 --- a/drivers/ax-driver/src/usb/mod.rs +++ b/drivers/ax-driver/src/usb/mod.rs @@ -233,7 +233,7 @@ fn pci_static_irq(endpoint: &rdrive::probe::pci::EndpointRc) -> Option { return Some(irq); } let line = endpoint.interrupt_line(); - (line != 0 && line != u8::MAX).then_some(line as usize) + (line != 0 && line != u8::MAX).then_some(crate::pci::legacy_line_to_irq(line)) } #[cfg(all(feature = "xhci-pci", target_os = "none"))] diff --git a/drivers/ax-driver/src/virtio/mod.rs b/drivers/ax-driver/src/virtio/mod.rs index 9ac7d1d300..1a9f3d8aa2 100644 --- a/drivers/ax-driver/src/virtio/mod.rs +++ b/drivers/ax-driver/src/virtio/mod.rs @@ -38,6 +38,9 @@ unsafe impl VirtIoHal for VirtIoHalImpl { let Ok(vaddr) = global_allocator().alloc_pages(pages, 0x1000, UsageKind::Dma) else { return (0, NonNull::dangling()); }; + unsafe { + core::ptr::write_bytes(vaddr as *mut u8, 0, pages * 0x1000); + } let paddr = axklib::mem::virt_to_phys(vaddr.into()).as_usize() as VirtIoPhysAddr; let ptr = NonNull::new(vaddr as _).expect("DMA allocator returned null"); (paddr, ptr) diff --git a/drivers/rdrive/Cargo.toml b/drivers/rdrive/Cargo.toml index 87c4e480a0..9101ad13ef 100644 --- a/drivers/rdrive/Cargo.toml +++ b/drivers/rdrive/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true version = "0.21.0" [dependencies] +acpi = "6.1.1" fdt-edit.workspace = true fdt-raw.workspace = true log = "0.4" @@ -27,3 +28,6 @@ rdif-pcie = { workspace = true } [dev-dependencies] rdif-intc.workspace = true rdif-clk.workspace = true + +[target.'cfg(target_arch = "x86_64")'.dependencies] +x86.workspace = true diff --git a/drivers/rdrive/src/probe/acpi.rs b/drivers/rdrive/src/probe/acpi.rs index 924d32c311..acb7ff4630 100644 --- a/drivers/rdrive/src/probe/acpi.rs +++ b/drivers/rdrive/src/probe/acpi.rs @@ -1,13 +1,64 @@ +use alloc::{ + collections::BTreeSet, + format, + rc::Rc, + string::{String, ToString}, + vec::Vec, +}; +use core::{ptr::NonNull, str::FromStr}; + +use acpi::{ + AcpiError, AcpiTables, Handler, PhysicalMapping, + aml::{ + AmlError, Interpreter, + namespace::{AmlName, NamespaceLevelKind}, + object::Object, + pci_routing::{IrqDescriptor, PciRoutingTable, Pin}, + resource::{InterruptPolarity, InterruptTrigger}, + }, + platform::{ + AcpiPlatform, + interrupt::{InterruptModel, Polarity, TriggerMode}, + pci::PciConfigRegions, + }, +}; +use spin::{Mutex, Once}; + use crate::{ - PlatformDevice, + DeviceId, PlatformDevice, error::DriverError, - probe::{OnProbeError, ProbeError}, - register::DriverRegister, + probe::{OnProbeError, ProbeError, pci::PciAddress}, + register::{DriverRegister, ProbeKind}, }; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +const PCI_INTX_VECTOR_BASE: usize = 0x30; +const PCI_ROOT_FALLBACK_PATHS: &[&str] = &["\\_SB.PCI0", "\\_SB.PCI1", "\\_SB.PC00", "\\_SB.PC01"]; + +static SYSTEM: Once = Once::new(); +static NULL_LOCK: Mutex<()> = Mutex::new(()); + +#[derive(Clone, Copy)] pub struct AcpiRoot { pub rsdp: usize, + pub phys_to_virt: fn(usize) -> *mut u8, +} + +impl core::fmt::Debug for AcpiRoot { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("AcpiRoot") + .field("rsdp", &self.rsdp) + .finish_non_exhaustive() + } +} + +impl AcpiRoot { + pub const fn new(rsdp: usize, phys_to_virt: fn(usize) -> *mut u8) -> Self { + Self { rsdp, phys_to_virt } + } + + pub const fn identity(rsdp: usize) -> Self { + Self::new(rsdp, identity_phys_to_virt) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -16,22 +67,876 @@ pub struct AcpiId { pub cids: &'static [&'static str], } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AcpiPciEcam { + pub segment_group: u16, + pub bus_start: u8, + pub bus_end: u8, + pub base_address: u64, +} + +impl AcpiPciEcam { + pub fn size(&self) -> usize { + let buses = usize::from(self.bus_end.saturating_sub(self.bus_start)) + 1; + buses << 20 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AcpiIoApic { + pub id: u8, + pub address: u32, + pub gsi_base: u32, + pub redirection_entries: u8, +} + +impl AcpiIoApic { + fn contains_gsi(self, gsi: u32) -> bool { + let start = self.gsi_base; + let end = start.saturating_add(u32::from(self.redirection_entries)); + (start..end).contains(&gsi) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AcpiGsiRoute { + pub gsi: u32, + pub vector: usize, + pub io_apic_id: u8, + pub io_apic_address: u32, + pub io_apic_input: u8, + pub trigger: AcpiIrqTrigger, + pub polarity: AcpiIrqPolarity, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AcpiIrqTrigger { + Edge, + Level, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AcpiIrqPolarity { + ActiveHigh, + ActiveLow, +} + +#[derive(Debug, Clone)] +pub struct AcpiRouting { + io_apics: Vec, +} + +impl AcpiRouting { + pub const fn new() -> Self { + Self { + io_apics: Vec::new(), + } + } + + pub fn add_io_apic(&mut self, io_apic: AcpiIoApic) { + self.io_apics.push(io_apic); + } + + pub fn io_apics(&self) -> &[AcpiIoApic] { + &self.io_apics + } + + pub fn resolve_gsi(&self, gsi: u32) -> Option { + let io_apic = self + .io_apics + .iter() + .copied() + .find(|io_apic| io_apic.contains_gsi(gsi))?; + let input = u8::try_from(gsi.saturating_sub(io_apic.gsi_base)).ok()?; + Some(AcpiGsiRoute { + gsi, + vector: PCI_INTX_VECTOR_BASE + gsi as usize, + io_apic_id: io_apic.id, + io_apic_address: io_apic.address, + io_apic_input: input, + trigger: AcpiIrqTrigger::Level, + polarity: AcpiIrqPolarity::ActiveLow, + }) + } +} + +impl Default for AcpiRouting { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AcpiPciIrqRoute { + pub address: PciAddress, + pub interrupt_pin: u8, + pub gsi: u32, + pub vector: usize, + pub io_apic_id: u8, + pub io_apic_address: u32, + pub io_apic_input: u8, + pub trigger: AcpiIrqTrigger, + pub polarity: AcpiIrqPolarity, +} + pub struct AcpiInfo<'a> { - _private: core::marker::PhantomData<&'a ()>, + pub root: &'a System, + pub path: &'a str, } pub type FnOnProbe = fn(AcpiInfo<'_>, PlatformDevice) -> Result<(), OnProbeError>; -pub fn check_root(_root: AcpiRoot) -> Result<(), DriverError> { - Err(DriverError::Unsupported("acpi")) +pub fn check_root(root: AcpiRoot) -> Result<(), DriverError> { + if root.rsdp == 0 { + return Err(acpi_error(AcpiError::NoValidRsdp)); + } + root.tables().map(|_| ()).map_err(acpi_error) } pub fn init(root: AcpiRoot) -> Result<(), DriverError> { - check_root(root) + let system = System::new(root)?; + info!( + "ACPI initialized: {} PCI ECAM region(s), {} IOAPIC(s)", + system.pci_ecam_regions().len(), + system.routing().io_apics().len() + ); + SYSTEM.call_once(|| system); + Ok(()) } pub(crate) fn try_probe_register( - _register: &DriverRegister, + register: &DriverRegister, ) -> Option>, ProbeError>> { + SYSTEM.get().map(|system| system.probe_register(register)) +} + +pub(crate) fn try_system() -> Option<&'static System> { + SYSTEM.get() +} + +pub fn with_acpi(f: impl FnOnce(&System) -> T) -> Option { + try_system().map(f) +} + +fn acpi_error(err: AcpiError) -> DriverError { + DriverError::Unknown(format!("{err:?}")) +} + +fn on_probe_error(err: impl core::fmt::Debug) -> OnProbeError { + OnProbeError::other(format!("{err:?}")) +} + +fn identity_phys_to_virt(paddr: usize) -> *mut u8 { + paddr as *mut u8 +} + +#[derive(Clone)] +struct AcpiHandler { + root: AcpiRoot, + pci_ecam_regions: Rc>, +} + +impl AcpiHandler { + fn new(root: AcpiRoot, pci_ecam_regions: Vec) -> Self { + Self { + root, + pci_ecam_regions: Rc::new(pci_ecam_regions), + } + } + + fn virt_addr(&self, physical_address: usize) -> usize { + (self.root.phys_to_virt)(physical_address) as usize + } + + fn pci_config_ptr( + &self, + address: acpi::PciAddress, + offset: u16, + width: usize, + ) -> Option<*mut u8> { + let offset = usize::from(offset); + if offset.checked_add(width)? > 4096 { + return None; + } + + let bus = address.bus(); + let region = self.pci_ecam_regions.iter().find(|region| { + address.segment() == region.segment_group + && bus >= region.bus_start + && bus <= region.bus_end + })?; + let bus_offset = usize::from(bus - region.bus_start) << 20; + let device_offset = usize::from(address.device()) << 15; + let function_offset = usize::from(address.function()) << 12; + let physical_address = + region.base_address as usize + bus_offset + device_offset + function_offset + offset; + + Some((self.root.phys_to_virt)(physical_address)) + } +} + +impl Handler for AcpiHandler { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> PhysicalMapping { + PhysicalMapping { + physical_start: physical_address, + virtual_start: NonNull::new(self.virt_addr(physical_address) as *mut T) + .expect("ACPI physical mapping must not be null"), + region_length: size, + mapped_length: size, + handler: self.clone(), + } + } + + fn unmap_physical_region(_region: &PhysicalMapping) {} + + fn read_u8(&self, address: usize) -> u8 { + unsafe { (self.virt_addr(address) as *const u8).read_volatile() } + } + + fn read_u16(&self, address: usize) -> u16 { + unsafe { (self.virt_addr(address) as *const u16).read_volatile() } + } + + fn read_u32(&self, address: usize) -> u32 { + unsafe { (self.virt_addr(address) as *const u32).read_volatile() } + } + + fn read_u64(&self, address: usize) -> u64 { + unsafe { (self.virt_addr(address) as *const u64).read_volatile() } + } + + fn write_u8(&self, address: usize, value: u8) { + unsafe { (self.virt_addr(address) as *mut u8).write_volatile(value) } + } + + fn write_u16(&self, address: usize, value: u16) { + unsafe { (self.virt_addr(address) as *mut u16).write_volatile(value) } + } + + fn write_u32(&self, address: usize, value: u32) { + unsafe { (self.virt_addr(address) as *mut u32).write_volatile(value) } + } + + fn write_u64(&self, address: usize, value: u64) { + unsafe { (self.virt_addr(address) as *mut u64).write_volatile(value) } + } + + fn read_io_u8(&self, port: u16) -> u8 { + read_io_u8(port) + } + + fn read_io_u16(&self, port: u16) -> u16 { + read_io_u16(port) + } + + fn read_io_u32(&self, port: u16) -> u32 { + read_io_u32(port) + } + + fn write_io_u8(&self, port: u16, value: u8) { + write_io_u8(port, value); + } + + fn write_io_u16(&self, port: u16, value: u16) { + write_io_u16(port, value); + } + + fn write_io_u32(&self, port: u16, value: u32) { + write_io_u32(port, value); + } + + fn read_pci_u8(&self, address: acpi::PciAddress, offset: u16) -> u8 { + if let Some(ptr) = self.pci_config_ptr(address, offset, 1) { + return unsafe { ptr.read_volatile() }; + } + pci_legacy_read_u8(address, offset).unwrap_or(u8::MAX) + } + + fn read_pci_u16(&self, address: acpi::PciAddress, offset: u16) -> u16 { + let lo = u16::from(self.read_pci_u8(address, offset)); + let hi = u16::from(self.read_pci_u8(address, offset.saturating_add(1))); + lo | (hi << 8) + } + + fn read_pci_u32(&self, address: acpi::PciAddress, offset: u16) -> u32 { + let b0 = u32::from(self.read_pci_u8(address, offset)); + let b1 = u32::from(self.read_pci_u8(address, offset.saturating_add(1))); + let b2 = u32::from(self.read_pci_u8(address, offset.saturating_add(2))); + let b3 = u32::from(self.read_pci_u8(address, offset.saturating_add(3))); + b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) + } + + fn write_pci_u8(&self, address: acpi::PciAddress, offset: u16, value: u8) { + if let Some(ptr) = self.pci_config_ptr(address, offset, 1) { + unsafe { ptr.write_volatile(value) }; + return; + } + pci_legacy_write_u8(address, offset, value); + } + + fn write_pci_u16(&self, address: acpi::PciAddress, offset: u16, value: u16) { + self.write_pci_u8(address, offset, value as u8); + self.write_pci_u8(address, offset.saturating_add(1), (value >> 8) as u8); + } + + fn write_pci_u32(&self, address: acpi::PciAddress, offset: u16, value: u32) { + self.write_pci_u8(address, offset, value as u8); + self.write_pci_u8(address, offset.saturating_add(1), (value >> 8) as u8); + self.write_pci_u8(address, offset.saturating_add(2), (value >> 16) as u8); + self.write_pci_u8(address, offset.saturating_add(3), (value >> 24) as u8); + } + + fn nanos_since_boot(&self) -> u64 { + 0 + } + + fn stall(&self, microseconds: u64) { + for _ in 0..microseconds.saturating_mul(100) { + core::hint::spin_loop(); + } + } + + fn sleep(&self, milliseconds: u64) { + self.stall(milliseconds.saturating_mul(1000)); + } + + fn create_mutex(&self) -> acpi::Handle { + acpi::Handle(0) + } + + fn acquire(&self, _mutex: acpi::Handle, _timeout: u16) -> Result<(), acpi::aml::AmlError> { + let _guard = NULL_LOCK.lock(); + Ok(()) + } + + fn release(&self, _mutex: acpi::Handle) {} +} + +impl AcpiRoot { + fn handler(self) -> AcpiHandler { + AcpiHandler::new(self, Vec::new()) + } + + fn handler_with_pci_ecam(self, pci_ecam_regions: Vec) -> AcpiHandler { + AcpiHandler::new(self, pci_ecam_regions) + } + + fn tables(self) -> Result, AcpiError> { + unsafe { AcpiTables::from_rsdp(self.handler(), self.rsdp) } + } +} + +pub struct System { + ecam_regions: Vec, + routing: AcpiRouting, + pci: Option, + probed_names: Mutex>, +} + +unsafe impl Send for System {} +unsafe impl Sync for System {} + +struct AcpiPciNamespace { + interpreter: Interpreter, + roots: Vec, +} + +struct AcpiPciRoot { + segment: u16, + bus: u8, + path: String, + prt: Option, +} + +impl System { + pub fn new(root: AcpiRoot) -> Result { + let tables = root.tables().map_err(acpi_error)?; + let ecam_regions = read_pci_ecam_regions(&tables)?; + let routing = read_interrupt_routing(&tables)?; + let pci = match read_pci_namespace(root, ecam_regions.clone()) { + Ok(pci) => Some(pci), + Err(err) => { + warn!("failed to discover ACPI PCI namespace: {err:?}"); + None + } + }; + + Ok(Self { + ecam_regions, + routing, + pci, + probed_names: Mutex::new(BTreeSet::new()), + }) + } + + pub fn pci_ecam_regions(&self) -> &[AcpiPciEcam] { + &self.ecam_regions + } + + pub fn routing(&self) -> &AcpiRouting { + &self.routing + } + + pub fn pci_irq_for_endpoint( + &self, + address: PciAddress, + interrupt_pin: u8, + ) -> Result, OnProbeError> { + let Some(mut irq) = self.resolve_endpoint_gsi(address, interrupt_pin)? else { + return Ok(None); + }; + let Some(gsi) = irq_descriptor_gsi(&irq) else { + return Err(OnProbeError::other(format!( + "ACPI PCI endpoint {} pin {} returned an invalid IRQ descriptor: {:?}", + address, interrupt_pin, irq + ))); + }; + irq.irq = gsi; + + let Some(route) = self.routing.resolve_gsi(gsi) else { + return Err(OnProbeError::other(format!( + "ACPI GSI {} for PCI endpoint {} is not covered by an IOAPIC", + gsi, address + ))); + }; + + Ok(Some(AcpiPciIrqRoute { + address, + interrupt_pin, + gsi: route.gsi, + vector: route.vector, + io_apic_id: route.io_apic_id, + io_apic_address: route.io_apic_address, + io_apic_input: route.io_apic_input, + trigger: irq_trigger(irq.trigger), + polarity: irq_polarity(irq.polarity), + })) + } + + fn resolve_endpoint_gsi( + &self, + address: PciAddress, + interrupt_pin: u8, + ) -> Result, OnProbeError> { + let pin = acpi_pin(interrupt_pin)?; + let Some(pci) = &self.pci else { + return Ok(None); + }; + let roots = self.pci_root_candidates(address, pci); + if roots.is_empty() { + return Ok(None); + } + + for root in roots { + let Some(prt) = &root.prt else { + continue; + }; + + match prt.route( + u16::from(address.device()), + u16::from(address.function()), + pin, + &pci.interpreter, + ) { + Ok(route) => return Ok(Some(route)), + Err(AmlError::PrtNoEntry) => {} + Err(err) => return Err(on_probe_error(err)), + } + } + + Ok(None) + } + + fn pci_root_candidates<'a>( + &self, + address: PciAddress, + pci: &'a AcpiPciNamespace, + ) -> Vec<&'a AcpiPciRoot> { + let mut roots = Vec::new(); + if let Some(root) = pci + .roots + .iter() + .find(|root| root.segment == address.segment() && root.bus == address.bus()) + .or_else(|| { + pci.roots + .iter() + .find(|root| root.segment == address.segment() && root.bus == 0) + }) + { + roots.push(root); + } + for path in PCI_ROOT_FALLBACK_PATHS { + if let Some(root) = pci.roots.iter().find(|root| root.path == *path) + && !roots.iter().any(|candidate| candidate.path == root.path) + { + roots.push(root); + } + } + roots + } + + fn probe_register( + &self, + register: &DriverRegister, + ) -> Result>, ProbeError> { + let mut out = Vec::new(); + for probe in register.probe_kinds { + let ProbeKind::Acpi { ids, on_probe } = probe else { + continue; + }; + if ids.is_empty() { + continue; + } + if self.probed_names.lock().contains(register.name) { + continue; + } + + let desc = crate::Descriptor { + name: register.name, + device_id: DeviceId::new(), + irq_parent: None, + }; + let info = AcpiInfo { + root: self, + path: "\\", + }; + let res = on_probe(info, PlatformDevice::new(desc)); + if res.is_ok() { + self.probed_names.lock().insert(register.name); + } + out.push(res); + } + Ok(out) + } +} + +fn read_pci_ecam_regions( + tables: &AcpiTables, +) -> Result, DriverError> { + let regions = PciConfigRegions::new(tables).map_err(acpi_error)?; + Ok(regions + .regions + .iter() + .map(|region| AcpiPciEcam { + segment_group: region.pci_segment_group, + bus_start: region.bus_number_start, + bus_end: region.bus_number_end, + base_address: region.base_address, + }) + .collect()) +} + +fn read_interrupt_routing(tables: &AcpiTables) -> Result { + let (model, _) = InterruptModel::new(tables).map_err(acpi_error)?; + let mut routing = AcpiRouting::new(); + if let InterruptModel::Apic(apic) = model { + for io_apic in &apic.io_apics { + routing.add_io_apic(AcpiIoApic { + id: io_apic.id, + address: io_apic.address, + gsi_base: io_apic.global_system_interrupt_base, + redirection_entries: 24, + }); + } + } + Ok(routing) +} + +fn read_pci_namespace( + root: AcpiRoot, + ecam_regions: Vec, +) -> Result { + let handler = root.handler_with_pci_ecam(ecam_regions); + let tables = unsafe { AcpiTables::from_rsdp(handler.clone(), root.rsdp) }?; + let platform = AcpiPlatform::new(tables, handler)?; + let interpreter = Interpreter::new_from_platform(&platform)?; + interpreter.initialize_namespace(); + + let mut roots = Vec::new(); + { + let mut namespace = interpreter.namespace.lock().clone(); + namespace + .traverse(|path, level| { + if level.kind == NamespaceLevelKind::Device && is_pci_root(&interpreter, path) { + let segment = + eval_integer_child(&interpreter, path, "_SEG")?.unwrap_or(0) as u16; + let bus = eval_integer_child(&interpreter, path, "_BBN")?.unwrap_or(0) as u8; + roots.push(AcpiPciRoot { + segment, + bus, + path: path.as_string(), + prt: None, + }); + } + Ok(true) + }) + .map_err(AcpiError::Aml)?; + } + + for root in &mut roots { + root.prt = read_pci_routing_table(&interpreter, &root.path)?; + } + for path in PCI_ROOT_FALLBACK_PATHS { + if roots.iter().any(|root| root.path == *path) { + continue; + } + let Some(prt) = read_pci_routing_table(&interpreter, path)? else { + continue; + }; + roots.push(AcpiPciRoot { + segment: 0, + bus: 0, + path: path.to_string(), + prt: Some(prt), + }); + } + + Ok(AcpiPciNamespace { interpreter, roots }) +} + +fn read_pci_routing_table( + interpreter: &Interpreter, + root_path: &str, +) -> Result, AcpiError> { + let prt_path = AmlName::from_str(&format!("{root_path}._PRT")).map_err(AcpiError::Aml)?; + match PciRoutingTable::from_prt_path(prt_path, interpreter) { + Ok(prt) => Ok(Some(prt)), + Err(AmlError::ObjectDoesNotExist(_)) | Err(AmlError::LevelDoesNotExist(_)) => Ok(None), + Err(err) => Err(AcpiError::Aml(err)), + } +} + +fn is_pci_root(interpreter: &Interpreter, path: &AmlName) -> bool { + has_pci_root_id(interpreter, path, "_HID") || has_pci_root_id(interpreter, path, "_CID") +} + +fn has_pci_root_id(interpreter: &Interpreter, path: &AmlName, name: &str) -> bool { + let Ok(Some(value)) = eval_child(interpreter, path, name) else { + return false; + }; + object_matches_pci_root_id(&value) +} + +fn object_matches_pci_root_id(value: &Object) -> bool { + match value { + Object::String(id) => matches!(id.as_str(), "PNP0A03" | "PNP0A08"), + Object::Integer(id) => { + let id = decode_eisa_id(*id as u32); + matches!(id.as_deref(), Some("PNP0A03" | "PNP0A08")) + } + Object::Package(values) => values.iter().any(|value| object_matches_pci_root_id(value)), + _ => false, + } +} + +fn eval_integer_child( + interpreter: &Interpreter, + path: &AmlName, + name: &str, +) -> Result, AmlError> { + eval_child(interpreter, path, name)? + .map(|value| value.as_integer()) + .transpose() +} + +fn eval_child( + interpreter: &Interpreter, + path: &AmlName, + name: &str, +) -> Result>, AmlError> { + let child = AmlName::from_str(name)?.resolve(path)?; + match interpreter.evaluate_if_present(child, Vec::new())? { + Some(value) => Ok(Some(Rc::new((*value).clone()))), + None => Ok(None), + } +} + +fn decode_eisa_id(raw: u32) -> Option { + if raw == 0 { + return None; + } + let chars = [ + (((raw >> 26) & 0x1f) as u8).wrapping_add(b'@'), + (((raw >> 21) & 0x1f) as u8).wrapping_add(b'@'), + (((raw >> 16) & 0x1f) as u8).wrapping_add(b'@'), + ]; + if !chars.iter().all(u8::is_ascii_uppercase) { + return None; + } + Some(format!( + "{}{}{}{:04X}", + chars[0] as char, + chars[1] as char, + chars[2] as char, + raw & 0xffff + )) +} + +fn acpi_pin(interrupt_pin: u8) -> Result { + match interrupt_pin { + 1 => Ok(Pin::IntA), + 2 => Ok(Pin::IntB), + 3 => Ok(Pin::IntC), + 4 => Ok(Pin::IntD), + _ => Err(OnProbeError::other(format!( + "invalid PCI interrupt pin {interrupt_pin}" + ))), + } +} + +fn irq_descriptor_gsi(descriptor: &IrqDescriptor) -> Option { + let irq = descriptor.irq; + if !descriptor.is_consumer && irq.count_ones() == 1 && irq <= u16::MAX as u32 { + Some(irq.trailing_zeros()) + } else { + Some(irq) + } +} + +fn irq_trigger(trigger: InterruptTrigger) -> AcpiIrqTrigger { + match trigger { + InterruptTrigger::Edge => AcpiIrqTrigger::Edge, + InterruptTrigger::Level => AcpiIrqTrigger::Level, + } +} + +fn irq_polarity(polarity: InterruptPolarity) -> AcpiIrqPolarity { + match polarity { + InterruptPolarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh, + InterruptPolarity::ActiveLow => AcpiIrqPolarity::ActiveLow, + } +} + +#[cfg(target_arch = "x86_64")] +fn pci_legacy_read_u8(address: acpi::PciAddress, offset: u16) -> Option { + let value = pci_legacy_read_aligned_u32(address, offset)?; + let shift = u32::from(offset & 0b11) * 8; + Some((value >> shift) as u8) +} + +#[cfg(not(target_arch = "x86_64"))] +fn pci_legacy_read_u8(_address: acpi::PciAddress, _offset: u16) -> Option { None } + +#[cfg(target_arch = "x86_64")] +fn pci_legacy_write_u8(address: acpi::PciAddress, offset: u16, value: u8) { + let Some(old) = pci_legacy_read_aligned_u32(address, offset) else { + return; + }; + let shift = u32::from(offset & 0b11) * 8; + let mask = 0xff_u32 << shift; + let new = (old & !mask) | (u32::from(value) << shift); + pci_legacy_write_aligned_u32(address, offset, new); +} + +#[cfg(not(target_arch = "x86_64"))] +fn pci_legacy_write_u8(_address: acpi::PciAddress, _offset: u16, _value: u8) {} + +#[cfg(target_arch = "x86_64")] +fn pci_legacy_config_address(address: acpi::PciAddress, offset: u16) -> Option { + if address.segment() != 0 || offset >= 256 { + return None; + } + + Some( + 0x8000_0000 + | (u32::from(address.bus()) << 16) + | (u32::from(address.device()) << 11) + | (u32::from(address.function()) << 8) + | u32::from(offset & !0b11), + ) +} + +#[cfg(target_arch = "x86_64")] +fn pci_legacy_read_aligned_u32(address: acpi::PciAddress, offset: u16) -> Option { + let config_address = pci_legacy_config_address(address, offset)?; + unsafe { + x86::io::outl(0xcf8, config_address); + Some(x86::io::inl(0xcfc)) + } +} + +#[cfg(target_arch = "x86_64")] +fn pci_legacy_write_aligned_u32(address: acpi::PciAddress, offset: u16, value: u32) { + if let Some(config_address) = pci_legacy_config_address(address, offset) { + unsafe { + x86::io::outl(0xcf8, config_address); + x86::io::outl(0xcfc, value); + } + } +} + +pub fn acpi_trigger(trigger: TriggerMode) -> AcpiIrqTrigger { + match trigger { + TriggerMode::Edge => AcpiIrqTrigger::Edge, + TriggerMode::Level => AcpiIrqTrigger::Level, + _ => AcpiIrqTrigger::Level, + } +} + +pub fn acpi_polarity(polarity: Polarity) -> AcpiIrqPolarity { + match polarity { + Polarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh, + Polarity::ActiveLow => AcpiIrqPolarity::ActiveLow, + _ => AcpiIrqPolarity::ActiveLow, + } +} + +#[cfg(target_arch = "x86_64")] +fn read_io_u8(port: u16) -> u8 { + unsafe { x86::io::inb(port) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn read_io_u8(_port: u16) -> u8 { + 0 +} + +#[cfg(target_arch = "x86_64")] +fn read_io_u16(port: u16) -> u16 { + unsafe { x86::io::inw(port) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn read_io_u16(_port: u16) -> u16 { + 0 +} + +#[cfg(target_arch = "x86_64")] +fn read_io_u32(port: u16) -> u32 { + unsafe { x86::io::inl(port) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn read_io_u32(_port: u16) -> u32 { + 0 +} + +#[cfg(target_arch = "x86_64")] +fn write_io_u8(port: u16, value: u8) { + unsafe { x86::io::outb(port, value) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn write_io_u8(_port: u16, _value: u8) {} + +#[cfg(target_arch = "x86_64")] +fn write_io_u16(port: u16, value: u16) { + unsafe { x86::io::outw(port, value) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn write_io_u16(_port: u16, _value: u16) {} + +#[cfg(target_arch = "x86_64")] +fn write_io_u32(port: u16, value: u32) { + unsafe { x86::io::outl(port, value) } +} + +#[cfg(not(target_arch = "x86_64"))] +fn write_io_u32(_port: u16, _value: u32) {} diff --git a/drivers/rdrive/tests/init_sources.rs b/drivers/rdrive/tests/init_sources.rs index 35106a73c7..1a0baf0a5f 100644 --- a/drivers/rdrive/tests/init_sources.rs +++ b/drivers/rdrive/tests/init_sources.rs @@ -31,13 +31,10 @@ static STATIC_REGISTER: DriverRegister = DriverRegister { fn unsupported_source_does_not_leave_static_backend_initialized() { let err = init_sources(&[ PlatformSource::Static, - PlatformSource::Acpi(AcpiRoot { rsdp: 0 }), + PlatformSource::Acpi(AcpiRoot::identity(0)), ]) - .expect_err("acpi should reject the source set before committing static state"); - assert!(matches!( - err, - rdrive::error::DriverError::Unsupported("acpi") - )); + .expect_err("invalid acpi should reject the source set before committing static state"); + assert!(matches!(err, rdrive::error::DriverError::Unknown(_))); rdrive::init(Platform::Static).expect("static platform should init"); rdrive::register_add(STATIC_REGISTER.clone()); diff --git a/drivers/rdrive/tests/phase1.rs b/drivers/rdrive/tests/phase1.rs index 15f17e8455..97b2500cb3 100644 --- a/drivers/rdrive/tests/phase1.rs +++ b/drivers/rdrive/tests/phase1.rs @@ -39,13 +39,10 @@ fn static_probe_registers_device() { #[test] fn acpi_source_is_unsupported() { - let err = - rdrive::init(Platform::Acpi(AcpiRoot { rsdp: 0 })).expect_err("acpi is not supported yet"); + let err = rdrive::init(Platform::Acpi(AcpiRoot::identity(0))) + .expect_err("invalid acpi should be rejected"); - assert!(matches!( - err, - rdrive::error::DriverError::Unsupported("acpi") - )); + assert!(matches!(err, rdrive::error::DriverError::Unknown(_))); } #[test] @@ -54,3 +51,25 @@ fn fdt_phandle_lookup_is_none_without_fdt_source() { assert!(rdrive::fdt_phandle_to_device_id(1.into()).is_none()); } + +#[test] +fn acpi_ioapic_routes_map_gsi_to_stable_vector() { + use rdrive::probe::acpi::{AcpiIoApic, AcpiRouting}; + + let mut routing = AcpiRouting::new(); + routing.add_io_apic(AcpiIoApic { + id: 0, + address: 0xfec0_0000, + gsi_base: 0, + redirection_entries: 24, + }); + + let irq = routing + .resolve_gsi(16) + .expect("gsi 16 should be handled by the IOAPIC"); + assert_eq!(irq.gsi, 16); + assert_eq!(irq.io_apic_id, 0); + assert_eq!(irq.io_apic_input, 16); + assert_eq!(irq.vector, 0x40); + assert!(routing.resolve_gsi(24).is_none()); +} diff --git a/os/StarryOS/configs/board/qemu-x86_64.toml b/os/StarryOS/configs/board/qemu-x86_64.toml index 1128aa2eb9..14efa6b20a 100644 --- a/os/StarryOS/configs/board/qemu-x86_64.toml +++ b/os/StarryOS/configs/board/qemu-x86_64.toml @@ -2,13 +2,11 @@ target = "x86_64-unknown-none" env = {} log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", + "axplat-dyn/efi", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", ] -plat_dyn = false +plat_dyn = true diff --git a/os/StarryOS/configs/qemu/qemu-x86_64.toml b/os/StarryOS/configs/qemu/qemu-x86_64.toml index fc535e5a51..ae05fb421b 100644 --- a/os/StarryOS/configs/qemu/qemu-x86_64.toml +++ b/os/StarryOS/configs/qemu/qemu-x86_64.toml @@ -2,6 +2,8 @@ args = [ "-m", "512M", "-nographic", + "-machine", + "q35", "-device", "virtio-blk-pci,drive=disk0", "-drive", @@ -11,7 +13,7 @@ args = [ "-netdev", "user,id=net0", ] -uefi = false -to_bin = false +uefi = true +to_bin = true success_regex = [] fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/os/arceos/modules/axconfig/src/driver_dyn_config.rs b/os/arceos/modules/axconfig/src/driver_dyn_config.rs index c97818c528..992cf2a6b6 100644 --- a/os/arceos/modules/axconfig/src/driver_dyn_config.rs +++ b/os/arceos/modules/axconfig/src/driver_dyn_config.rs @@ -25,17 +25,34 @@ mod arch { pub const KERNEL_BASE_VADDR: usize = 0xffff_ffff_8000_0000; } -#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))] +#[cfg(target_arch = "x86_64")] mod arch { - pub const ARCH: &str = "aarch64"; - pub const PACKAGE: &str = "axplat-aarch64-generic"; - pub const PLATFORM: &str = "aarch64-generic"; - pub const TIMER_IRQ: usize = 0xf0; - pub const IPI_IRQ: usize = 0; + pub const ARCH: &str = "x86_64"; + pub const PACKAGE: &str = "axplat-dyn"; + pub const PLATFORM: &str = "x86_64-plat-dyn"; + pub const TIMER_IRQ: usize = 0; + pub const IPI_IRQ: usize = 0xf3; pub const KERNEL_ASPACE_BASE: usize = 0xffff_8000_0000_0000; pub const KERNEL_ASPACE_SIZE: usize = 0x0000_7fff_ffff_f000; - pub const KERNEL_BASE_PADDR: usize = 0x20_0000; - pub const KERNEL_BASE_VADDR: usize = 0xffff_8000_0020_0000; + pub const KERNEL_BASE_PADDR: usize = 0; + pub const KERNEL_BASE_VADDR: usize = 0xffff_8000_0000_0000; +} + +#[cfg(not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86_64" +)))] +mod arch { + pub const ARCH: &str = "unknown"; + pub const PACKAGE: &str = "axplat-dyn"; + pub const PLATFORM: &str = "unknown-plat-dyn"; + pub const TIMER_IRQ: usize = 0; + pub const IPI_IRQ: usize = 0; + pub const KERNEL_ASPACE_BASE: usize = 0; + pub const KERNEL_ASPACE_SIZE: usize = 0; + pub const KERNEL_BASE_PADDR: usize = 0; + pub const KERNEL_BASE_VADDR: usize = 0; } #[doc = " Architecture identifier."] @@ -111,3 +128,12 @@ pub mod plat { #[doc = " No need."] pub const PHYS_VIRT_OFFSET: usize = 0; } + +#[cfg(test)] +mod tests { + #[test] + #[cfg(target_arch = "x86_64")] + fn x86_64_dynamic_platform_uses_dedicated_ipi_vector() { + assert_eq!(super::devices::IPI_IRQ, 0xf3); + } +} diff --git a/platforms/axplat-dyn/Cargo.toml b/platforms/axplat-dyn/Cargo.toml index 7bb0a2517a..92f0a0d01f 100644 --- a/platforms/axplat-dyn/Cargo.toml +++ b/platforms/axplat-dyn/Cargo.toml @@ -14,6 +14,7 @@ default = ["smp", "irq" ] smp = ["ax-plat/smp"] irq = ["ax-plat/irq"] rtc = [] +efi = ["somehal/efi"] fp-simd = ["ax-cpu/fp-simd"] uspace = ["somehal/uspace"] hv = ["somehal/hv", "ax-cpu/arm-el2"] diff --git a/platforms/axplat-dyn/link.ld b/platforms/axplat-dyn/link.ld index 9e69b8dbfc..2b9a58b2cd 100644 --- a/platforms/axplat-dyn/link.ld +++ b/platforms/axplat-dyn/link.ld @@ -12,6 +12,12 @@ PROVIDE(_ebss = __bss_stop); PROVIDE(boot_stack = __cpu0_stack); PROVIDE(boot_stack_top = __cpu0_stack_top); _percpu_load_start = __percpu_start; +/* + * ax-cpu x86_64 trap assembly uses the historical __PERCPU_TSS symbol as a + * GS-relative offset. ax-percpu/custom-base exports the per-CPU object as TSS, + * so provide the expected offset without forcing the .percpu VMA to zero. + */ +PROVIDE(__PERCPU_TSS = TSS - _percpu_load_start); _skernel = VM_LOAD_ADDRESS; _ekernel = __kernel_code_end; diff --git a/platforms/axplat-dyn/src/generic_timer.rs b/platforms/axplat-dyn/src/generic_timer.rs index b0881cc7c9..b934c807e3 100644 --- a/platforms/axplat-dyn/src/generic_timer.rs +++ b/platforms/axplat-dyn/src/generic_timer.rs @@ -14,7 +14,7 @@ pub(crate) fn ticks_to_nanos(ticks: u64) -> u64 { if freq == 0 { return 0; } - (ticks * ax_plat::time::NANOS_PER_SEC) / freq + ((ticks as u128 * ax_plat::time::NANOS_PER_SEC as u128) / freq as u128) as u64 } pub(crate) fn nanos_to_ticks(nanos: u64) -> u64 { @@ -22,7 +22,7 @@ pub(crate) fn nanos_to_ticks(nanos: u64) -> u64 { if freq == 0 { return 0; } - (nanos * freq) / ax_plat::time::NANOS_PER_SEC + ((nanos as u128 * freq as u128) / ax_plat::time::NANOS_PER_SEC as u128) as u64 } pub fn try_init_epoch_offset(epoch_time_nanos: u64) -> bool { diff --git a/platforms/axplat-dyn/src/init.rs b/platforms/axplat-dyn/src/init.rs index 5d34e42675..4874b72703 100644 --- a/platforms/axplat-dyn/src/init.rs +++ b/platforms/axplat-dyn/src/init.rs @@ -9,11 +9,9 @@ impl InitIf for InitIfImpl { /// This function should be called immediately after the kernel has booted, /// and performed earliest platform configuration and initialization (e.g., /// early console, clocking). - fn init_early(cpu_id: usize, _dtb: usize) { - #[cfg(not(target_arch = "riscv64"))] - let _ = cpu_id; + fn init_early(_cpu_id: usize, _dtb: usize) { #[cfg(target_arch = "riscv64")] - somehal::arch::register_current_cpu_id(cpu_id, ax_plat::percpu::this_cpu_id); + somehal::arch::register_current_cpu_id(_cpu_id, ax_plat::percpu::this_cpu_id); ax_cpu::init::init_trap(); #[cfg(all(target_arch = "aarch64", feature = "fp-simd"))] { @@ -25,11 +23,9 @@ impl InitIf for InitIfImpl { /// Initializes the platform at the early stage for secondary cores. #[cfg(feature = "smp")] - fn init_early_secondary(cpu_id: usize) { - #[cfg(not(target_arch = "riscv64"))] - let _ = cpu_id; + fn init_early_secondary(_cpu_id: usize) { #[cfg(target_arch = "riscv64")] - somehal::arch::register_current_cpu_id(cpu_id, ax_plat::percpu::this_cpu_id); + somehal::arch::register_current_cpu_id(_cpu_id, ax_plat::percpu::this_cpu_id); ax_cpu::init::init_trap(); #[cfg(all(target_arch = "aarch64", feature = "fp-simd"))] { diff --git a/platforms/axplat-dyn/src/mem.rs b/platforms/axplat-dyn/src/mem.rs index 3355b939a0..6ed9205201 100644 --- a/platforms/axplat-dyn/src/mem.rs +++ b/platforms/axplat-dyn/src/mem.rs @@ -7,8 +7,56 @@ static FREE_LIST: Once> = Once::new(); static RESERVED_LIST: Once> = Once::new(); static MMIO_LIST: Once> = Once::new(); +#[cfg(target_arch = "x86_64")] +const X86_FIXED_MMIO_RANGES: &[RawRange] = &[ + (0xfec0_0000, 0x1000), // IOAPIC + (0xfed0_0000, 0x1000), // HPET + (0xfee0_0000, 0x1000), // LAPIC +]; + +#[cfg(target_arch = "x86_64")] +const X86_RESERVED_RAM_RANGES: &[RawRange] = &[ + // Match the static q35 platform: the low 2 MiB contains legacy holes and + // boot-time data such as the AP trampoline, and must not enter the heap. + (0, 0x20_0000), +]; + struct MemIfImpl; +fn push_non_overlapping(list: &mut Vec, range: RawRange) { + let (start, size) = range; + if size == 0 { + return; + } + + list.sort_unstable_by_key(|&(start, _)| start); + let original_len = list.len(); + let mut cursor = start; + let end = start.saturating_add(size); + for index in 0..original_len { + let (existing_start, existing_size) = list[index]; + let existing_end = existing_start.saturating_add(existing_size); + if existing_end <= cursor { + continue; + } + if existing_start >= end { + break; + } + if existing_start > cursor { + list.push((cursor, existing_start - cursor)).unwrap(); + } + cursor = cursor.max(existing_end); + if cursor >= end { + break; + } + } + + if cursor < end { + list.push((cursor, end - cursor)).unwrap(); + } + list.sort_unstable_by_key(|&(start, _)| start); +} + #[impl_plat_interface] impl MemIf for MemIfImpl { fn phys_ram_ranges() -> &'static [RawRange] { @@ -26,12 +74,16 @@ impl MemIf for MemIfImpl { fn reserved_phys_ram_ranges() -> &'static [RawRange] { RESERVED_LIST.call_once(|| { let mut list = Vec::new(); + #[cfg(target_arch = "x86_64")] + for &range in X86_RESERVED_RAM_RANGES { + push_non_overlapping(&mut list, range); + } for r in somehal::mem::memory_map() { if matches!( r.memory_type, MemoryType::Reserved | MemoryType::KImage | MemoryType::PerCpuData ) { - list.push((r.physical_start, r.size_in_bytes)).unwrap(); + push_non_overlapping(&mut list, (r.physical_start, r.size_in_bytes)); } } list @@ -43,9 +95,14 @@ impl MemIf for MemIfImpl { let mut list = Vec::new(); for r in somehal::mem::memory_map() { if matches!(r.memory_type, MemoryType::Mmio) { - list.push((r.physical_start, r.size_in_bytes)).unwrap(); + push_non_overlapping(&mut list, (r.physical_start, r.size_in_bytes)); } } + #[cfg(target_arch = "x86_64")] + for &range in X86_FIXED_MMIO_RANGES { + // QEMU/OVMF does not always report fixed PC chipset MMIO holes. + push_non_overlapping(&mut list, range); + } list }) } diff --git a/platforms/somehal/Cargo.toml b/platforms/somehal/Cargo.toml index 228a0d4b47..d6eef69e12 100644 --- a/platforms/somehal/Cargo.toml +++ b/platforms/somehal/Cargo.toml @@ -40,3 +40,7 @@ arm-gic-driver = {workspace = true, features = ["rdif"]} ax-riscv-plic = { workspace = true } riscv.workspace = true sbi-rt = { workspace = true, features = ["legacy"] } + +[target.'cfg(target_arch = "x86_64")'.dependencies] +x2apic = "0.5" +x86 = "0.52" diff --git a/platforms/somehal/src/arch/x86_64/mod.rs b/platforms/somehal/src/arch/x86_64/mod.rs index be2d02e560..a97ac15a90 100644 --- a/platforms/somehal/src/arch/x86_64/mod.rs +++ b/platforms/somehal/src/arch/x86_64/mod.rs @@ -1,14 +1,78 @@ -use crate::common::PlatOp; +use alloc::vec::Vec; + +use rdrive::probe::acpi::{AcpiIoApic, AcpiIrqPolarity, AcpiIrqTrigger}; +use spin::Mutex; +use x2apic::ioapic::{IoApic, IrqFlags, IrqMode}; + +use crate::{common::PlatOp, irq::_handle_irq}; pub struct Plat; +const APIC_TIMER_VECTOR: usize = 0x20; +const IOAPIC_VECTOR_BASE: usize = 0x30; + +const LAPIC_REG_EOI: u32 = 0x0b0; +const LAPIC_REG_ICR_LOW: u32 = 0x300; +const LAPIC_REG_ICR_HIGH: u32 = 0x310; +const ICR_DELIVERY_PENDING: u32 = 1 << 12; +const ICR_FIXED_BASE: u32 = 0x0000_4000; +const ICR_DEST_SELF: u32 = 0x0004_0000; +const ICR_DEST_ALL_EXCLUDING_SELF: u32 = 0x000c_0000; + +static IOAPICS: Mutex> = Mutex::new(Vec::new()); + +struct IoApicState { + info: AcpiIoApic, + ioapic: IoApic, +} + +impl IoApicState { + fn contains(&self, gsi: u32) -> bool { + let start = self.info.gsi_base; + let end = start.saturating_add(u32::from(self.info.redirection_entries)); + (start..end).contains(&gsi) + } + + fn input_for(&self, gsi: u32) -> Option { + let input = gsi.checked_sub(self.info.gsi_base)?; + u8::try_from(input).ok() + } +} + impl PlatOp for Plat { fn irq_set_enable(irq: rdrive::IrqId, enable: bool) { - let raw = irq.into(); - let irq = someboot::irq::IrqId::new(raw); + let raw = irq.raw(); - if irq == someboot::irq::systimer_irq() { - someboot::irq::irq_set_enable(irq, enable); + if raw == someboot::irq::systimer_irq().raw() { + someboot::irq::irq_set_enable(someboot::irq::IrqId::new(raw), enable); + return; + } + + set_ioapic_vector_enable(raw, enable); + } + + fn send_ipi(irq: rdrive::IrqId, target: crate::irq::IpiTarget) { + let vector = irq.raw() as u8; + + unsafe { + match target { + crate::irq::IpiTarget::Current { .. } => { + send_lapic_ipi(0, ICR_FIXED_BASE | ICR_DEST_SELF | u32::from(vector)) + } + crate::irq::IpiTarget::Other { cpu_id } => { + let Some(apic_id) = someboot::smp::cpu_idx_to_id(cpu_id) else { + warn!("failed to resolve CPU index {cpu_id} to APIC ID"); + return; + }; + send_lapic_ipi(raw_apic_id(apic_id), ICR_FIXED_BASE | u32::from(vector)); + } + crate::irq::IpiTarget::AllExceptCurrent { .. } => { + send_lapic_ipi( + 0, + ICR_FIXED_BASE | ICR_DEST_ALL_EXCLUDING_SELF | u32::from(vector), + ); + } + } } } @@ -16,6 +80,18 @@ impl PlatOp for Plat { someboot::irq::systimer_irq() } + fn irq_handler_with_raw(raw: usize) -> Option { + if raw == APIC_TIMER_VECTOR { + _handle_irq(raw.into()); + lapic_eoi(); + return Some(someboot::irq::systimer_irq()); + } + + _handle_irq(raw.into()); + lapic_eoi(); + Some(someboot::irq::IrqId::new(raw)) + } + fn systick_irq() -> rdrive::IrqId { someboot::irq::systimer_irq().raw().into() } @@ -25,4 +101,136 @@ impl PlatOp for Plat { fn secondary_init_intc(_cpu_idx: usize) {} fn secondary_init_systick() {} + + fn send_ipi_to_cpu(cpu_id: usize) { + Self::send_ipi( + APIC_TIMER_VECTOR.into(), + crate::irq::IpiTarget::Other { cpu_id }, + ); + } +} + +pub fn init_acpi_irq() { + init_ioapics_from_acpi(); +} + +fn init_ioapics_from_acpi() { + let Some(routing) = rdrive::probe::acpi::with_acpi(|system| system.routing().clone()) else { + return; + }; + + let mut ioapics = IOAPICS.lock(); + if !ioapics.is_empty() { + return; + } + + for info in routing.io_apics().iter().copied() { + let ioapic_base = someboot::mem::phys_to_virt(info.address as usize) as u64; + let mut ioapic = unsafe { IoApic::new(ioapic_base) }; + let max_entry = unsafe { ioapic.max_table_entry() }; + let redirection_entries = max_entry.saturating_add(1); + + unsafe { + ioapic.init(IOAPIC_VECTOR_BASE as u8); + for input in 0..=max_entry { + let mut entry = ioapic.table_entry(input); + entry.set_flags(entry.flags() | IrqFlags::MASKED); + ioapic.set_table_entry(input, entry); + } + } + + info!( + "ACPI IOAPIC initialized: id={} base={:#x} gsi_base={} entries={}", + info.id, info.address, info.gsi_base, redirection_entries + ); + ioapics.push(IoApicState { + info: AcpiIoApic { + redirection_entries, + ..info + }, + ioapic, + }); + } +} + +fn set_ioapic_vector_enable(vector: usize, enable: bool) { + let Some(gsi) = vector.checked_sub(IOAPIC_VECTOR_BASE).map(|gsi| gsi as u32) else { + return; + }; + + let mut ioapics = IOAPICS.lock(); + let Some(ioapic) = ioapics.iter_mut().find(|ioapic| ioapic.contains(gsi)) else { + return; + }; + let Some(input) = ioapic.input_for(gsi) else { + return; + }; + + unsafe { + let mut entry = ioapic.ioapic.table_entry(input); + entry.set_vector(vector as u8); + entry.set_mode(IrqMode::Fixed); + entry.set_flags(intx_flags( + AcpiIrqTrigger::Level, + AcpiIrqPolarity::ActiveLow, + )); + entry.set_dest(0); + ioapic.ioapic.set_table_entry(input, entry); + + if enable { + ioapic.ioapic.enable_irq(input); + } else { + ioapic.ioapic.disable_irq(input); + } + } +} + +fn intx_flags(trigger: AcpiIrqTrigger, polarity: AcpiIrqPolarity) -> IrqFlags { + let mut flags = IrqFlags::empty(); + if trigger == AcpiIrqTrigger::Level { + flags |= IrqFlags::LEVEL_TRIGGERED; + } + if polarity == AcpiIrqPolarity::ActiveLow { + flags |= IrqFlags::LOW_ACTIVE; + } + flags +} + +fn lapic_eoi() { + unsafe { + lapic_write(LAPIC_REG_EOI, 0); + } +} + +fn raw_apic_id(id: usize) -> u32 { + (id as u32) << 24 +} + +unsafe fn send_lapic_ipi(destination: u32, icr_low: u32) { + unsafe { + lapic_write(LAPIC_REG_ICR_HIGH, destination); + lapic_write(LAPIC_REG_ICR_LOW, icr_low); + while lapic_read(LAPIC_REG_ICR_LOW) & ICR_DELIVERY_PENDING != 0 { + core::hint::spin_loop(); + } + } +} + +unsafe fn lapic_read(offset: u32) -> u32 { + let ptr = lapic_ptr(offset) as *const u32; + unsafe { ptr.read_volatile() } +} + +unsafe fn lapic_write(offset: u32, value: u32) { + let ptr = lapic_ptr(offset); + unsafe { + ptr.write_volatile(value); + } +} + +fn lapic_ptr(offset: u32) -> *mut u32 { + const IA32_APIC_BASE: u32 = 0x1b; + const LAPIC_BASE_MASK: u64 = 0xffff_f000; + let base = unsafe { x86::msr::rdmsr(IA32_APIC_BASE) & LAPIC_BASE_MASK } as usize; + unsafe { someboot::mem::phys_to_virt(base).add(offset as usize) }.cast() } diff --git a/platforms/somehal/src/driver.rs b/platforms/somehal/src/driver.rs index 0336f165c9..b8bc8fd3ba 100644 --- a/platforms/somehal/src/driver.rs +++ b/platforms/somehal/src/driver.rs @@ -9,9 +9,10 @@ pub fn rdrive_setup() { .unwrap(); } else if let Some(rsdp) = someboot::rsdp_addr_phys() { info!("Initializing rdrive with ACPI RSDP at {:#x}", rsdp); - if let Err(err) = rdrive::init(rdrive::Platform::Acpi(rdrive::probe::acpi::AcpiRoot { + if let Err(err) = rdrive::init(rdrive::Platform::Acpi(rdrive::probe::acpi::AcpiRoot::new( rsdp, - })) { + someboot::mem::phys_to_virt, + ))) { warn!( "failed to initialize rdrive with ACPI RSDP {:#x}: {:?}", rsdp, err diff --git a/platforms/somehal/src/lib.rs b/platforms/somehal/src/lib.rs index bec4b660b3..f69ef8b37c 100644 --- a/platforms/somehal/src/lib.rs +++ b/platforms/somehal/src/lib.rs @@ -46,6 +46,8 @@ pub fn post_paging() { someboot::post_allocator(); // note: irq controller should be initialized when probe. driver::rdrive_setup(); + #[cfg(target_arch = "x86_64")] + arch::init_acpi_irq(); } #[unsafe(no_mangle)] diff --git a/scripts/axbuild/src/arceos/build.rs b/scripts/axbuild/src/arceos/build.rs index 56d2f312ef..c260669334 100644 --- a/scripts/axbuild/src/arceos/build.rs +++ b/scripts/axbuild/src/arceos/build.rs @@ -543,15 +543,25 @@ AX_IP = "10.0.2.15" } #[test] - fn base_cargo_config_defaults_to_bin_false_for_x86_64_targets() { + fn prepared_cargo_config_defaults_x86_64_to_dynamic_platform() { + let metadata = repo_metadata(); let cargo = ArceosBuildInfo::default_for_target("x86_64-unknown-none") - .into_base_cargo_config_with_log( - "ax-helloworld".to_string(), - "x86_64-unknown-none".to_string(), - vec![], - ); + .into_prepared_base_cargo_config_with_metadata( + "ax-helloworld", + "x86_64-unknown-none", + None, + &metadata, + ) + .unwrap(); - assert!(!cargo.to_bin); + assert!(cargo.to_bin); + assert!( + cargo + .target + .ends_with("scripts/targets/pie/x86_64-unknown-none.json") + ); + assert!(cargo.features.contains(&"ax-std/plat-dyn".to_string())); + assert!(!cargo.features.contains(&"ax-hal/x86-pc".to_string())); } #[test] @@ -571,7 +581,7 @@ AX_IP = "10.0.2.15" true, None )); - assert!(!build::resolve_effective_plat_dyn( + assert!(build::resolve_effective_plat_dyn( "x86_64-unknown-none", true, Some(true) diff --git a/scripts/axbuild/src/arceos/cbuild.rs b/scripts/axbuild/src/arceos/cbuild.rs index f715146331..e6cb619e67 100644 --- a/scripts/axbuild/src/arceos/cbuild.rs +++ b/scripts/axbuild/src/arceos/cbuild.rs @@ -1,5 +1,5 @@ use std::{ - collections::BTreeSet, + collections::{BTreeSet, HashMap}, fs, path::{Path, PathBuf}, process::{Command, Stdio}, @@ -15,6 +15,7 @@ use crate::{ }; const AX_LIBC_PACKAGE: &str = "ax-libc"; +const PIC_RUSTFLAG: &str = "-Crelocation-model=pic"; #[derive(Debug, Clone)] pub(crate) struct ArceosCBuildInput { @@ -37,8 +38,10 @@ pub(crate) fn build_c_app( ) -> anyhow::Result { let mut cargo = build::load_cargo_config(request)?; cargo.package = AX_LIBC_PACKAGE.to_string(); + cargo.target = request.target.clone(); cargo.to_bin = false; cargo.features = map_c_app_features(&input.features, &cargo.features); + let dynamic_pie = dynamic_pie_for_c_app(&cargo.features); let mode = if request.debug { "debug" } else { "release" }; let arch = request.arch.as_str(); @@ -61,7 +64,13 @@ pub(crate) fn build_c_app( fs::create_dir_all(&input.out_dir) .with_context(|| format!("failed to create {}", input.out_dir.display()))?; - build_axlibc_staticlib(workspace_root, &cargo, &input.target_dir, request.debug)?; + build_axlibc_staticlib( + workspace_root, + &cargo, + &input.target_dir, + request.debug, + dynamic_pie, + )?; write_pthread_mutex_header(&generated_include_dir, &cargo.features)?; let rust_lib = input .target_dir @@ -84,15 +93,16 @@ pub(crate) fn build_c_app( ) .context("failed to locate ArceOS C app linker scripts")?; - let cflags = cflags( + let cflags = cflags(CFlagsInput { workspace_root, arch, mode, - &generated_include_dir, - &include_dir, - &cargo.features, - cargo.log, - ); + generated_include_dir: &generated_include_dir, + include_dir: &include_dir, + features: &cargo.features, + log: cargo.log, + dynamic_pie, + }); let lib_objects = compile_dir_c_sources(&c_source_dir, &axlibc_obj_dir, &cflags, None, "axlibc")?; let app_objects = compile_dir_c_sources(&input.app_dir, &app_obj_dir, &cflags, None, "app")?; @@ -153,8 +163,13 @@ fn build_axlibc_staticlib( cargo: &Cargo, target_dir: &Path, debug: bool, + dynamic_pie: bool, ) -> anyhow::Result<()> { let mut command = Command::new("cargo"); + let mut env = cargo.env.clone(); + if dynamic_pie { + append_pic_rustflag(&mut env); + } command .current_dir(workspace_root) .arg("build") @@ -174,7 +189,7 @@ fn build_axlibc_staticlib( for arg in &cargo.args { command.arg(arg); } - for (key, value) in &cargo.env { + for (key, value) in &env { command.env(key, value); } command @@ -182,6 +197,33 @@ fn build_axlibc_staticlib( .context("failed to build ax-libc static library") } +fn dynamic_pie_for_c_app(features: &[String]) -> bool { + has_feature(features, "plat-dyn") +} + +fn append_pic_rustflag(env: &mut HashMap) { + const ENCODED_RUSTFLAGS: &str = "CARGO_ENCODED_RUSTFLAGS"; + const RUSTFLAGS: &str = "RUSTFLAGS"; + + if let Some(flags) = env.get_mut(ENCODED_RUSTFLAGS) { + if !flags.is_empty() { + flags.push('\x1f'); + } + flags.push_str(PIC_RUSTFLAG); + return; + } + + if let Some(flags) = env.get_mut(RUSTFLAGS) { + if !flags.is_empty() { + flags.push(' '); + } + flags.push_str(PIC_RUSTFLAG); + return; + } + + env.insert(ENCODED_RUSTFLAGS.to_string(), PIC_RUSTFLAG.to_string()); +} + fn find_final_linker_script( target_dir: &Path, target: &str, @@ -339,47 +381,56 @@ fn platform_linker_owner_prefix(platform: &str, features: &[String]) -> &'static } } -fn cflags( - workspace_root: &Path, - arch: &str, - mode: &str, - generated_include_dir: &Path, - include_dir: &Path, - features: &[String], +struct CFlagsInput<'a> { + workspace_root: &'a Path, + arch: &'a str, + mode: &'a str, + generated_include_dir: &'a Path, + include_dir: &'a Path, + features: &'a [String], log: Option, -) -> Vec { + dynamic_pie: bool, +} + +fn cflags(input: CFlagsInput<'_>) -> Vec { let mut flags = vec![ "-nostdinc".to_string(), "-fno-builtin".to_string(), "-ffreestanding".to_string(), "-Wall".to_string(), - format!("-I{}", generated_include_dir.display()), - format!("-I{}", include_dir.display()), + format!("-I{}", input.generated_include_dir.display()), + format!("-I{}", input.include_dir.display()), ]; - for feature in c_config_features(features) { + for feature in c_config_features(input.features) { flags.push(format!("-DAX_CONFIG_{}", c_define_name(&feature))); } flags.push(format!( "-DAX_LOG_{}", - format!("{:?}", log.unwrap_or(LogLevel::Warn)).to_uppercase() + format!("{:?}", input.log.unwrap_or(LogLevel::Warn)).to_uppercase() )); - if mode == "release" { + if input.mode == "release" { flags.push("-O3".to_string()); } - match arch { + if input.dynamic_pie { + flags.push("-fPIE".to_string()); + } + match input.arch { "riscv64" => flags.extend([ "-march=rv64gc".to_string(), "-mabi=lp64d".to_string(), "-mcmodel=medany".to_string(), ]), "loongarch64" => flags.push("-msoft-float".to_string()), - "x86_64" if !has_feature(features, "fp-simd") => flags.push("-mno-sse".to_string()), - "aarch64" if !has_feature(features, "fp-simd") => { + "x86_64" if !has_feature(input.features, "fp-simd") => flags.push("-mno-sse".to_string()), + "aarch64" if !has_feature(input.features, "fp-simd") => { flags.push("-mgeneral-regs-only".to_string()) } _ => {} } - flags.push(format!("-I{}", workspace_root.join("include").display())); + flags.push(format!( + "-I{}", + input.workspace_root.join("include").display() + )); flags } @@ -713,6 +764,43 @@ mod tests { assert!(features.contains(&"smp".to_string())); } + #[test] + fn dynamic_c_apps_use_pie_for_every_dynamic_platform() { + assert!(dynamic_pie_for_c_app(&strings(&["plat-dyn"]))); + assert!(dynamic_pie_for_c_app(&strings(&["ax-std/plat-dyn"]))); + assert!(!dynamic_pie_for_c_app(&strings(&["smp"]))); + } + + #[test] + fn pic_rustflag_is_appended_to_axlibc_cargo_env() { + let mut env = std::collections::HashMap::new(); + append_pic_rustflag(&mut env); + assert_eq!( + env.get("CARGO_ENCODED_RUSTFLAGS"), + Some(&PIC_RUSTFLAG.to_string()) + ); + + let mut env = std::collections::HashMap::from([( + "CARGO_ENCODED_RUSTFLAGS".to_string(), + "-Cforce-frame-pointers=yes".to_string(), + )]); + append_pic_rustflag(&mut env); + assert_eq!( + env.get("CARGO_ENCODED_RUSTFLAGS"), + Some(&format!("-Cforce-frame-pointers=yes\x1f{PIC_RUSTFLAG}")) + ); + + let mut env = std::collections::HashMap::from([( + "RUSTFLAGS".to_string(), + "-Cforce-frame-pointers=yes".to_string(), + )]); + append_pic_rustflag(&mut env); + assert_eq!( + env.get("RUSTFLAGS"), + Some(&format!("-Cforce-frame-pointers=yes {PIC_RUSTFLAG}")) + ); + } + #[test] fn map_c_app_features_forwards_multitask_to_runtime_features() { let features = map_c_app_features(&strings(&["multitask"]), &[]); diff --git a/scripts/axbuild/src/arceos/mod.rs b/scripts/axbuild/src/arceos/mod.rs index dac8691013..49e0babf50 100644 --- a/scripts/axbuild/src/arceos/mod.rs +++ b/scripts/axbuild/src/arceos/mod.rs @@ -236,15 +236,19 @@ impl ArceOS { request: &ResolvedBuildRequest, cargo: &Cargo, ) -> anyhow::Result> { - match request.qemu_config.as_deref() { + let mut qemu = match request.qemu_config.as_deref() { Some(path) => self .app .tool_mut() .read_qemu_config_from_path_for_cargo(cargo, path) .await - .map(Some), - None => Ok(None), + .map(Some)?, + None => None, + }; + if let Some(qemu) = qemu.as_mut() { + crate::test::qemu::apply_dynamic_x86_64_qemu_boot(qemu, cargo); } + Ok(qemu) } async fn load_uboot_config( diff --git a/scripts/axbuild/src/arceos/rootfs.rs b/scripts/axbuild/src/arceos/rootfs.rs index c922dcf70a..9d844fd180 100644 --- a/scripts/axbuild/src/arceos/rootfs.rs +++ b/scripts/axbuild/src/arceos/rootfs.rs @@ -52,6 +52,7 @@ pub(super) async fn qemu_with_explicit_rootfs( .load_qemu_config(&request, &cargo) .await? .unwrap_or_default(); + qemu_test::apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); patch_qemu_rootfs(&mut qemu, &rootfs); qemu_test::apply_smp_qemu_arg(&mut qemu, request.smp); arceos diff --git a/scripts/axbuild/src/arceos/test.rs b/scripts/axbuild/src/arceos/test.rs index 3fe9dc562c..9caf5de788 100644 --- a/scripts/axbuild/src/arceos/test.rs +++ b/scripts/axbuild/src/arceos/test.rs @@ -1174,6 +1174,7 @@ async fn build_and_run_c_test( None, SnapshotPersistence::Discard, )?; + let cargo = build::load_cargo_config(&request)?; let input = cbuild::ArceosCBuildInput { app_dir: source_dir.clone(), app_name: c_test_app_name(&source_dir, &test.name), @@ -1182,7 +1183,8 @@ async fn build_and_run_c_test( features: build_config.build.features.clone(), }; let output = cbuild::build_c_app(&workspace_root, &request, &input)?; - let qemu = qemu_config; + let mut qemu = qemu_config; + qemu_test::apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); ensure_qemu_runtime_assets(arceos.app.workspace_root(), &qemu)?; arceos .app diff --git a/scripts/axbuild/src/build.rs b/scripts/axbuild/src/build.rs index 6aca02bc2d..c2b0299c68 100644 --- a/scripts/axbuild/src/build.rs +++ b/scripts/axbuild/src/build.rs @@ -114,7 +114,7 @@ impl BuildInfo { pub fn default_for_target(target: &str) -> Self { Self { - plat_dyn: supports_platform_dynamic(target), + plat_dyn: defaults_to_platform_dynamic(target), ..Self::default() } } @@ -210,7 +210,7 @@ impl BuildInfo { prepare_std_build_env(&mut cargo.env, target, plat_dyn, metadata)?; pass_std_build_nested_features(&mut cargo.env, &mut cargo.features); cargo.extra_config = Some(std_cargo_config_path(plat_dyn)?.display().to_string()); - cargo.to_bin = false; + cargo.to_bin = plat_dyn && target.starts_with("x86_64-"); return Ok(cargo); } @@ -234,7 +234,7 @@ impl BuildInfo { package.to_string(), cargo_target, args, - default_to_bin_for_target(target), + default_to_bin_for_target_config(target, plat_dyn), )) } @@ -486,7 +486,7 @@ pub(crate) fn cargo_target_json_path(target: &str, plat_dyn: bool) -> anyhow::Re if plat_dyn { if !matches!( target, - "aarch64-unknown-none-softfloat" | "riscv64gc-unknown-none-elf" + "aarch64-unknown-none-softfloat" | "riscv64gc-unknown-none-elf" | "x86_64-unknown-none" ) { bail!("unsupported PIE target `{target}`"); } @@ -737,13 +737,21 @@ pub(crate) fn resolve_effective_plat_dyn( } fn supports_platform_dynamic(target: &str) -> bool { - target.starts_with("aarch64-") || target.starts_with("riscv64") + target.starts_with("aarch64-") || target.starts_with("riscv64") || target.starts_with("x86_64-") +} + +fn defaults_to_platform_dynamic(target: &str) -> bool { + target.starts_with("aarch64-") || target.starts_with("riscv64") || target.starts_with("x86_64-") } fn default_to_bin_for_target(target: &str) -> bool { !target.starts_with("x86_64-") && !target.starts_with("loongarch64-") } +fn default_to_bin_for_target_config(target: &str, plat_dyn: bool) -> bool { + default_to_bin_for_target(target) || (plat_dyn && target.starts_with("x86_64-")) +} + fn normalize_legacy_feature_alias(feature: &str) -> String { if feature == "axstd" { "ax-std".to_string() @@ -1604,6 +1612,7 @@ mod tests { .unwrap(); assert!(cargo.features.contains(&"arceos-rust/plat-dyn".to_string())); + assert!(!cargo.env.contains_key("ARCEOS_RUST_FEATURES")); assert!(!cargo.env.contains_key("ARCEOS_RUST_CONFIG")); let config = std::fs::read_to_string(cargo.extra_config.unwrap()).unwrap(); assert!(config.contains("link-arg=-pie")); @@ -1611,6 +1620,49 @@ mod tests { assert!(!config.contains("link-arg=-Taxplat.x")); } + #[test] + fn std_build_dynamic_x86_64_prepares_binary_artifact() { + let metadata = repo_metadata(); + let cargo = BuildInfo { + std_build: true, + ..BuildInfo::default_for_target("x86_64-unknown-none") + } + .into_prepared_base_cargo_config_with_metadata( + "test-arceos-std-app", + "x86_64-unknown-none", + None, + &metadata, + ) + .unwrap(); + + assert_eq!(cargo.target, "x86_64-unknown-hermit"); + assert!(cargo.to_bin); + assert!(cargo.features.contains(&"arceos-rust/plat-dyn".to_string())); + assert!(!cargo.env.contains_key("ARCEOS_RUST_FEATURES")); + } + + #[test] + fn std_build_plat_dyn_stays_on_arceos_rust_dependency() { + let mut info = BuildInfo { + std_build: true, + features: vec!["ax-feat/plat-dyn".to_string(), "alloc".to_string()], + ..BuildInfo::default() + }; + + info.resolve_std_features(); + let mut envs = HashMap::new(); + pass_std_build_nested_features(&mut envs, &mut info.features); + + assert_eq!( + info.features, + vec![ + "arceos-rust/alloc".to_string(), + "arceos-rust/plat-dyn".to_string() + ] + ); + assert!(!envs.contains_key("ARCEOS_RUST_FEATURES")); + } + #[test] fn cargo_target_json_path_maps_no_pie_targets() { let cases = [ @@ -1640,6 +1692,28 @@ mod tests { assert!(path.ends_with("scripts/targets/pie/riscv64gc-unknown-none-elf.json")); } + #[test] + fn cargo_target_json_path_maps_x86_64_dyn_to_pie_target() { + let path = cargo_target_json_path("x86_64-unknown-none", true).unwrap(); + + assert!(path.ends_with("scripts/targets/pie/x86_64-unknown-none.json")); + } + + #[test] + fn x86_64_defaults_to_dynamic_platform() { + assert!(supports_platform_dynamic("x86_64-unknown-none")); + assert!(BuildInfo::default_for_target("x86_64-unknown-none").plat_dyn); + assert!(resolve_effective_plat_dyn( + "x86_64-unknown-none", + true, + None + )); + assert!(default_to_bin_for_target_config( + "x86_64-unknown-none", + true + )); + } + #[test] fn build_cargo_args_uses_json_target_spec_and_build_std() { let args = BuildInfo::build_cargo_args( @@ -1691,6 +1765,7 @@ mod tests { include_str!("../../targets/no-pie/x86_64-unknown-none.json"), include_str!("../../targets/pie/aarch64-unknown-none-softfloat.json"), include_str!("../../targets/pie/riscv64gc-unknown-none-elf.json"), + include_str!("../../targets/pie/x86_64-unknown-none.json"), ]; for spec in specs { @@ -1716,6 +1791,24 @@ mod tests { assert!(!link_args.iter().any(|arg| arg == "-pie")); } + #[test] + fn x86_64_pie_target_uses_final_linker_script() { + let spec: serde_json::Value = + serde_json::from_str(include_str!("../../targets/pie/x86_64-unknown-none.json")) + .unwrap(); + + assert_eq!(spec["position-independent-executables"], true); + assert_eq!(spec["static-position-independent-executables"], true); + assert_eq!(spec["relocation-model"], "pic"); + + let link_args = spec["pre-link-args"]["gnu-lld"].as_array().unwrap(); + assert!(link_args.iter().any(|arg| arg == "-pie")); + assert!(link_args.iter().any(|arg| arg == "-znostart-stop-gc")); + assert!(link_args.iter().any(|arg| arg == "-Tlinker.x")); + assert!(!link_args.iter().any(|arg| arg == "-Taxplat.x")); + assert!(!link_args.iter().any(|arg| arg == "-no-pie")); + } + #[test] fn detects_axfeat_direct_dependency_via_metadata() { let workspace = temp_workspace("ax-feat-app", "ax-feat = \"0.1.0\"\n").unwrap(); diff --git a/scripts/axbuild/src/starry/build.rs b/scripts/axbuild/src/starry/build.rs index 6819d40963..d9d1adecaf 100644 --- a/scripts/axbuild/src/starry/build.rs +++ b/scripts/axbuild/src/starry/build.rs @@ -483,11 +483,11 @@ mod tests { } #[test] - fn default_x86_starry_build_info_keeps_static_qemu_feature() { + fn default_x86_starry_build_info_uses_dynamic_platform() { let build_info = default_starry_build_info_for_target("x86_64-unknown-none"); - assert!(!build_info.plat_dyn); - assert_eq!(build_info.features, vec!["qemu".to_string()]); + assert!(build_info.plat_dyn); + assert!(build_info.features.is_empty()); } #[test] diff --git a/scripts/axbuild/src/starry/rootfs.rs b/scripts/axbuild/src/starry/rootfs.rs index e4473bc2be..4e3b502b16 100644 --- a/scripts/axbuild/src/starry/rootfs.rs +++ b/scripts/axbuild/src/starry/rootfs.rs @@ -113,6 +113,7 @@ pub(super) async fn load_patched_qemu_config( } else if apply_default_args { patch_qemu_rootfs(&mut qemu, request, starry.app.workspace_root(), None, mode)?; } + qemu_test::apply_dynamic_x86_64_qemu_boot(&mut qemu, cargo); qemu_test::apply_smp_qemu_arg(&mut qemu, request.smp); Ok(qemu) diff --git a/scripts/axbuild/src/starry/test.rs b/scripts/axbuild/src/starry/test.rs index 309555d2d6..67d980e36b 100644 --- a/scripts/axbuild/src/starry/test.rs +++ b/scripts/axbuild/src/starry/test.rs @@ -752,7 +752,7 @@ impl Starry { let mut prepared = Vec::with_capacity(cases.len()); let mut rootfs_paths = BTreeSet::new(); for starry_case in cases { - let qemu = self + let mut qemu = self .app .tool_mut() .read_qemu_config_from_path_for_cargo(cargo, &starry_case.case.qemu_config_path) @@ -763,6 +763,7 @@ impl Starry { starry_case.case.display_name ) })?; + qemu_test::apply_dynamic_x86_64_qemu_boot(&mut qemu, cargo); let rootfs_path = Self::qemu_case_rootfs_path(self.app.workspace_root(), &qemu, default_rootfs_path); rootfs_paths.insert(rootfs_path.clone()); @@ -941,6 +942,7 @@ impl Starry { rootfs::RootfsPatchMode::EnsureDiskBootNet, ); qemu.args.extend(prepared_assets.extra_qemu_args.clone()); + qemu_test::apply_dynamic_x86_64_qemu_boot(&mut qemu, cargo); case::run_qemu_with_prepared_case_assets( &mut self.app, cargo, diff --git a/scripts/axbuild/src/test/qemu.rs b/scripts/axbuild/src/test/qemu.rs index fdf6efaa9e..81dae248f4 100644 --- a/scripts/axbuild/src/test/qemu.rs +++ b/scripts/axbuild/src/test/qemu.rs @@ -13,6 +13,7 @@ use crate::{ test::case::{HostHttpServerConfig, TestQemuCase, TestQemuSubcase, TestQemuSubcaseKind}, }; +const DYNAMIC_X86_64_QEMU_DEBUG_ENV: &str = "AXBUILD_X86_64_DYN_QEMU_DEBUG"; const TIMEOUT_SCALE_ENV: &str = "AXBUILD_TEST_TIMEOUT_SCALE"; #[derive(Debug, Clone, PartialEq, Eq)] @@ -107,13 +108,13 @@ struct IndexedQemuCase { } #[derive(Debug, Deserialize)] -struct QemuCaseExtraConfig { +pub(crate) struct QemuCaseExtraConfig { #[serde(default)] - test_commands: Vec, + pub(crate) test_commands: Vec, #[serde(default)] - host_symbolize_success_regex: Vec, + pub(crate) host_symbolize_success_regex: Vec, #[serde(default)] - host_http_server: Option, + pub(crate) host_http_server: Option, } pub(crate) fn qemu_config_name(arch: &str) -> String { @@ -780,7 +781,9 @@ pub(crate) fn load_test_qemu_case_fields( }) } -fn load_qemu_case_extra_config(qemu_config_path: &Path) -> anyhow::Result { +pub(crate) fn load_qemu_case_extra_config( + qemu_config_path: &Path, +) -> anyhow::Result { let content = fs::read_to_string(qemu_config_path) .with_context(|| format!("failed to read {}", qemu_config_path.display()))?; toml::from_str(&content) @@ -790,11 +793,7 @@ fn load_qemu_case_extra_config(qemu_config_path: &Path) -> anyhow::Result anyhow::Result> { - let content = fs::read_to_string(qemu_config_path) - .with_context(|| format!("failed to read {}", qemu_config_path.display()))?; - let config: QemuCaseExtraConfig = toml::from_str(&content) - .with_context(|| format!("failed to parse {}", qemu_config_path.display()))?; - Ok(config.host_http_server) + Ok(load_qemu_case_extra_config(qemu_config_path)?.host_http_server) } fn discover_qemu_subcases( @@ -906,6 +905,190 @@ pub(crate) fn apply_smp_qemu_arg(qemu: &mut QemuConfig, smp: Option) { QemuArgsMut::new(&mut qemu.args).set_option_value("-smp", cpu_num.to_string()); } +pub(crate) fn apply_dynamic_x86_64_qemu_boot(qemu: &mut QemuConfig, cargo: &Cargo) { + if !cargo_uses_dynamic_x86_64_platform(cargo) { + return; + } + + qemu.uefi = true; + qemu.to_bin = true; + ensure_uefi_drive_bus(qemu); + keep_qemu_default_devices_for_uefi(qemu); + disable_unneeded_default_x86_64_devices(qemu); + disable_dynamic_x86_64_five_level_paging(qemu); + apply_drive_snapshot_without_global_snapshot(qemu); + apply_dynamic_x86_64_qemu_debug_args(qemu); +} + +fn apply_dynamic_x86_64_qemu_debug_args(qemu: &mut QemuConfig) { + let Ok(value) = std::env::var(DYNAMIC_X86_64_QEMU_DEBUG_ENV) else { + return; + }; + if !env_flag_enabled(&value) { + return; + } + + push_unique_arg(&mut qemu.args, "-no-reboot"); + push_unique_arg(&mut qemu.args, "-S"); + push_unique_arg(&mut qemu.args, "-s"); +} + +fn env_flag_enabled(value: &str) -> bool { + !matches!( + value.trim().to_ascii_lowercase().as_str(), + "" | "0" | "false" | "no" | "off" + ) +} + +fn push_unique_arg(args: &mut Vec, arg: &str) { + if !args.iter().any(|existing| existing == arg) { + args.push(arg.to_string()); + } +} + +fn ensure_uefi_drive_bus(qemu: &mut QemuConfig) { + for index in 0..qemu.args.len() { + if qemu.args.get(index).is_some_and(|arg| arg == "-machine") + && let Some(machine) = qemu.args.get_mut(index + 1) + { + remove_machine_option(machine, "sata=off"); + } + } +} + +fn remove_machine_option(machine: &mut String, option: &str) { + let parts = machine + .split(',') + .filter(|part| *part != option) + .collect::>(); + *machine = parts.join(","); +} + +fn keep_qemu_default_devices_for_uefi(qemu: &mut QemuConfig) { + qemu.args.retain(|arg| arg != "-nodefaults"); +} + +fn disable_unneeded_default_x86_64_devices(qemu: &mut QemuConfig) { + let has_network_arg = qemu + .args + .iter() + .any(|arg| matches!(arg.as_str(), "-net" | "-netdev" | "-nic")); + if !has_network_arg { + qemu.args.push("-net".to_string()); + qemu.args.push("none".to_string()); + } + + let has_vga_arg = qemu.args.iter().any(|arg| arg == "-vga"); + if !has_vga_arg { + qemu.args.push("-vga".to_string()); + qemu.args.push("none".to_string()); + } +} + +fn disable_dynamic_x86_64_five_level_paging(qemu: &mut QemuConfig) { + for index in 0..qemu.args.len() { + if qemu.args.get(index).is_some_and(|arg| arg == "-cpu") + && let Some(cpu) = qemu.args.get_mut(index + 1) + { + disable_qemu_cpu_feature(cpu, "la57"); + } + } +} + +fn disable_qemu_cpu_feature(cpu: &mut String, feature: &str) { + let disabled_feature = format!("-{feature}"); + if cpu.split(',').any(|part| part.trim() == disabled_feature) { + return; + } + + if !cpu.is_empty() { + cpu.push(','); + } + cpu.push_str(&disabled_feature); +} + +fn apply_drive_snapshot_without_global_snapshot(qemu: &mut QemuConfig) { + let mut global_snapshot = false; + qemu.args.retain(|arg| { + let keep = arg != "-snapshot"; + if !keep { + global_snapshot = true; + } + keep + }); + if !global_snapshot { + return; + } + + for index in 0..qemu.args.len() { + if qemu.args.get(index).is_some_and(|arg| arg == "-drive") + && let Some(drive) = qemu.args.get_mut(index + 1) + { + ensure_drive_snapshot_on(drive); + } + } +} + +fn ensure_drive_snapshot_on(drive: &mut String) { + let mut replaced = false; + let parts = drive + .split(',') + .map(|part| { + if part.starts_with("snapshot=") { + replaced = true; + "snapshot=on".to_string() + } else { + part.to_string() + } + }) + .collect::>(); + if replaced { + *drive = parts.join(","); + } else { + drive.push_str(",snapshot=on"); + } +} + +pub(crate) fn cargo_uses_dynamic_x86_64_platform(cargo: &Cargo) -> bool { + cargo_target_is_dynamic_x86_64(&cargo.target) + && cargo_dynamic_platform_features(cargo).any(dynamic_platform_feature) +} + +fn cargo_target_is_dynamic_x86_64(target: &str) -> bool { + target.ends_with("x86_64-unknown-none") + || target.ends_with("x86_64-unknown-none.json") + || target.ends_with("x86_64-unknown-hermit") +} + +fn dynamic_platform_feature(feature: &str) -> bool { + matches!( + feature, + "plat-dyn" + | "ax-feat/plat-dyn" + | "ax-std/plat-dyn" + | "ax-hal/plat-dyn" + | "ax-libc/plat-dyn" + | "arceos-rust/plat-dyn" + | "dyn-plat" + | "starry-kernel/plat-dyn" + ) +} + +fn cargo_dynamic_platform_features(cargo: &Cargo) -> impl Iterator { + cargo.features.iter().map(String::as_str).chain( + cargo + .env + .get("ARCEOS_RUST_FEATURES") + .into_iter() + .flat_map(|features| { + features + .split(',') + .map(str::trim) + .filter(|feature| !feature.is_empty()) + }), + ) +} + pub(crate) fn smp_from_qemu_arg(qemu: &QemuConfig) -> Option { let args = QemuArgs::new(&qemu.args); let value = args.option_value("-smp")?; @@ -1204,8 +1387,338 @@ where #[cfg(test)] mod tests { + use std::{ + env, + ffi::{OsStr, OsString}, + sync::{LazyLock, Mutex}, + }; + use super::*; + static ENV_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); + + struct TempEnvVar { + key: &'static str, + original: Option, + } + + impl TempEnvVar { + fn set(key: &'static str, value: impl AsRef) -> Self { + let original = env::var_os(key); + unsafe { + env::set_var(key, value); + } + Self { key, original } + } + + fn unset(key: &'static str) -> Self { + let original = env::var_os(key); + unsafe { + env::remove_var(key); + } + Self { key, original } + } + } + + impl Drop for TempEnvVar { + fn drop(&mut self) { + match self.original.as_ref() { + Some(value) => unsafe { + env::set_var(self.key, value); + }, + None => unsafe { + env::remove_var(self.key); + }, + } + } + } + + #[test] + fn dynamic_x86_64_cargo_uses_uefi_bin_qemu_boot() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["ax-std/plat-dyn".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + uefi: false, + to_bin: false, + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert!(qemu.uefi); + assert!(qemu.to_bin); + } + + #[test] + fn dynamic_x86_64_std_cargo_uses_uefi_bin_qemu_boot() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let env = [( + "ARCEOS_RUST_FEATURES".to_string(), + "arceos-rust/plat-dyn".to_string(), + )] + .into(); + let cargo = Cargo { + target: "x86_64-unknown-hermit".to_string(), + env, + features: Vec::new(), + to_bin: false, + ..Default::default() + }; + let mut qemu = QemuConfig { + uefi: false, + to_bin: false, + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert!(qemu.uefi); + assert!(qemu.to_bin); + } + + #[test] + fn dynamic_x86_64_qemu_boot_converts_global_snapshot_to_drive_snapshots() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["plat-dyn".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec![ + "-nographic".to_string(), + "-snapshot".to_string(), + "-drive".to_string(), + "id=disk0,format=raw,file=rootfs.img".to_string(), + "-smp".to_string(), + "1".to_string(), + ], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-nographic", + "-drive", + "id=disk0,format=raw,file=rootfs.img,snapshot=on", + "-smp", + "1", + "-net", + "none", + "-vga", + "none" + ] + ); + } + + #[test] + fn dynamic_x86_64_qemu_boot_keeps_uefi_drive_bus_available() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["dyn-plat".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec![ + "-machine".to_string(), + "q35,sata=off,smbus=off,i8042=off".to_string(), + ], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-machine", + "q35,smbus=off,i8042=off", + "-net", + "none", + "-vga", + "none" + ] + ); + } + + #[test] + fn dynamic_x86_64_qemu_boot_keeps_default_uefi_disk_bus_available() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["dyn-plat".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec![ + "-nodefaults".to_string(), + "-machine".to_string(), + "q35,sata=off,smbus=off,i8042=off".to_string(), + ], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-machine", + "q35,smbus=off,i8042=off", + "-net", + "none", + "-vga", + "none" + ] + ); + } + + #[test] + fn dynamic_x86_64_qemu_boot_disables_five_level_paging_cpu_feature() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["dyn-plat".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec![ + "-cpu".to_string(), + "host,+x2apic".to_string(), + "-machine".to_string(), + "q35".to_string(), + ], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-cpu", + "host,+x2apic,-la57", + "-machine", + "q35", + "-net", + "none", + "-vga", + "none" + ] + ); + } + + #[test] + fn dynamic_x86_64_qemu_boot_keeps_explicit_network_and_vga_args() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::unset(DYNAMIC_X86_64_QEMU_DEBUG_ENV); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["dyn-plat".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec![ + "-netdev".to_string(), + "user,id=net0".to_string(), + "-device".to_string(), + "virtio-net-pci,netdev=net0".to_string(), + "-vga".to_string(), + "std".to_string(), + ], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-netdev", + "user,id=net0", + "-device", + "virtio-net-pci,netdev=net0", + "-vga", + "std" + ] + ); + } + + #[test] + fn dynamic_x86_64_qemu_boot_can_enable_debug_stub() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::set(DYNAMIC_X86_64_QEMU_DEBUG_ENV, "1"); + let cargo = Cargo { + target: "scripts/targets/pie/x86_64-unknown-none.json".to_string(), + features: vec!["dyn-plat".to_string()], + to_bin: true, + ..Default::default() + }; + let mut qemu = QemuConfig { + args: vec!["-nographic".to_string()], + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert_eq!( + qemu.args, + [ + "-nographic", + "-net", + "none", + "-vga", + "none", + "-no-reboot", + "-S", + "-s" + ] + ); + } + + #[test] + fn static_x86_64_cargo_keeps_existing_qemu_boot() { + let _guard = ENV_LOCK.lock().unwrap(); + let _debug = TempEnvVar::set(DYNAMIC_X86_64_QEMU_DEBUG_ENV, "1"); + let cargo = Cargo { + target: "scripts/targets/no-pie/x86_64-unknown-none.json".to_string(), + features: vec!["ax-hal/x86-pc".to_string()], + to_bin: false, + ..Default::default() + }; + let mut qemu = QemuConfig { + uefi: false, + to_bin: false, + ..Default::default() + }; + + apply_dynamic_x86_64_qemu_boot(&mut qemu, &cargo); + + assert!(!qemu.uefi); + assert!(!qemu.to_bin); + } + #[test] fn qemu_failure_summary_is_aggregated() { let mut summary = QemuTestSummary::default(); diff --git a/scripts/targets/pie/x86_64-unknown-none.json b/scripts/targets/pie/x86_64-unknown-none.json new file mode 100644 index 0000000000..52ba125270 --- /dev/null +++ b/scripts/targets/pie/x86_64-unknown-none.json @@ -0,0 +1,41 @@ +{ + "arch": "x86_64", + "code-model": "kernel", + "cpu": "x86-64", + "crt-objects-fallback": "false", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "disable-redzone": true, + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "llvm-target": "x86_64-unknown-none-elf", + "max-atomic-width": 64, + "metadata": { + "description": "Freestanding/bare-metal x86_64 softfloat", + "host_tools": false, + "std": false, + "tier": 2 + }, + "panic-strategy": "abort", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-lld": [ + "-pie", + "-znostart-stop-gc", + "-Tlinker.x" + ] + }, + "relocation-model": "pic", + "relro-level": "full", + "rustc-abi": "softfloat", + "stack-probes": { + "kind": "inline" + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "kcfi", + "kernel-address" + ], + "target-pointer-width": 64 +} diff --git a/test-suit/arceos/c/httpclient/build-x86_64-unknown-none.toml b/test-suit/arceos/c/httpclient/build-x86_64-unknown-none.toml index 09a79b30c2..7df140236c 100644 --- a/test-suit/arceos/c/httpclient/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/httpclient/build-x86_64-unknown-none.toml @@ -1,6 +1,7 @@ -features = ["alloc", "paging", "net", "ax-driver/plat-static", "ax-driver/virtio-net"] +features = ["alloc", "paging", "net", "ax-driver/virtio-net"] log = "Info" +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/memtest/build-x86_64-unknown-none.toml b/test-suit/arceos/c/memtest/build-x86_64-unknown-none.toml index 052a3ab2c3..4979f8d226 100644 --- a/test-suit/arceos/c/memtest/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/memtest/build-x86_64-unknown-none.toml @@ -1,6 +1,7 @@ features = ["alloc", "paging"] log = "Trace" +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/pthread-pipe/build-x86_64-unknown-none.toml b/test-suit/arceos/c/pthread-pipe/build-x86_64-unknown-none.toml index c5e959da53..3ec3ec7eba 100644 --- a/test-suit/arceos/c/pthread-pipe/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/pthread-pipe/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["alloc", "paging", "multitask", "fd", "pipe"] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/pthread-rr/build-x86_64-unknown-none.toml b/test-suit/arceos/c/pthread-rr/build-x86_64-unknown-none.toml index 4f8436a2b2..2b99b279c0 100644 --- a/test-suit/arceos/c/pthread-rr/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/pthread-rr/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["alloc", "paging", "multitask", "sched-rr"] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/pthread-sleep/build-x86_64-unknown-none.toml b/test-suit/arceos/c/pthread-sleep/build-x86_64-unknown-none.toml index 6e50f34e14..8dea6c5561 100644 --- a/test-suit/arceos/c/pthread-sleep/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/pthread-sleep/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["alloc", "paging", "multitask", "irq"] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/pthread/build-x86_64-unknown-none.toml b/test-suit/arceos/c/pthread/build-x86_64-unknown-none.toml index 83c32765bd..68ac0425f3 100644 --- a/test-suit/arceos/c/pthread/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/pthread/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["alloc", "paging", "multitask"] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/smp1/build-x86_64-unknown-none.toml b/test-suit/arceos/c/smp1/build-x86_64-unknown-none.toml index 3fdeef093e..d987af0063 100644 --- a/test-suit/arceos/c/smp1/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/smp1/build-x86_64-unknown-none.toml @@ -1,6 +1,7 @@ features = [] log = "Info" +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/c/smp4/build-x86_64-unknown-none.toml b/test-suit/arceos/c/smp4/build-x86_64-unknown-none.toml index 6e180c9f60..52f2c108b4 100644 --- a/test-suit/arceos/c/smp4/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/c/smp4/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = [] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/backtrace-raw-badfp/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/backtrace-raw-badfp/build-x86_64-unknown-none.toml index da9c9b1961..ac35224b4b 100644 --- a/test-suit/arceos/rust/backtrace-raw-badfp/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/backtrace-raw-badfp/build-x86_64-unknown-none.toml @@ -2,5 +2,6 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 1 +plat_dyn = true [env] DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-badfp/qemu-x86_64.toml b/test-suit/arceos/rust/backtrace-raw-badfp/qemu-x86_64.toml index 63b3978247..ccb8e8f8d8 100644 --- a/test-suit/arceos/rust/backtrace-raw-badfp/qemu-x86_64.toml +++ b/test-suit/arceos/rust/backtrace-raw-badfp/qemu-x86_64.toml @@ -14,6 +14,6 @@ args = [ uefi = false to_bin = false success_regex = [ - "(?ms)^emitting raw backtrace report \\(corrupted fp\\)\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", + "(?s)emitting raw backtrace report \\(corrupted fp\\)\\.\\.\\..*BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*BACKTRACE_END\\b.*test pass", ] fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none-panic.toml b/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none-panic.toml index 750d0e2917..c9fd16f09c 100644 --- a/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none-panic.toml +++ b/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none-panic.toml @@ -2,5 +2,6 @@ features = ["ax-std", "panic-path"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none.toml index ae9b3e9ce3..a95ec188f1 100644 --- a/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/backtrace-raw-basic/build-x86_64-unknown-none.toml @@ -2,5 +2,6 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-basic/qemu-x86_64.toml b/test-suit/arceos/rust/backtrace-raw-basic/qemu-x86_64.toml index c24fc7b375..e27860b127 100644 --- a/test-suit/arceos/rust/backtrace-raw-basic/qemu-x86_64.toml +++ b/test-suit/arceos/rust/backtrace-raw-basic/qemu-x86_64.toml @@ -14,6 +14,6 @@ args = [ uefi = false to_bin = false success_regex = [ - "(?ms)^emitting raw backtrace report\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", + "(?s)emitting raw backtrace report\\.\\.\\..*BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*BACKTRACE_END\\b.*test pass", ] fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/test-suit/arceos/rust/backtrace-raw-normal/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/backtrace-raw-normal/build-x86_64-unknown-none.toml index da9c9b1961..ac35224b4b 100644 --- a/test-suit/arceos/rust/backtrace-raw-normal/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/backtrace-raw-normal/build-x86_64-unknown-none.toml @@ -2,5 +2,6 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 1 +plat_dyn = true [env] DWARF = "y" diff --git a/test-suit/arceos/rust/backtrace-raw-normal/qemu-x86_64.toml b/test-suit/arceos/rust/backtrace-raw-normal/qemu-x86_64.toml index 55a343d19c..4b52292ada 100644 --- a/test-suit/arceos/rust/backtrace-raw-normal/qemu-x86_64.toml +++ b/test-suit/arceos/rust/backtrace-raw-normal/qemu-x86_64.toml @@ -14,6 +14,6 @@ args = [ uefi = false to_bin = false success_regex = [ - "(?ms)^emitting raw backtrace report \\(normal fp chain\\)\\.\\.\\.$.*^BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*^BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BT\\s+1\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*^BACKTRACE_END\\b.*^test pass\\s*$", + "(?s)emitting raw backtrace report \\(normal fp chain\\)\\.\\.\\..*BACKTRACE_BEGIN\\b.*\\bkind=raw\\b.*BT\\s+0\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*BT\\s+1\\s+ip=0x[0-9a-fA-F]+\\s+fp=0x[0-9a-fA-F]+.*BACKTRACE_END\\b.*test pass", ] fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] diff --git a/test-suit/arceos/rust/backtrace/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/backtrace/build-x86_64-unknown-none.toml index d6e89762ed..a26d3bff41 100644 --- a/test-suit/arceos/rust/backtrace/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/backtrace/build-x86_64-unknown-none.toml @@ -1,5 +1,6 @@ features = ["ax-std", "ax-std/backtrace"] log = "Info" +plat_dyn = true [env] BACKTRACE = "y" diff --git a/test-suit/arceos/rust/display/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/display/build-x86_64-unknown-none.toml index 22ce22029f..22c38f1d21 100644 --- a/test-suit/arceos/rust/display/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/display/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-gpu"] +features = ["ax-std", "ax-driver/virtio-gpu"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/exception/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/exception/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/exception/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/exception/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/fs/shell/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/fs/shell/build-x86_64-unknown-none.toml index 4c89f16752..a4b4e3417c 100644 --- a/test-suit/arceos/rust/fs/shell/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/fs/shell/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-blk"] +features = ["ax-std", "ax-driver/virtio-blk"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/fs/shell/qemu-x86_64.toml b/test-suit/arceos/rust/fs/shell/qemu-x86_64.toml index f7b0fddc29..999aaabc2e 100644 --- a/test-suit/arceos/rust/fs/shell/qemu-x86_64.toml +++ b/test-suit/arceos/rust/fs/shell/qemu-x86_64.toml @@ -21,4 +21,4 @@ shell_prefix = "arceos:" shell_init_cmd = "pwd && echo 'FS shell tests passed!'" success_regex = ["FS shell tests passed!"] fail_regex = ["(?i)\\bpanic(?:ked)?\\b"] -timeout=3 +timeout = 30 diff --git a/test-suit/arceos/rust/memtest/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/memtest/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/memtest/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/memtest/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/net/echoserver/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/net/echoserver/build-x86_64-unknown-none.toml index eff1acc096..9d329a249e 100644 --- a/test-suit/arceos/rust/net/echoserver/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/net/echoserver/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-net"] +features = ["ax-std", "ax-driver/virtio-net"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_IP = "10.0.2.15" AX_GW = "10.0.2.2" diff --git a/test-suit/arceos/rust/net/httpclient/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/net/httpclient/build-x86_64-unknown-none.toml index eff1acc096..9d329a249e 100644 --- a/test-suit/arceos/rust/net/httpclient/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/net/httpclient/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-net"] +features = ["ax-std", "ax-driver/virtio-net"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_IP = "10.0.2.15" AX_GW = "10.0.2.2" diff --git a/test-suit/arceos/rust/net/httpserver/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/net/httpserver/build-x86_64-unknown-none.toml index eff1acc096..9d329a249e 100644 --- a/test-suit/arceos/rust/net/httpserver/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/net/httpserver/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-net"] +features = ["ax-std", "ax-driver/virtio-net"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_IP = "10.0.2.15" AX_GW = "10.0.2.2" diff --git a/test-suit/arceos/rust/net/udpserver/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/net/udpserver/build-x86_64-unknown-none.toml index eff1acc096..9d329a249e 100644 --- a/test-suit/arceos/rust/net/udpserver/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/net/udpserver/build-x86_64-unknown-none.toml @@ -1,7 +1,8 @@ -features = ["ax-std", "ax-hal/x86-pc", "ax-driver/plat-static", "ax-driver/virtio-net"] +features = ["ax-std", "ax-driver/virtio-net"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_IP = "10.0.2.15" AX_GW = "10.0.2.2" diff --git a/test-suit/arceos/rust/task/affinity/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/affinity/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/affinity/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/affinity/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/ipi/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/ipi/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/ipi/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/ipi/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/irq/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/irq/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/irq/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/irq/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/lockdep/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/lockdep/build-x86_64-unknown-none.toml index 6090889bf6..d28d3998df 100644 --- a/test-suit/arceos/rust/task/lockdep/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/lockdep/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/parallel/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/parallel/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/parallel/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/parallel/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/priority/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/priority/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/priority/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/priority/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/sleep/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/sleep/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/sleep/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/sleep/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/stack_guard_page/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/stack_guard_page/build-x86_64-unknown-none.toml index 3058a71944..78796b64c9 100644 --- a/test-suit/arceos/rust/task/stack_guard_page/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/stack_guard_page/build-x86_64-unknown-none.toml @@ -2,4 +2,5 @@ features = ["ax-std"] log = "Info" max_cpu_num = 4 +plat_dyn = true [env] diff --git a/test-suit/arceos/rust/task/tls/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/tls/build-x86_64-unknown-none.toml index f326b766d9..6a081145b3 100644 --- a/test-suit/arceos/rust/task/tls/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/tls/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_IP = "10.0.2.15" AX_GW = "10.0.2.2" diff --git a/test-suit/arceos/rust/task/wait_queue/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/wait_queue/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/wait_queue/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/wait_queue/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/wait_queue_remote_wake/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/wait_queue_remote_wake/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/wait_queue_remote_wake/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/wait_queue_remote_wake/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/rust/task/yield/build-x86_64-unknown-none.toml b/test-suit/arceos/rust/task/yield/build-x86_64-unknown-none.toml index 63f6e5549d..c185532cb4 100644 --- a/test-suit/arceos/rust/task/yield/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/rust/task/yield/build-x86_64-unknown-none.toml @@ -2,6 +2,7 @@ features = ["ax-std"] log = "Warn" max_cpu_num = 4 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/std/qemu-smp1/build-x86_64-unknown-none.toml b/test-suit/arceos/std/qemu-smp1/build-x86_64-unknown-none.toml index d3f1f41315..83fc38460d 100644 --- a/test-suit/arceos/std/qemu-smp1/build-x86_64-unknown-none.toml +++ b/test-suit/arceos/std/qemu-smp1/build-x86_64-unknown-none.toml @@ -1,10 +1,10 @@ std = true features = [ - "ax-hal/x86-pc", ] log = "Info" max_cpu_num = 1 +plat_dyn = true [env] AX_GW = "10.0.2.2" AX_IP = "10.0.2.15" diff --git a/test-suit/arceos/std/qemu-smp1/thread_test/qemu-x86_64.toml b/test-suit/arceos/std/qemu-smp1/thread_test/qemu-x86_64.toml index 435d37ffcc..0f6df13403 100644 --- a/test-suit/arceos/std/qemu-smp1/thread_test/qemu-x86_64.toml +++ b/test-suit/arceos/std/qemu-smp1/thread_test/qemu-x86_64.toml @@ -1,4 +1,4 @@ -args = ["-machine", "q35", "-cpu", "max", "-m", "128M", "-smp", "1", "-nographic", "-device", "virtio-blk-pci,drive=disk0", "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/runtime-assets/arceos/std/qemu-smp1/thread_test/disk.img", "-device", "virtio-net-pci,netdev=net0", "-netdev", "user,id=net0", "-serial", "mon:stdio"] +args = ["-machine", "q35", "-cpu", "max", "-m", "512M", "-smp", "1", "-nographic", "-device", "virtio-blk-pci,drive=disk0", "-drive", "id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/runtime-assets/arceos/std/qemu-smp1/thread_test/disk.img", "-device", "virtio-net-pci,netdev=net0", "-netdev", "user,id=net0", "-serial", "mon:stdio"] uefi = false to_bin = false success_regex = ["=== 所有测试完成 ==="] diff --git a/test-suit/starryos/normal/qemu-smp1/build-x86_64-unknown-none.toml b/test-suit/starryos/normal/qemu-smp1/build-x86_64-unknown-none.toml index 48f79a5df5..22f4bef03f 100644 --- a/test-suit/starryos/normal/qemu-smp1/build-x86_64-unknown-none.toml +++ b/test-suit/starryos/normal/qemu-smp1/build-x86_64-unknown-none.toml @@ -2,13 +2,11 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", + "starry-kernel/input", ] -plat_dyn = false +plat_dyn = true diff --git a/test-suit/starryos/normal/qemu-smp4/build-x86_64-unknown-none.toml b/test-suit/starryos/normal/qemu-smp4/build-x86_64-unknown-none.toml index 415120f3e0..d011f5668f 100644 --- a/test-suit/starryos/normal/qemu-smp4/build-x86_64-unknown-none.toml +++ b/test-suit/starryos/normal/qemu-smp4/build-x86_64-unknown-none.toml @@ -2,14 +2,12 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } log = "Warn" features = [ - "ax-hal/x86-pc", - "qemu", - "ax-driver/plat-static", "ax-driver/virtio-blk", "ax-driver/virtio-net", "ax-driver/virtio-gpu", "ax-driver/virtio-input", "ax-driver/virtio-socket", + "starry-kernel/input", ] -plat_dyn = false +plat_dyn = true max_cpu_num = 4 diff --git a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml index 821fbc5876..9d14d2c671 100644 --- a/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml +++ b/test-suit/starryos/normal/qemu-smp4/test-sched-family/qemu-x86_64.toml @@ -1,9 +1,7 @@ target = "x86_64-unknown-none" env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" } -features = [ - "qemu", -] -plat_dyn = false +features = [] +plat_dyn = true max_cpu_num = 4 log = "Warn" args = [