Repository navigation
Conversation
…vices go The owner ruled on the dependency audit's uefi row: own bindings, in place of the loader track's plan to move to the current uefi release. `bootloader/src/efi/` holds UEFI 2.10's tables, the protocols the loader opens (LoadedImage, DevicePath, SimpleFileSystem and File, PartitionInfo, BlockIo, GraphicsOutput, Rng, PciRootBridgeIo), the console it writes, the status codes and GUIDs, each struct cited to its section and pinned by compile-time size_of/offset_of! asserts that run on both loader targets at every build. A module and not a crate: the loader is its one user, and a crate would add a manifest, a description and a workspace entry for no second reader; a host test cannot link a no_std, no_main UEFI binary, and the asserts check the layout on the targets themselves. The wrappers are safe where the hazard was ours: - a name crosses to firmware only as CStr16, which carries its NUL; - a protocol is opened only through BootServices::get (GET_PROTOCOL) or ::exclusive, whose bound is the Exclusive marker; the opener is private, so the two clippy.toml rules that guarded uefi's openers go with them; - an open protocol closes when its Scoped drops, a handle buffer is freed; - the console and the allocator refuse once exit_boot_services has run, which nulls the table itself: no SIGNAL_EXIT_BOOT_SERVICES callback is registered, so end_this_pass has no event to close; - BlockIo reads OptimalTransferLengthGranularity only at revision 3 and later, which deletes the unsafe cast rootimage.rs needed to reach the revision through uefi's type; - the memory map is taken into &mut [u64], which deletes watchdog.rs's unsafe byte-slice cast. The panic handler says `[PANIC]: <info>` on the console, as uefi-services' did, and powers the machine off; uefi-services' ten-second stall before the power-off, a flat wait, does not come with it. One HARDDRIVE decoder (efi::HardDrive::parse, UEFI 2.10 §10.3.5.1) serves boot_partition, bootnext's protocol path and its NVRAM load options, and boot_disk; bootnext's own byte decoder goes, and a load option's HARDDRIVE node is now held to the spec's 42 bytes. The ESP's \toyos\log.guid is read off LoadedImage's DeviceHandle, the volume firmware loaded the image from (§9.1.1), not through LocateDevicePath. A status prints by its Appendix D name: `(NOT_FOUND)` where the loader wrote `(UEFI Error NOT_FOUND: ())`. That is the one change to the loader's text, and it is on refusal lines alone. The loader track's stage 4 loses its uefi bullet and its uefi-services exit, and the owner's ruling joins its bounds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
One-off differential (not landed), applied, built on both targets and restored by the script below; #!/bin/sh
# The one-off differential: apply, build the loader for both targets, restore.
L=$LOGS
cd <worktree> || exit 2
git apply --check $L/differential.patch || exit 2
git apply $L/differential.patch
CARGO_NET_OFFLINE=true cargo run -- --build-only > $L/differential-x86_64.log 2>&1; X=$?
CARGO_NET_OFFLINE=true cargo run -- --build-only --arch aarch64 > $L/differential-aarch64.log 2>&1; A=$?
git apply -R $L/differential.patch
git checkout -- Cargo.lock
echo "x86_64 EXIT=$X aarch64 EXIT=$A"
git status --porcelain --ignore-submodules=noneControl script: #!/bin/sh
# Negative control of the differential: the PartitionInfo revision this branch
# first wrote (0x0001_0000, where the spec's 0x0001000 is 0x1000), and one GUID
# byte. Each must turn the differential's build red; then restore.
L=$LOGS
cd <worktree> || exit 2
git apply $L/differential.patch || exit 2
sed -i '' 's/ const REVISION: u32 = 0x1000;/ const REVISION: u32 = 0x0001_0000;/' bootloader/src/efi/proto.rs
git diff --stat bootloader/src/efi/proto.rs
CARGO_NET_OFFLINE=true cargo run -- --build-only > $L/differential-control-revision.log 2>&1; R=$?
sed -i '' 's/ const REVISION: u32 = 0x0001_0000;/ const REVISION: u32 = 0x1000;/' bootloader/src/efi/proto.rs
sed -i '' 's/0x8868e871, 0xe4f1, 0x11d3, \[0xbc, 0x22/0x8868e871, 0xe4f1, 0x11d3, [0xbc, 0x23/' bootloader/src/efi/mod.rs
CARGO_NET_OFFLINE=true cargo run -- --build-only > $L/differential-control-guid.log 2>&1; G=$?
sed -i '' 's/0x8868e871, 0xe4f1, 0x11d3, \[0xbc, 0x23/0x8868e871, 0xe4f1, 0x11d3, [0xbc, 0x22/' bootloader/src/efi/mod.rs
git apply -R $L/differential.patch
git checkout -- Cargo.lock
echo "revision mutant EXIT=$R guid mutant EXIT=$G"
git status --porcelain --ignore-submodules=nonediff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml
index 9250af93c..298af099e 100644
--- a/bootloader/Cargo.toml
+++ b/bootloader/Cargo.toml
@@ -39,3 +39,6 @@ toyos-update = { path = "../toyos-update" }
# with: this target is soft-float, and the x86 backend reaches for SSE and
# SHA-NI registers a UEFI application may not assume are its own.
sha2 = { version = "0.10", default-features = false, features = ["force-soft"] }
+# ONE-OFF differential, not landed.
+uefi-raw = "=0.5.0"
+uefi = { version = "=0.26.0", default-features = false }
diff --git a/bootloader/src/efi/boot.rs b/bootloader/src/efi/boot.rs
index 8009db982..a053863f2 100644
--- a/bootloader/src/efi/boot.rs
+++ b/bootloader/src/efi/boot.rs
@@ -400,3 +400,6 @@ impl BootServices {
}
}
+
+#[path = "differential/boot.rs"]
+mod differential;
diff --git a/bootloader/src/efi/differential/boot.rs b/bootloader/src/efi/differential/boot.rs
new file mode 100644
index 000000000..30e2cfd6c
--- /dev/null
+++ b/bootloader/src/efi/differential/boot.rs
@@ -0,0 +1,32 @@
+//! ONE-OFF, NOT LANDED: `efi::boot` held against uefi-raw 0.5.0 and uefi 0.26.0.
+include!("macros.rs");
+use super::*;
+use uefi_raw::table as rt;
+
+same_size!(BootServices, rt::boot::BootServices);
+same_offset!(BootServices, allocate_pages, rt::boot::BootServices, allocate_pages);
+same_offset!(BootServices, free_pages, rt::boot::BootServices, free_pages);
+same_offset!(BootServices, get_memory_map, rt::boot::BootServices, get_memory_map);
+same_offset!(BootServices, allocate_pool, rt::boot::BootServices, allocate_pool);
+same_offset!(BootServices, free_pool, rt::boot::BootServices, free_pool);
+same_offset!(BootServices, exit_boot_services, rt::boot::BootServices, exit_boot_services);
+same_offset!(BootServices, set_watchdog_timer, rt::boot::BootServices, set_watchdog_timer);
+same_offset!(BootServices, open_protocol, rt::boot::BootServices, open_protocol);
+same_offset!(BootServices, close_protocol, rt::boot::BootServices, close_protocol);
+same_offset!(BootServices, locate_handle_buffer, rt::boot::BootServices, locate_handle_buffer);
+same_offset!(BootServices, create_event_ex, rt::boot::BootServices, create_event_ex);
+same_size!(MemoryDescriptor, rt::boot::MemoryDescriptor);
+same_offset!(MemoryDescriptor, ty, rt::boot::MemoryDescriptor, ty);
+same_offset!(MemoryDescriptor, phys_start, rt::boot::MemoryDescriptor, phys_start);
+same_offset!(MemoryDescriptor, virt_start, rt::boot::MemoryDescriptor, virt_start);
+same_offset!(MemoryDescriptor, page_count, rt::boot::MemoryDescriptor, page_count);
+same_offset!(MemoryDescriptor, att, rt::boot::MemoryDescriptor, att);
+const _: () = {
+ assert!(LOADER_DATA == rt::boot::MemoryType::LOADER_DATA.0);
+ assert!(MemoryDescriptor::MMIO == rt::boot::MemoryType::MMIO.0);
+ assert!(MemoryDescriptor::MMIO_PORT_SPACE == rt::boot::MemoryType::MMIO_PORT_SPACE.0);
+ assert!(MemoryDescriptor::WRITE_BACK == rt::boot::MemoryAttribute::WRITE_BACK.bits());
+ assert!(GET_PROTOCOL == uefi::table::boot::OpenProtocolAttributes::GetProtocol as u32);
+ assert!(EXCLUSIVE == uefi::table::boot::OpenProtocolAttributes::Exclusive as u32);
+ assert!(PAGE_SIZE == uefi::table::boot::PAGE_SIZE);
+};
diff --git a/bootloader/src/efi/differential/macros.rs b/bootloader/src/efi/differential/macros.rs
new file mode 100644
index 000000000..053f36f74
--- /dev/null
+++ b/bootloader/src/efi/differential/macros.rs
@@ -0,0 +1,33 @@
+// ONE-OFF, NOT LANDED: the differential's shared macros.
+#[allow(unused_macros)]
+macro_rules! same_size {
+ ($ours:ty, $theirs:ty) => {
+ const _: () = assert!(core::mem::size_of::<$ours>() == core::mem::size_of::<$theirs>());
+ };
+}
+#[allow(unused_macros)]
+macro_rules! same_offset {
+ ($ours:ty, $of:ident, $theirs:ty, $tf:ident) => {
+ const _: () = assert!(core::mem::offset_of!($ours, $of) == core::mem::offset_of!($theirs, $tf));
+ };
+}
+#[allow(dead_code)]
+const fn bytes_eq(a: &[u8], b: &[u8]) -> bool {
+ if a.len() != b.len() {
+ return false;
+ }
+ let mut i = 0;
+ while i < a.len() {
+ if a[i] != b[i] {
+ return false;
+ }
+ i += 1;
+ }
+ true
+}
+#[allow(unused_macros)]
+macro_rules! same_guid {
+ ($ours:expr, $theirs:expr) => {
+ const _: () = assert!(bytes_eq(&$ours.0, &$theirs.to_bytes()));
+ };
+}
diff --git a/bootloader/src/efi/differential/proto.rs b/bootloader/src/efi/differential/proto.rs
new file mode 100644
index 000000000..03f25879d
--- /dev/null
+++ b/bootloader/src/efi/differential/proto.rs
@@ -0,0 +1,103 @@
+//! ONE-OFF, NOT LANDED: `efi::proto` held against uefi-raw 0.5.0 and uefi 0.26.0.
+include!("macros.rs");
+use super::*;
+use uefi_raw::protocol as rp;
+
+// Protocols.
+same_size!(TextOutput, rp::console::SimpleTextOutputProtocol);
+same_offset!(TextOutput, output_string, rp::console::SimpleTextOutputProtocol, output_string);
+same_offset!(TextOutput, clear_screen, rp::console::SimpleTextOutputProtocol, clear_screen);
+
+same_size!(LoadedImage, rp::loaded_image::LoadedImageProtocol);
+same_offset!(LoadedImage, device_handle, rp::loaded_image::LoadedImageProtocol, device_handle);
+same_offset!(LoadedImage, image_base, rp::loaded_image::LoadedImageProtocol, image_base);
+same_offset!(LoadedImage, image_size, rp::loaded_image::LoadedImageProtocol, image_size);
+same_offset!(LoadedImage, image_data_type, rp::loaded_image::LoadedImageProtocol, image_data_type);
+
+same_size!(DevicePath, rp::device_path::DevicePathProtocol);
+same_size!([u8; HardDrive::LEN], uefi::proto::device_path::media::HardDrive);
+
+same_size!(SimpleFileSystem, rp::file_system::SimpleFileSystemProtocol);
+same_offset!(SimpleFileSystem, open_volume, rp::file_system::SimpleFileSystemProtocol, open_volume);
+same_size!(FileProtocol, rp::file_system::FileProtocolV1);
+same_offset!(FileProtocol, open, rp::file_system::FileProtocolV1, open);
+same_offset!(FileProtocol, close, rp::file_system::FileProtocolV1, close);
+same_offset!(FileProtocol, delete, rp::file_system::FileProtocolV1, delete);
+same_offset!(FileProtocol, read, rp::file_system::FileProtocolV1, read);
+same_offset!(FileProtocol, write, rp::file_system::FileProtocolV1, write);
+same_offset!(FileProtocol, set_position, rp::file_system::FileProtocolV1, set_position);
+same_offset!(FileProtocol, get_info, rp::file_system::FileProtocolV1, get_info);
+same_offset!(FileProtocol, flush, rp::file_system::FileProtocolV1, flush);
+same_offset!(RawFileInfo, file_size, rp::file_system::FileInfo, file_size);
+same_offset!(RawFileInfo, attribute, rp::file_system::FileInfo, attribute);
+const _: () = assert!(size_of::<RawFileInfo>() == offset_of!(rp::file_system::FileInfo, file_name));
+
+same_size!(PartitionInfo, uefi::proto::media::partition::PartitionInfo);
+same_offset!(PartitionInfo, revision, uefi::proto::media::partition::PartitionInfo, revision);
+same_offset!(PartitionInfo, kind, uefi::proto::media::partition::PartitionInfo, partition_type);
+
+same_size!(BlockIo, rp::block::BlockIoProtocol);
+same_offset!(BlockIo, revision, rp::block::BlockIoProtocol, revision);
+same_offset!(BlockIo, media, rp::block::BlockIoProtocol, media);
+same_offset!(BlockIo, read_blocks, rp::block::BlockIoProtocol, read_blocks);
+same_offset!(BlockIoMedia, media_id, rp::block::BlockIoMedia, media_id);
+same_offset!(BlockIoMedia, media_present, rp::block::BlockIoMedia, media_present);
+same_offset!(BlockIoMedia, block_size, rp::block::BlockIoMedia, block_size);
+same_offset!(BlockIoMedia, io_align, rp::block::BlockIoMedia, io_align);
+same_offset!(BlockIoMedia, last_block, rp::block::BlockIoMedia, last_block);
+same_size!(BlockIoMediaRevision3, rp::block::BlockIoMedia);
+same_offset!(
+ BlockIoMediaRevision3,
+ optimal_transfer_length_granularity,
+ rp::block::BlockIoMedia,
+ optimal_transfer_length_granularity
+);
+
+same_size!(Gop, rp::console::GraphicsOutputProtocol);
+same_offset!(Gop, mode, rp::console::GraphicsOutputProtocol, mode);
+same_size!(GopMode, rp::console::GraphicsOutputProtocolMode);
+same_offset!(GopMode, info, rp::console::GraphicsOutputProtocolMode, info);
+same_offset!(GopMode, frame_buffer_base, rp::console::GraphicsOutputProtocolMode, frame_buffer_base);
+same_offset!(GopMode, frame_buffer_size, rp::console::GraphicsOutputProtocolMode, frame_buffer_size);
+same_size!(GopModeInfo, rp::console::GraphicsOutputModeInformation);
+same_offset!(GopModeInfo, horizontal_resolution, rp::console::GraphicsOutputModeInformation, horizontal_resolution);
+same_offset!(GopModeInfo, vertical_resolution, rp::console::GraphicsOutputModeInformation, vertical_resolution);
+same_offset!(GopModeInfo, pixel_format, rp::console::GraphicsOutputModeInformation, pixel_format);
+same_offset!(GopModeInfo, pixels_per_scan_line, rp::console::GraphicsOutputModeInformation, pixels_per_scan_line);
+
+same_size!(Rng, rp::rng::RngProtocol);
+same_offset!(Rng, get_rng, rp::rng::RngProtocol, get_rng);
+
+same_guid!(<LoadedImage as super::super::boot::Protocol>::GUID, rp::loaded_image::LoadedImageProtocol::GUID);
+same_guid!(<DevicePath as super::super::boot::Protocol>::GUID, rp::device_path::DevicePathProtocol::GUID);
+same_guid!(<SimpleFileSystem as super::super::boot::Protocol>::GUID, rp::file_system::SimpleFileSystemProtocol::GUID);
+same_guid!(FILE_INFO, rp::file_system::FileInfo::ID);
+same_guid!(<BlockIo as super::super::boot::Protocol>::GUID, rp::block::BlockIoProtocol::GUID);
+same_guid!(<Gop as super::super::boot::Protocol>::GUID, rp::console::GraphicsOutputProtocol::GUID);
+same_guid!(<Rng as super::super::boot::Protocol>::GUID, rp::rng::RngProtocol::GUID);
+same_guid!(<PartitionInfo as super::super::boot::Protocol>::GUID, <uefi::proto::media::partition::PartitionInfo as uefi::Identify>::GUID);
+same_guid!(<PciRootBridgeIo as super::super::boot::Protocol>::GUID, uefi::guid!("2f707ebb-4a1a-11d4-9a38-0090273fc14d"));
+// Constants.
+const _: () = {
+ assert!(Mode::Read.bits() == rp::file_system::FileMode::READ.bits());
+ assert!(Mode::ReadWrite.bits() == rp::file_system::FileMode::READ.union(rp::file_system::FileMode::WRITE).bits());
+ assert!(
+ Mode::CreateReadWrite.bits()
+ == rp::file_system::FileMode::CREATE
+ .union(rp::file_system::FileMode::READ)
+ .union(rp::file_system::FileMode::WRITE)
+ .bits()
+ );
+ assert!(DIRECTORY == rp::file_system::FileAttribute::DIRECTORY.bits());
+ assert!(PixelFormat::RGB.0 == rp::console::GraphicsPixelFormat::PIXEL_RED_GREEN_BLUE_RESERVED_8_BIT_PER_COLOR.0);
+ assert!(PixelFormat::BGR.0 == rp::console::GraphicsPixelFormat::PIXEL_BLUE_GREEN_RED_RESERVED_8_BIT_PER_COLOR.0);
+ assert!(PixelFormat::BIT_MASK.0 == rp::console::GraphicsPixelFormat::PIXEL_BIT_MASK.0);
+ assert!(PixelFormat::BLT_ONLY.0 == rp::console::GraphicsPixelFormat::PIXEL_BLT_ONLY.0);
+ assert!(PartitionInfo::REVISION == uefi::proto::media::partition::PartitionInfoRevision::PROTOCOL_REVISION.0);
+ assert!(PartitionInfo::GPT == uefi::proto::media::partition::PartitionType::GPT.0);
+ assert!(HardDrive::TYPE.0 == uefi::proto::device_path::DeviceType::MEDIA.0);
+ assert!(HardDrive::TYPE.1 == uefi::proto::device_path::DeviceSubType::MEDIA_HARD_DRIVE.0);
+ assert!(HardDrive::GPT == uefi::proto::device_path::media::PartitionFormat::GPT.0);
+ assert!(END_ENTIRE.0 == uefi::proto::device_path::DeviceType::END.0);
+ assert!(END_ENTIRE.1 == uefi::proto::device_path::DeviceSubType::END_ENTIRE.0);
+};
diff --git a/bootloader/src/efi/differential/runtime.rs b/bootloader/src/efi/differential/runtime.rs
new file mode 100644
index 000000000..68fcb0a8f
--- /dev/null
+++ b/bootloader/src/efi/differential/runtime.rs
@@ -0,0 +1,31 @@
+//! ONE-OFF, NOT LANDED: `efi::runtime` held against uefi-raw 0.5.0.
+include!("macros.rs");
+use super::*;
+use uefi_raw::table as rt;
+
+same_size!(RuntimeServices, rt::runtime::RuntimeServices);
+same_offset!(RuntimeServices, get_time, rt::runtime::RuntimeServices, get_time);
+same_offset!(RuntimeServices, get_variable, rt::runtime::RuntimeServices, get_variable);
+same_offset!(RuntimeServices, get_next_variable_name, rt::runtime::RuntimeServices, get_next_variable_name);
+same_offset!(RuntimeServices, set_variable, rt::runtime::RuntimeServices, set_variable);
+same_offset!(RuntimeServices, reset_system, rt::runtime::RuntimeServices, reset_system);
+same_offset!(RuntimeServices, query_variable_info, rt::runtime::RuntimeServices, query_variable_info);
+same_size!(Time, uefi_raw::time::Time);
+same_offset!(Time, year, uefi_raw::time::Time, year);
+same_offset!(Time, month, uefi_raw::time::Time, month);
+same_offset!(Time, day, uefi_raw::time::Time, day);
+same_offset!(Time, hour, uefi_raw::time::Time, hour);
+same_offset!(Time, minute, uefi_raw::time::Time, minute);
+same_offset!(Time, second, uefi_raw::time::Time, second);
+same_offset!(Time, nanosecond, uefi_raw::time::Time, nanosecond);
+same_offset!(Time, time_zone, uefi_raw::time::Time, time_zone);
+same_offset!(Time, daylight, uefi_raw::time::Time, daylight);
+same_guid!(GLOBAL_VARIABLE, rt::runtime::VariableVendor::GLOBAL_VARIABLE.0);
+const _: () = {
+ assert!(ResetType::COLD.0 == rt::runtime::ResetType::COLD.0);
+ assert!(ResetType::WARM.0 == rt::runtime::ResetType::WARM.0);
+ assert!(ResetType::SHUTDOWN.0 == rt::runtime::ResetType::SHUTDOWN.0);
+ assert!(VariableAttributes::NON_VOLATILE == rt::runtime::VariableAttributes::NON_VOLATILE.bits());
+ assert!(VariableAttributes::BOOTSERVICE_ACCESS == rt::runtime::VariableAttributes::BOOTSERVICE_ACCESS.bits());
+ assert!(VariableAttributes::RUNTIME_ACCESS == rt::runtime::VariableAttributes::RUNTIME_ACCESS.bits());
+};
diff --git a/bootloader/src/efi/differential/top.rs b/bootloader/src/efi/differential/top.rs
new file mode 100644
index 000000000..b5e73825d
--- /dev/null
+++ b/bootloader/src/efi/differential/top.rs
@@ -0,0 +1,52 @@
+//! ONE-OFF, NOT LANDED: `efi`'s own tables, GUIDs and status codes held
+//! against uefi-raw 0.5.0 and uefi 0.26.0, at compile time, on the target built.
+include!("macros.rs");
+use super::*;
+use uefi_raw::table as rt;
+
+same_size!(Guid, uefi_raw::Guid);
+same_size!(TableHeader, rt::Header);
+same_offset!(TableHeader, revision, rt::Header, revision);
+same_offset!(TableHeader, header_size, rt::Header, size);
+same_size!(RawSystemTable, rt::system::SystemTable);
+same_offset!(RawSystemTable, firmware_revision, rt::system::SystemTable, firmware_revision);
+same_offset!(RawSystemTable, con_out, rt::system::SystemTable, stdout);
+same_offset!(RawSystemTable, runtime_services, rt::system::SystemTable, runtime_services);
+same_offset!(RawSystemTable, boot_services, rt::system::SystemTable, boot_services);
+same_offset!(RawSystemTable, number_of_table_entries, rt::system::SystemTable, number_of_configuration_table_entries);
+same_offset!(RawSystemTable, configuration_table, rt::system::SystemTable, configuration_table);
+same_size!(ConfigurationTable, rt::configuration::ConfigurationTable);
+same_offset!(ConfigurationTable, guid, rt::configuration::ConfigurationTable, vendor_guid);
+same_offset!(ConfigurationTable, address, rt::configuration::ConfigurationTable, vendor_table);
+same_guid!(ACPI2_GUID, uefi::table::cfg::ACPI2_GUID);
+same_guid!(crate::gcd::DXE_SERVICES_TABLE_GUID, uefi::guid!("05ad34ba-6f02-4214-952e-4da0398e2bb9"));
+same_guid!(crate::floor::VENDOR, uefi::guid!("33be3d4a-30e6-49f5-8050-f169d93a20fb"));
+const _: () = {
+ // Every name the table gives, against the crate's value for that name.
+ let mut i = 0;
+ while i < NAMES.len() {
+ let (code, name) = NAMES[i];
+ assert!(code == their(name), "a status code's name and value disagree with uefi-raw");
+ i += 1;
+ }
+ assert!(Status::SUCCESS.0 == uefi_raw::Status::SUCCESS.0);
+ assert!(Status::BUFFER_TOO_SMALL.0 == uefi_raw::Status::BUFFER_TOO_SMALL.0);
+ assert!(Status::NOT_FOUND.0 == uefi_raw::Status::NOT_FOUND.0);
+ assert!(Status::ABORTED.0 == uefi_raw::Status::ABORTED.0);
+ assert!(Status::NOT_FOUND.0 == toyos_update::floor::NOT_FOUND);
+};
+const fn their(name: &str) -> usize {
+ macro_rules! table {
+ ($($n:ident),*) => {{
+ $(if bytes_eq(name.as_bytes(), stringify!($n).as_bytes()) { return uefi_raw::Status::$n.0; })*
+ panic!("a name uefi-raw has no status for")
+ }};
+ }
+ table!(SUCCESS, WARN_UNKNOWN_GLYPH, WARN_DELETE_FAILURE, WARN_WRITE_FAILURE, WARN_BUFFER_TOO_SMALL,
+ WARN_STALE_DATA, WARN_FILE_SYSTEM, WARN_RESET_REQUIRED, LOAD_ERROR, INVALID_PARAMETER, UNSUPPORTED,
+ BAD_BUFFER_SIZE, BUFFER_TOO_SMALL, NOT_READY, DEVICE_ERROR, WRITE_PROTECTED, OUT_OF_RESOURCES,
+ VOLUME_CORRUPTED, VOLUME_FULL, NO_MEDIA, MEDIA_CHANGED, NOT_FOUND, ACCESS_DENIED, NO_RESPONSE, NO_MAPPING,
+ TIMEOUT, NOT_STARTED, ALREADY_STARTED, ABORTED, ICMP_ERROR, TFTP_ERROR, PROTOCOL_ERROR,
+ INCOMPATIBLE_VERSION, SECURITY_VIOLATION, CRC_ERROR, END_OF_MEDIA, END_OF_FILE, INVALID_LANGUAGE,
+ COMPROMISED_DATA, IP_ADDRESS_CONFLICT, HTTP_ERROR)
+}
diff --git a/bootloader/src/efi/mod.rs b/bootloader/src/efi/mod.rs
index 22e0fcfcc..7660682bf 100644
--- a/bootloader/src/efi/mod.rs
+++ b/bootloader/src/efi/mod.rs
@@ -16,6 +16,8 @@
mod boot;
mod proto;
mod runtime;
+#[path = "differential/top.rs"]
+mod differential;
use core::ffi::c_void;
use core::fmt;
diff --git a/bootloader/src/efi/proto.rs b/bootloader/src/efi/proto.rs
index d60c848a7..5a7f36236 100644
--- a/bootloader/src/efi/proto.rs
+++ b/bootloader/src/efi/proto.rs
@@ -237,7 +237,7 @@ pub enum Mode {
}
impl Mode {
- fn bits(self) -> u64 {
+ const fn bits(self) -> u64 {
const READ: u64 = 0x1;
const WRITE: u64 = 0x2;
const CREATE: u64 = 0x8000_0000_0000_0000;
@@ -659,3 +659,6 @@ impl super::Scoped<'_, PciRootBridgeIo> {
unsafe { (self.configuration)(self.this(), &mut resources) }.ok().map(|()| resources)
}
}
+
+#[path = "differential/proto.rs"]
+mod differential;
diff --git a/bootloader/src/efi/runtime.rs b/bootloader/src/efi/runtime.rs
index 16bca2e34..88dcd32bf 100644
--- a/bootloader/src/efi/runtime.rs
+++ b/bootloader/src/efi/runtime.rs
@@ -173,3 +173,6 @@ impl RuntimeServices {
unsafe { (self.reset_system)(kind, status, 0, ptr::null()) }
}
}
+
+#[path = "differential/runtime.rs"]
+mod differential;
diff --git a/bootloader/src/floor.rs b/bootloader/src/floor.rs
index ba57078db..a0b6ef240 100644
--- a/bootloader/src/floor.rs
+++ b/bootloader/src/floor.rs
@@ -19,7 +19,7 @@ const SCOPE: Scope = Scope::from_word(env!("TOYOS_IMAGE_FLOOR"));
/// The vendor every floor is under: `33BE3D4A-30E6-49F5-8050-F169D93A20FB`,
/// minted for this and used for nothing else.
-const VENDOR: Guid = Guid::new(0x33be3d4a, 0x30e6, 0x49f5, [0x80, 0x50, 0xf1, 0x69, 0xd9, 0x3a, 0x20, 0xfb]);
+pub(crate) const VENDOR: Guid = Guid::new(0x33be3d4a, 0x30e6, 0x49f5, [0x80, 0x50, 0xf1, 0x69, 0xd9, 0x3a, 0x20, 0xfb]);
/// The only attributes the floor is written with.
const ATTRIBUTES: u32 = VariableAttributes::NON_VOLATILE | VariableAttributes::BOOTSERVICE_ACCESS;
diff --git a/bootloader/src/gcd.rs b/bootloader/src/gcd.rs
index e33da4457..808a0c0e7 100644
--- a/bootloader/src/gcd.rs
+++ b/bootloader/src/gcd.rs
@@ -24,7 +24,7 @@ use crate::efi::{Guid, Status, SystemTable};
const HEAD: &str = "GCD:";
/// The configuration table entry carrying [`DxeServices`] (PI 1.8 Vol. 2 §7.1).
-const DXE_SERVICES_TABLE_GUID: Guid =
+pub(crate) const DXE_SERVICES_TABLE_GUID: Guid =
Guid::new(0x05ad34ba, 0x6f02, 0x4214, [0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9]);
/// `EFI_DXE_SERVICES_TABLE_SIGNATURE`: the eight bytes `DXE_SERV`, read as a |
|
Mutation patches (negative controls), applied, run and restored by the script below. #!/bin/sh
# Negative controls, each a checked patch applied, run and restored.
L=$LOGS
cd <worktree> || exit 2
# 1. PartitionInfo read at the type GUID, not the unique one: the log volume is never found.
git apply --check $L/mutation-partition.patch && git apply $L/mutation-partition.patch || exit 2
cargo test --test toyos-build -- screen_loader_clears > $L/mutation-partition.log 2>&1; P=$?
git apply -R $L/mutation-partition.patch
# 2. A boot service dropped from the table: every later function pointer moves.
git apply --check $L/mutation-boot-services.patch && git apply $L/mutation-boot-services.patch || exit 2
cargo run -- --build-only > $L/mutation-boot-services.log 2>&1; B=$?
git apply -R $L/mutation-boot-services.patch
echo "partition mutant EXIT=$P boot-services mutant EXIT=$B"
git status --porcelain --ignore-submodules=nonediff --git a/bootloader/src/efi/proto.rs b/bootloader/src/efi/proto.rs
index d60c848a7..7b0520fa6 100644
--- a/bootloader/src/efi/proto.rs
+++ b/bootloader/src/efi/proto.rs
@@ -405,7 +405,7 @@ impl PartitionInfo {
return None;
}
let record = self.record;
- Some(record[16..32].try_into().expect("sixteen bytes"))
+ Some(record[0..16].try_into().expect("sixteen bytes"))
}
}
diff --git a/bootloader/src/efi/boot.rs b/bootloader/src/efi/boot.rs
index 8009db982..08d206b9d 100644
--- a/bootloader/src/efi/boot.rs
+++ b/bootloader/src/efi/boot.rs
@@ -62,7 +62,6 @@ pub struct BootServices {
unload_image: Unused,
exit_boot_services: unsafe extern "efiapi" fn(image: *mut c_void, key: usize) -> Status,
get_next_monotonic_count: Unused,
- stall: Unused,
set_watchdog_timer:
unsafe extern "efiapi" fn(seconds: usize, code: u64, data_size: usize, data: *const u16) -> Status,
connect_controller: Unused, |
|
T14 at |
…nic after a refused ExitBootServices goes straight to the reset The round-1 review of #815 found the load-option walk in bootnext.rs narrowed from main's rule (SignatureType at byte 41, signature at 24..40, any node at least 42 bytes long) to HardDrive::parse's exact 42 bytes. Those bytes are any OS's to write through runtime variable access, and no test at any tier can see the rule change: QEMU only reaches the "no Boot#### entry" arm. Main's decoder is restored byte for byte; HardDrive::parse stays for device paths firmware built (boot_partition, our_partition), and the loader track's stage 2 still unifies the two under its host tests. UEFI 2.10 §7.4.6 allows only the memory allocation services after a first ExitBootServices call, failed or not. A panic in the retry (fill_memory_map's assert) reached a handler that still wrote through ConOut. EXITING is set before each exit call; print and the panic handler read the console only while it is clear, so the handler goes straight to ResetSystem. The loader track's bound no longer puts the module path in the owner's ruling, and the arm64 issue no longer says the bootloader is the uefi crate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
T14 at
|
…NG on, and stage 2 names both HARDDRIVE decoders it folds Stage 4's handler writes loader.log through the File protocol, which UEFI 2.10 §7.4.6 forbids from the first ExitBootServices call as it forbids the console; as written it would undo the EXITING fix. It now skips loaderlog from EXITING on and goes straight to ResetSystem's shutdown. Stage 2 called toyos_update::entry::partition the one HARDDRIVE rule but named neither of the loader's two decoders going, efi::HardDrive::parse and bootnext::gpt_signature, so it could close with three. Both are named, and the exit gains an rg that finds them today. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C
|
Review of #815 at Net lines ( Round-2 findings
BLOCKERNone. NOTE
LAND |
|
Reposted by the orchestrator with the T14's model and firmware strings masked; the text is otherwise the reviewer's (original comment 6086105246, deleted because GitHub keeps edit history). Review of #815 at Net lines ( BLOCKER
NOTE
Judged, no finding
SEND BACK |
|
Reposted by the orchestrator with the T14's model and firmware strings masked; the text is otherwise the reviewer's (original comment 6086705633, deleted because GitHub keeps edit history). Review of #815 at Net lines ( Round-1 findings
Merge of
|
The loader calls UEFI through its own bindings,
bootloader/src/efi/, anduefi0.26 anduefi-services0.23 leave the tree with the seven crates only they pulled in (uefi-raw,uefi-macros,ptr_metaand its derive,ucs2,bit_field,uguid). This follows the owner's ruling on the dependency audit's uefi row ("Own bindings"), which replaces the loader track's plan to move to the currentuefirelease.Head:
ab9772596, withorigin/mainatc757a86b8merged in. It isa29d650e8plus one commit that edits onlyissues/the-loader-does-only-what-must-precede-the-handover.md(git diff --shortstat a29d650e8 ab9772596: 1 file, +9/−2), so no built byte differs froma29d650e8and every reading ata29d650e8below stands for this head. Net lines:git diff --shortstat origin/main...HEADgives 23 files, +2020/−481. Of that, production is about 1.8k lines ofefi/(the audit estimated 1.5–2.5k), against the 91 lockfile lines and about 27k lines of crate source that leave. No test is added.What changed, and why
descriptionand a workspace entry for a second reader that does not exist. A host test cannot link ano_std,no_mainUEFI binary. So the layouts are checked by compile-timesize_of/offset_of!asserts that run at every build of both loader targets, on the targets themselves. Every struct cites its UEFI 2.10 section:EFI_TIME(§8.3);EFI_FILE_INFO(§13.4–13.5);CStr16, which carries its NUL. Literals are checked at compile time bycstr16!.src/bootlog.rsreads thecstr16!("loader.log")spelling, and it is kept.BootServices::get(GET_PROTOCOL) or::exclusive, which requires theExclusivemarker. The raw opener is private. So the type now does the job of the twoclippy.tomlrules that nameduefi's openers, and both rules go. Those lines name paths that no longer exist, so this is outside the brief's named fence, but it is forced.Scopedcloses the protocol on drop, andHandlesfrees its pool buffer.exit_boot_servicesnulls the table once firmware accepts the exit. After that the console and the allocator refuse. NoSIGNAL_EXIT_BOOT_SERVICEScallback is registered, soend_this_passhas no event left to close.ExitBootServicescall, accepted or not, the console refuses: UEFI 2.10 §7.4.6 allows only the memory allocation services after it. A flag,EXITING, is set before eachexitcall, and bothprintand the panic handler check it. A panic in the retry, such asfill_memory_map's assert, therefore skips the console and goes straight toResetSystem.OptimalTransferLengthGranularityonly at revision 3 or later. The old code read a whole revision 3 media struct on any revision, and needed an unsafe cast to reachrevision. That cast is deleted.&mut [u64], which deleteswatchdog.rs's unsafe byte-slice cast.EXITINGit writes[PANIC]: <info>to the console, asuefi-servicesdid, then callsResetSystem(SHUTDOWN); fromEXITINGon it writes nothing and only resets.uefi-servicesstalled 10 s first; that is a flat wait, and it is not carried over. On metal, a loader panic's line is therefore on the panel for no time before power-off. Stage 4 of the track gives the line toloader.log.main.efi::HardDrive::parsereads nodes of device paths that firmware built:boot_partition,bootnext::our_partitionandboot_disk, the last through its type tag. It takes only a node of the spec's 42 bytes;uefi0.26 did the same.Boot####load option is any writer's bytes, sobootnextkeepsmain's own walk and decoder for it, unchanged:SignatureTypeat byte 41 and the signature at 24..40, for any node of 42 bytes or more. QEMU never reaches the positive arm, and no test at any tier would see a change to that rule. So it does not change here.toyos_update::entry::partition, where its host tests and its §10.3.5.1 oracle can hold them.\toyos\log.guidis read from the SimpleFileSystem onLoadedImage.DeviceHandle(§9.1.1). The old code went throughLocateDevicePath.(NOT_FOUND)where the loader wrote(UEFI Error NOT_FOUND: ()). An unknown status prints in hex.issues/the-loader-does-only-what-must-precede-the-handover.md:uefiupgrade bullet and itsuefi-servicesexit.efi/mod.rsholds today.loaderlogfromEXITINGon and goes straight toResetSystem's shutdown: §7.4.6 forbids the File protocol from the firstExitBootServicescall as it forbids the console, so the stage as written would have undone this change'sEXITINGrule.toyos_update::entry::partition,efi::HardDrive::parseandbootnext::gpt_signature, and its exit gainsrg 'fn gpt_signature|fn parse' bootloader/src/{bootnext.rs,efi/proto.rs}finding nothing; today it findsefi/proto.rs:153andbootnext.rs:175.issues/toyos-runs-on-arm64.mdno longer says the bootloader is theueficrate.Checks (high-risk: the firmware boundary, both architectures)
Every run below is at
a29d650e8, after the merge, except host, which was run again atab9772596. The commit between them edits one issue file and no source.cargo run -- --build-onlycargo run -- --build-only --arch aarch64undocumented_unsafe_blockscargo run -- --clippyab9772596cargo run -- --ci hostcargo test --test toyos-buildAbout the runs:
uptimeload averages were 20.25 before and 28.03 after, on 14 cores. At00e4c6076it was also 41 of 41, at 59.65 → 61.59.virt_*tests in the suite are the AArch64 boots, all green: the loader on AAVMF, withvirt_user_mode's seed fromEFI_RNG_PROTOCOL,virt_no_seed_refused,virt_el2_dropandvirt_early_panic. The x86-64 boots are on OVMF.Oracle 1: the old crate, as a differential. This was a one-off and is not landed; its patch is posted as a comment.
efiagainstuefi-raw0.5.0 anduefi0.26.0, and every status name in the table againstuefi-raw's value for that name.sh differential.shapplies it, builds both targets and restores. x86_64 EXIT=0, aarch64 EXIT=0.PartitionInfo::REVISIONset to0x0001_0000, the build is red, EXIT=101. That is the value this branch first wrote. The spec spells it0x0001000, which is 0x1000, so the differential caught a real slip.ACPI2_GUIDchanged, the build is red, EXIT=101.Oracle 2: OVMF and AAVMF, base against branch.
cargo test --test toyos-build -- screen_loader_clears virt_user_mode --nocaptureran ond6298c83e(EXIT=0) and on00e4c6076(EXIT=0).(UEFI Error NOT_FOUND: ())→(NOT_FOUND)on AArch64's black-box refusal.Boot partition: LBA 2048+69632, the ROOT LBA and length, the GOP mode, stride and framebuffer, the GCD and seed lines, and the kernel size and relocations.Negative controls on the landed checks. Mutation patches are posted as comments.
EXITINGhas no test and no mutation: no QEMU firmware refuses the firstExitBootServices, so a panic between the two calls cannot be provoked. It rests on reading against §7.4.6.stallfromBootServices: the build is red, EXIT=101, onsize_of::<BootServices>() == 24 + 44 * 8.screen_loader_clearsis red, EXIT=1.[PANIC]: panicked at bootloader/src/main.rs:…on the console, then a guest shutdown.Oracle 3: the T14's firmware.
00e4c6076, run by the orchestrator. Five boots, each image's sha256 checked againstrequest.txtbefore it was flashed, and eachtoyos-metal --fat32-checkEXIT=0. The judge gave EXIT=0,[metal] 252 passed, 0 failed, 5 boot(s), forboot:testcases loader_watchdog_arms blackbox_unclaimed_page blackbox_done_chain blackbox_foreign_record boot_deadline_ends_a_wedge.loader.log. So PartitionInfo answered on the T14, and\toyos\log.guidwas read through the SimpleFileSystem onLoadedImage.DeviceHandle.Firmware watchdog: 60 s,Boot partition: LBA 2048+69632 signature [<guid>],ROOT: read into memory at … from LBA 212992+626688, andSeed: 32 bytes from EFI_RNG_PROTOCOL.Boot chain: BootNext=0002, so this loader gets the machine back. This is the same line as main's last readback on that stick.gpt: firmware booted us from partition <guid> at LBA 2048+69632, with nobut firmware saidline. This is the hardware oracle forHardDrive::parse's start and size offsets.a29d650e8, run by the orchestrator; it stands forab9772596, which changes no source. Since00e4c6076,a29d650e8changes the boot path in three places: the restored load-option decoder, which is main's; theEXITINGflag; and the merge ofmain.deadlinewedge,foreignrecord,metalcase,testcases-watchdog,testcases), each image's sha256 checked againstmetal-r2/request.txtbefore its flash (9d4b9f7d…,41545394…,6c57c6b8…,93215087…,0cae5be0…), eachtoyos-metal --fat32-checkEXIT=0.cargo test --test toyos-build -- --metal --metal-readback <metal-r2> boot:testcases loader_watchdog_arms blackbox_unclaimed_page blackbox_done_chain blackbox_foreign_record boot_deadline_ends_a_wedge, EXIT=0,[metal] 252 passed, 0 failed, 5 boot(s).loader.log.Boot chain: BootNext=0002, so this loader gets the machine back: the restored load-option decoder's positive arm, the same line as main's readback on this stick.gpt: firmware booted us from partition <guid> at LBA 2048+69632, with nobut firmware saidline. Every boot reached the kernel, so a successfulExitBootServicesis unaffected byEXITINGon the T14's firmware, and The fork's LLVM gives a loop counter's recurrence only the wrap flags proven for it, a sysroot whose compilers lose a loop's last exit is refused, and the ScalarEvolution issue is closed #790's compiler builds a loader that boots there.testcases'loader.log, normalised (timestamps, hashes, GUIDs, addresses, tick counts and versions masked), against main's (testcasesfrom An installed app sees its own package read-only and its own folder as HOME, and nothing else of /apps or /home #807's boot at0ad87a593, based6298c83e, same stick): 84 lines each, 26 differing on each side, every difference a per-image value — the anti-rollback floor names, kernel size and segment offsets, theroot=id, the black box's arm time and GUID, counter values, the floor's value, and the previous boot's log tail. TheGOP:,GCD:and root-bridge lines are identical after masking. No status-name line differs on this boot, as no refusal was printed.What I am unsure of
🤖 Generated with Claude Code
https://claude.ai/code/session_017cSFvbD35xJ2kGANVdm23C