diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bcce705f..d9ee27e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,13 @@ jobs: include: - TARGET: x86_64-unknown-linux-gnu OS: ubuntu-latest + FEATURES: '' - TARGET: aarch64-unknown-linux-gnu OS: ubuntu-latest + FEATURES: '--no-default-features' - TARGET: i686-unknown-linux-gnu OS: ubuntu-latest + FEATURES: '--no-default-features --features alloc' runs-on: ${{ matrix.OS }} env: @@ -30,7 +33,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' - + - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -39,9 +42,9 @@ jobs: profile: minimal target: ${{ matrix.target }} components: llvm-tools-preview - + - name: Build crates - run: cargo build --target $TARGET + run: cargo build --target $TARGET $FEATURES test: runs-on: ubuntu-latest @@ -84,3 +87,22 @@ jobs: - name: Run clippy (tests) run: cargo clippy --tests + + test-no-alloc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + default: true + profile: minimal + components: llvm-tools-preview + target: x86_64-unknown-none + + - name: Build no alloc test exe + working-directory: tools/no_alloc_check + run: cargo build diff --git a/.gitignore b/.gitignore index 9414c352..4ba2d8dd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tests/*.aml tests/*.lst tests/*.txt dumps/ +tools/no_alloc_check/target diff --git a/Cargo.toml b/Cargo.toml index e429a7fe..1d14a8e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [workspace] members = ["tools/aml_tester", "tools/acpi_dumper", "tools/aml_test_tools", "tools/uacpi_test_adapter"] +# See the note in the no_alloc_check README.md to see why this is excluded. +exclude = ["tools/no_alloc_check"] resolver = "2" [package] diff --git a/tools/no_alloc_check/.cargo/config.toml b/tools/no_alloc_check/.cargo/config.toml new file mode 100644 index 00000000..5f4ec049 --- /dev/null +++ b/tools/no_alloc_check/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = ["x86_64-unknown-none"] diff --git a/tools/no_alloc_check/Cargo.toml b/tools/no_alloc_check/Cargo.toml new file mode 100644 index 00000000..874d1a3c --- /dev/null +++ b/tools/no_alloc_check/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "no_alloc_check" +version = "0.0.0" +edition = "2024" +publish = false + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[dependencies] +acpi = { path = "../..", default-features = false } +pci_types = "0.10.1" diff --git a/tools/no_alloc_check/README.md b/tools/no_alloc_check/README.md new file mode 100644 index 00000000..b039417e --- /dev/null +++ b/tools/no_alloc_check/README.md @@ -0,0 +1,23 @@ +# no_alloc_check test tool + +This executable is used to check that the crate is usable in a no-alloc environment. It was born +out of a comment on [issue 311](https://github.com/rust-osdev/acpi/issues/311) that it didn't work. + +The main `acpi` crate is compiled with no alloc support in one of the workflow builds. This +executable is also built in the workflow as a sanity check that the crate can be *linked to* in a +no-alloc environment. + +This executable should build, but actually attempting to run it will result in an access +violation. To demonstrate that the crate can be used in a no-alloc environment it is enough that it +builds successfully. + +## Acknowledgements + +The code was based on the [example by zulinx86](https://zenn.dev/zulinx86/articles/rust-nostd-101) + +## Exclusion from the workspace + +This crate is excluded from the workspace because it requires the profile setting `panic = abort`. + +We do not want to apply that profile to the whole workspace, but the panic profile setting cannot +be applied to individual crates in a workspace. diff --git a/tools/no_alloc_check/rust-toolchain.toml b/tools/no_alloc_check/rust-toolchain.toml new file mode 100644 index 00000000..5d56faf9 --- /dev/null +++ b/tools/no_alloc_check/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/tools/no_alloc_check/src/main.rs b/tools/no_alloc_check/src/main.rs new file mode 100644 index 00000000..29fd5be8 --- /dev/null +++ b/tools/no_alloc_check/src/main.rs @@ -0,0 +1,51 @@ +//! A simple executable to check that the `acpi` crate can be used in a no-alloc environment. +//! +//! This was born out of a comment in [issue 311](https://github.com/rust-osdev/acpi/issues/311) +//! that it didn't work. +//! +//! This executable should build, but actually attempting to run it will result in an access +//! violation. It is enough that it builds successfully. +//! +//! Heavily adapted from the +//! [example by zulinx86](https://zenn.dev/zulinx86/articles/rust-nostd-101) + +#![no_std] +#![no_main] + +mod null_handler; + +use core::{arch::asm, panic::PanicInfo}; + +use null_handler::NullHandler; + +fn sys_exit(status: i32) -> ! { + unsafe { + asm!( + "syscall", + in("rax") 60, + in("rdi") status, + options(noreturn) + ); + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn _start() -> ! { + use ::acpi::{AcpiTables, sdt::madt::Madt}; + + // Clearly actually executing this will result in an access violation. The point is to check that it compiles. + let tables = unsafe { AcpiTables::from_rsdp(NullHandler {}, 0xd1e as usize) } + .unwrap_or_else(|x| panic!("Failed to parse RSDP table: {:?}", x)); + + let mut _madt = tables.find_table::().expect("Failed to find MADT"); + + _madt.get().entries().for_each(|_entry| {}); + + // Just in case it doesn't die already! + sys_exit(0); +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} diff --git a/tools/no_alloc_check/src/null_handler.rs b/tools/no_alloc_check/src/null_handler.rs new file mode 100644 index 00000000..8c72b967 --- /dev/null +++ b/tools/no_alloc_check/src/null_handler.rs @@ -0,0 +1,93 @@ +//! A [`Handler`] that does nothing useful. +//! +//! This is pretty much a duplicate of [`aml_test_utils::NullHandler`]. We don't use that version +//! because `aml_test_tools` is not a `no_std` crate, which we need for this executable. + +use acpi::{Handler, PhysicalMapping}; +use pci_types::PciAddress; + +#[derive(Clone)] +pub struct NullHandler; + +/// A [`Handler`] that does nothing. If required, it will generally return zero. +/// +/// This is useful as a placeholder if you really don't care what values the core [`acpi`] parser +/// receives. +/// +/// The handler is not ever called in this crate, but it must be present for compilation purposes. +impl Handler for NullHandler { + unsafe fn map_physical_region(&self, _physical_address: usize, _size: usize) -> PhysicalMapping { + // This isn't implemented in `aml_tester` either + todo!() + } + + fn unmap_physical_region(_region: &PhysicalMapping) {} + + fn read_u8(&self, _address: usize) -> u8 { + 0 + } + + fn read_u16(&self, _address: usize) -> u16 { + 0 + } + + fn read_u32(&self, _address: usize) -> u32 { + 0 + } + + fn read_u64(&self, _address: usize) -> u64 { + 0 + } + + fn write_u8(&self, _address: usize, _value: u8) {} + + fn write_u16(&self, _address: usize, _value: u16) {} + + fn write_u32(&self, _address: usize, _value: u32) {} + + fn write_u64(&self, _address: usize, _value: u64) {} + + fn read_io_u8(&self, _port: u16) -> u8 { + 0 + } + + fn read_io_u16(&self, _port: u16) -> u16 { + 0 + } + + fn read_io_u32(&self, _port: u16) -> u32 { + 0 + } + + fn write_io_u8(&self, _port: u16, _value: u8) {} + + fn write_io_u16(&self, _port: u16, _value: u16) {} + + fn write_io_u32(&self, _port: u16, _value: u32) {} + + fn read_pci_u8(&self, _address: PciAddress, _offset: u16) -> u8 { + 0 + } + + fn read_pci_u16(&self, _address: PciAddress, _offset: u16) -> u16 { + 0 + } + + fn read_pci_u32(&self, _address: PciAddress, _offset: u16) -> u32 { + 0 + } + + fn write_pci_u8(&self, _address: PciAddress, _offset: u16, _value: u8) {} + + fn write_pci_u16(&self, _address: PciAddress, _offset: u16, _value: u16) {} + + fn write_pci_u32(&self, _address: PciAddress, _offset: u16, _value: u32) {} + + fn nanos_since_boot(&self) -> u64 { + 1000 + } + + fn stall(&self, _microseconds: u64) {} + + fn sleep(&self, _milliseconds: u64) {} +}