diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad81f23..ca6425a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,17 @@ on: workflow_dispatch: jobs: + ci: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: CI (fmt-check + clippy + test) + run: make ci + build: + name: build-${{ matrix.arch }} + needs: ci runs-on: ubuntu-latest strategy: matrix: diff --git a/Makefile b/Makefile index a3e151a..7ad2905 100644 --- a/Makefile +++ b/Makefile @@ -51,6 +51,30 @@ sign-bin: docker-build-base sign-test: docker-build-base $(DOCKER_RUN) $(DOCKER_SAMEUSER) $(BUILD_IMAGE) cargo test --manifest-path crates/stage0-sign/Cargo.toml --target x86_64-unknown-linux-musl +# ---- CI gate: fmt + clippy + unit tests across all crates (what the branch ruleset requires) ---- +# stage0 is not one cargo workspace: 4 independent crates in two target families. The EFI crates +# (ena, stage0, stage0-test-payload) are no_std UEFI and not host-testable -> fmt + clippy on the +# UEFI target; the host signer (stage0-sign) also runs its unit tests (incl. the golden vector). +UEFI_CRATES := ena stage0 stage0-test-payload +HOST_CRATES := stage0-sign +.PHONY: ci fmt-fix +fmt-fix: docker-build-base ## Apply rustfmt across all crates (no --check) + $(DOCKER_RUN) $(DOCKER_SAMEUSER) $(BUILD_IMAGE) bash -c '\ + for c in $(UEFI_CRATES) $(HOST_CRATES); do cargo fmt --manifest-path crates/$$c/Cargo.toml; done' +# ena.efi is include_bytes!'d by the stage0 crate, so it must exist before clippy/check can +# compile it (a clean CI checkout has no build/ artifacts; a warm local tree hid this). +ci: docker-build-base build/x86_64/ena.efi + $(DOCKER_RUN) $(DOCKER_SAMEUSER) $(BUILD_IMAGE) bash -c '\ + set -e; rustup target add x86_64-unknown-uefi x86_64-unknown-linux-musl; \ + for c in $(UEFI_CRATES) $(HOST_CRATES); do \ + echo ">> fmt-check $$c"; cargo fmt --manifest-path crates/$$c/Cargo.toml --check; \ + done; \ + for c in $(UEFI_CRATES); do \ + echo ">> clippy $$c (uefi)"; cargo clippy --manifest-path crates/$$c/Cargo.toml --target x86_64-unknown-uefi -- -D warnings; \ + done; \ + echo ">> clippy stage0-sign (host)"; cargo clippy --manifest-path crates/stage0-sign/Cargo.toml --target x86_64-unknown-linux-musl --all-targets -- -D warnings; \ + echo ">> test stage0-sign (host)"; cargo test --manifest-path crates/stage0-sign/Cargo.toml --target x86_64-unknown-linux-musl' + # ---- ed25519 release key for "signed mode" payload admission ---- # Vendor key; stage0 only ever sees the public half, pinned in the metadata doc. Generated by # stage0-sign keygen (the domain-separated signer the verifier admits against). diff --git a/crates/ena/src/binding.rs b/crates/ena/src/binding.rs index c06a9ae..511657f 100644 --- a/crates/ena/src/binding.rs +++ b/crates/ena/src/binding.rs @@ -49,7 +49,9 @@ unsafe extern "efiapi" fn supported( agent: boot::image_handle(), controller: None, }; - let pci = match unsafe { boot::open_protocol::(params, boot::OpenProtocolAttributes::GetProtocol) } { + let pci = match unsafe { + boot::open_protocol::(params, boot::OpenProtocolAttributes::GetProtocol) + } { Ok(p) => p, Err(_) => return Status::UNSUPPORTED, }; @@ -76,14 +78,19 @@ unsafe extern "efiapi" fn start( agent: boot::image_handle(), controller: Some(controller), }; - let pci = match unsafe { boot::open_protocol::(params, boot::OpenProtocolAttributes::ByDriver) } { + let pci = match unsafe { + boot::open_protocol::(params, boot::OpenProtocolAttributes::ByDriver) + } { Ok(p) => p, Err(e) => { // ALREADY_STARTED is expected: connect_all_controllers reaches ENA twice (via // the parent bus and directly), so start() is invoked a second time once we // already manage the device. That is benign; only log genuine failures. if e.status() != Status::ALREADY_STARTED { - uefi::println!("ena: start(): open PciIo BY_DRIVER failed: {:?}", e.status()); + uefi::println!( + "ena: start(): open PciIo BY_DRIVER failed: {:?}", + e.status() + ); } return e.status(); } @@ -100,7 +107,13 @@ unsafe extern "efiapi" fn start( let mac = ena.mac(); uefi::println!( "ena: probe OK mac={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} mtu={}", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], ena.mtu + mac[0], + mac[1], + mac[2], + mac[3], + mac[4], + mac[5], + ena.mtu ); // Keep PciIo open for the device's lifetime (probe stored the raw pointer in `ena`). core::mem::forget(pci); diff --git a/crates/ena/src/device.rs b/crates/ena/src/device.rs index 2837fb8..f4217c1 100644 --- a/crates/ena/src/device.rs +++ b/crates/ena/src/device.rs @@ -26,7 +26,7 @@ use core::sync::atomic::{fence, Ordering}; use uefi::boot; use uefi::Status; -use crate::pci::{PciAttrOp, PciMapOp, PciWidth, PciIoProtocol, PCI_ATTR_BUS_MASTER}; +use crate::pci::{PciAttrOp, PciIoProtocol, PciMapOp, PciWidth, PCI_ATTR_BUS_MASTER}; /// `uefi::println!` with an `ena:` prefix; no alloc, writes straight to the console. macro_rules! log { @@ -74,7 +74,7 @@ const DEV_CTL_RESET: u32 = 1 << 0; // DEV_STS bits (ena_regs DEV_STS layout). RESET_IN_PROGRESS is bit 3 (0x8), NOT bit 1. const DEV_STS_READY: u32 = 1 << 0; // 0x01 const DEV_STS_RESET_IN_PROGRESS: u32 = 1 << 3; // 0x08 -// CAPS fields: reset timeout in 100ms units at bits [6:1]. + // CAPS fields: reset timeout in 100ms units at bits [6:1]. const CAPS_RESET_TIMEOUT_SHIFT: u32 = 1; const CAPS_RESET_TIMEOUT_MASK: u32 = 0x3e; @@ -103,7 +103,7 @@ const DEPTH_MAX: u16 = 1024; // sanity ceiling on the device-reported max depth const SQ_DESC_SIZE: usize = 16; // ena_eth_io_{tx,rx}_desc are 16 bytes const RX_CDESC_WORDS: u8 = 4; // ena_eth_io_rx_cdesc_base = 16 bytes const TX_CDESC_WORDS: u8 = 2; // ena_eth_io_tx_cdesc = 8 bytes -// CREATE_SQ sq_identity.sq_direction [6:5]. + // CREATE_SQ sq_identity.sq_direction [6:5]. const SQ_DIR_TX: u8 = 1 << 5; const SQ_DIR_RX: u8 = 2 << 5; // CREATE_SQ caps: placement_policy HOST (1) in [3:0]; is_physically_contiguous bit0. @@ -146,7 +146,7 @@ const ADMIN_DEPTH: u16 = 16; const ADMIN_ENTRY_SIZE: u32 = 64; const AENQ_DEPTH: u16 = 2; // ENA_AENQ_COUNT const AENQ_ENTRY_SIZE: u32 = 64; // ena_admin_aenq_entry is 64 bytes (was wrongly 16) -// AQ/ACQ caps register layout: depth in [15:0], entry-size in [31:16]. + // AQ/ACQ caps register layout: depth in [15:0], entry-size in [31:16]. fn caps_word(depth: u16, entry_size: u32) -> u32 { (depth as u32) | (entry_size << 16) } @@ -187,7 +187,14 @@ unsafe fn enable_msix(pci: &PciIoProtocol) { unsafe fn reg_read32(pci: &PciIoProtocol, off: u64) -> u32 { let mut v: u32 = 0; - let st = (pci.mem.read)(pci, PciWidth::U32, ENA_REG_BAR, off, 1, (&mut v as *mut u32).cast()); + let st = (pci.mem.read)( + pci, + PciWidth::U32, + ENA_REG_BAR, + off, + 1, + (&mut v as *mut u32).cast(), + ); if !st.is_success() { log!(" MMIO read @0x{off:02x} failed: {st:?}"); } @@ -196,7 +203,14 @@ unsafe fn reg_read32(pci: &PciIoProtocol, off: u64) -> u32 { unsafe fn reg_write32(pci: &PciIoProtocol, off: u64, val: u32) { let mut v = val; - let st = (pci.mem.write)(pci, PciWidth::U32, ENA_REG_BAR, off, 1, (&mut v as *mut u32).cast()); + let st = (pci.mem.write)( + pci, + PciWidth::U32, + ENA_REG_BAR, + off, + 1, + (&mut v as *mut u32).cast(), + ); if !st.is_success() { log!(" MMIO write @0x{off:02x} failed: {st:?}"); } @@ -232,7 +246,11 @@ impl Dma { ); if !st.is_success() || host.is_null() { log!(" AllocateBuffer({pages} pages) failed: {st:?}"); - return Err(if st.is_success() { Status::OUT_OF_RESOURCES } else { st }); + return Err(if st.is_success() { + Status::OUT_OF_RESOURCES + } else { + st + }); } core::ptr::write_bytes(host as *mut u8, 0, pages * PAGE); @@ -241,7 +259,7 @@ impl Dma { let mut mapping: *mut c_void = core::ptr::null_mut(); let st = (pci.map)( pci, - PciMapOp::BusMasterCommonBuffer, + PciMapOp::CommonBuffer, host as *const c_void, &mut nbytes, &mut dev, @@ -252,7 +270,12 @@ impl Dma { let _ = (pci.free_buffer)(pci, pages, host); return Err(st); } - Ok(Dma { host: host as *mut u8, dev, pages, mapping }) + Ok(Dma { + host: host as *mut u8, + dev, + pages, + mapping, + }) } } @@ -274,7 +297,12 @@ impl AdminQueue { let sq = Dma::alloc(pci, ADMIN_DEPTH as usize * ADMIN_ENTRY_SIZE as usize)?; let cq = Dma::alloc(pci, ADMIN_DEPTH as usize * ADMIN_ENTRY_SIZE as usize)?; let aenq = Dma::alloc(pci, AENQ_DEPTH as usize * AENQ_ENTRY_SIZE as usize)?; - log!(" admin DMA: sq dev=0x{:x} cq dev=0x{:x} aenq dev=0x{:x}", sq.dev, cq.dev, aenq.dev); + log!( + " admin DMA: sq dev=0x{:x} cq dev=0x{:x} aenq dev=0x{:x}", + sq.dev, + cq.dev, + aenq.dev + ); // Exact ena_com_admin_init order: AQ base, ACQ base, AQ caps, ACQ caps, THEN the // AENQ (base, caps, head doorbell). The openbsd-ena writeup found the device only @@ -285,7 +313,11 @@ impl AdminQueue { reg_write32(pci, regs::ACQ_BASE_HI, (cq.dev >> 32) as u32); fence(Ordering::SeqCst); reg_write32(pci, regs::AQ_CAPS, caps_word(ADMIN_DEPTH, ADMIN_ENTRY_SIZE)); - reg_write32(pci, regs::ACQ_CAPS, caps_word(ADMIN_DEPTH, ADMIN_ENTRY_SIZE)); + reg_write32( + pci, + regs::ACQ_CAPS, + caps_word(ADMIN_DEPTH, ADMIN_ENTRY_SIZE), + ); fence(Ordering::SeqCst); // AENQ: base, then caps, then head doorbell (q_depth = all slots initially free). reg_write32(pci, regs::AENQ_BASE_LO, aenq.dev as u32); @@ -328,7 +360,7 @@ impl AdminQueue { // Advance tail (free-running, wraps the ring; phase flips on wrap). self.sq_tail = self.sq_tail.wrapping_add(1); - if self.sq_tail % ADMIN_DEPTH == 0 { + if self.sq_tail.is_multiple_of(ADMIN_DEPTH) { self.sq_phase ^= 1; } reg_write32(pci, regs::AQ_DB, self.sq_tail as u32); @@ -346,7 +378,11 @@ impl AdminQueue { break; } if waited >= deadline_ticks { - log!(" !! admin completion timeout (cq_head={} phase={})", self.cq_head, self.cq_phase); + log!( + " !! admin completion timeout (cq_head={} phase={})", + self.cq_head, + self.cq_phase + ); return Err(Status::TIMEOUT); } delay_ms(1); @@ -362,13 +398,16 @@ impl AdminQueue { // Advance completion head; flip expected phase on wrap. self.cq_head = self.cq_head.wrapping_add(1); - if self.cq_head % ADMIN_DEPTH == 0 { + if self.cq_head.is_multiple_of(ADMIN_DEPTH) { self.cq_phase ^= 1; } if status != 0 { let ext = (out[5] as u16) << 8 | out[4] as u16; - log!(" !! admin cmd opcode={} failed: status={status} ext=0x{ext:04x}", (*cmd)[2]); + log!( + " !! admin cmd opcode={} failed: status={status} ext=0x{ext:04x}", + (*cmd)[2] + ); return Err(Status::DEVICE_ERROR); } Ok(out) @@ -401,7 +440,10 @@ unsafe fn create_cq( msix_vector: u32, ) -> Result { let dma = Dma::alloc(pci, depth as usize * entry_words as usize * 4)?; - log!(" create_cq: dev=0x{:x} words={entry_words} depth={depth} vec={msix_vector}", dma.dev); + log!( + " create_cq: dev=0x{:x} words={entry_words} depth={depth} vec={msix_vector}", + dma.dev + ); let mut cmd = [0u8; 64]; cmd[2] = OP_CREATE_CQ; cmd[4] = CQ_CAPS_1_POLL_MODE; // cq_caps_1: poll mode, no interrupt (see const) @@ -480,7 +522,7 @@ unsafe fn set_host_attributes(pci: &PciIoProtocol, admin: &mut AdminQueue) -> Re cmd[17] = FEAT_HOST_ATTRIBUTES; write_mem_addr(&mut cmd, 20, info.dev); // os_info_ba admin.submit(pci, &cmd)?; - core::mem::forget(info); // keep the page mapped for the device's lifetime + // Dma has no destructor, so `info` pages stay mapped for the device lifetime (intentional leak). Ok(()) } @@ -492,7 +534,7 @@ pub struct Ena { pci: *const PciIoProtocol, mac: [u8; 6], pub mtu: u32, - depth: u16, // IO ring depth, queried from the device (see DEPTH_MIN/MAX) + depth: u16, // IO ring depth, queried from the device (see DEPTH_MIN/MAX) _admin: AdminQueue, // kept so its DMA isn't freed rx_cq: IoQueue, rx_sq: IoQueue, @@ -528,7 +570,8 @@ impl Ena { let dev = self.rx_bufs[buf_idx].dev; wr_u16(d, 0, BUF_SIZE as u16); // length d.add(2).write_volatile(0); - d.add(3).write_volatile(RX_DESC_CTRL | (self.rx_sq_phase & 1)); // ctrl + d.add(3) + .write_volatile(RX_DESC_CTRL | (self.rx_sq_phase & 1)); // ctrl wr_u16(d, 4, buf_idx as u16); // req_id wr_u16(d, 6, 0); wr_u32(d, 8, dev as u32); // buff_addr_lo @@ -536,7 +579,7 @@ impl Ena { wr_u16(d, 14, 0); fence(Ordering::SeqCst); self.rx_sq_tail = self.rx_sq_tail.wrapping_add(1); - if self.rx_sq_tail % self.depth == 0 { + if self.rx_sq_tail.is_multiple_of(self.depth) { self.rx_sq_phase ^= 1; } self.db(self.rx_sq.db_off, self.rx_sq_tail as u32); @@ -554,7 +597,11 @@ impl Ena { /// RX completion ring is empty. Refills the consumed buffer. pub unsafe fn receive(&mut self, out: *mut u8, cap: usize) -> Option { let slot = (self.rx_cq_head % self.depth) as usize; - let c = self.rx_cq.dma.host.add(slot * (RX_CDESC_WORDS as usize * 4)); + let c = self + .rx_cq + .dma + .host + .add(slot * (RX_CDESC_WORDS as usize * 4)); if (rd_u8(c, 3) & 1) != (self.rx_cq_phase & 1) { return None; // status bit24 (byte3 bit0) = phase: not yet written } @@ -566,7 +613,7 @@ impl Ena { core::ptr::copy_nonoverlapping(self.rx_bufs[req_id].host, out, n); } self.rx_cq_head = self.rx_cq_head.wrapping_add(1); - if self.rx_cq_head % self.depth == 0 { + if self.rx_cq_head.is_multiple_of(self.depth) { self.rx_cq_phase ^= 1; } if req_id < self.rx_bufs.len() { @@ -577,7 +624,12 @@ impl Ena { /// Queue `len` bytes from `data` for transmit (copied into a bounce buffer). /// `caller` is handed back by `poll_tx` once the hardware completes it. - pub unsafe fn transmit(&mut self, data: *const u8, len: usize, caller: *const c_void) -> Result<(), Status> { + pub unsafe fn transmit( + &mut self, + data: *const u8, + len: usize, + caller: *const c_void, + ) -> Result<(), Status> { // Limited by the bounce-buffer count, not the ring depth (buffers are decoupled). if self.tx_sq_tail.wrapping_sub(self.tx_cq_head) >= TX_FILL { return Err(Status::NOT_READY); // caller must GetStatus to drain completions @@ -596,7 +648,7 @@ impl Ena { fence(Ordering::SeqCst); self.tx_caller[bslot] = caller; self.tx_sq_tail = self.tx_sq_tail.wrapping_add(1); - if self.tx_sq_tail % self.depth == 0 { + if self.tx_sq_tail.is_multiple_of(self.depth) { self.tx_sq_phase ^= 1; } self.db(self.tx_sq.db_off, self.tx_sq_tail as u32); @@ -607,13 +659,17 @@ impl Ena { /// submission order on a single SQ, so the completion slot matches the submit slot. pub unsafe fn poll_tx(&mut self) -> Option<*const c_void> { let slot = (self.tx_cq_head % self.depth) as usize; - let c = self.tx_cq.dma.host.add(slot * (TX_CDESC_WORDS as usize * 4)); + let c = self + .tx_cq + .dma + .host + .add(slot * (TX_CDESC_WORDS as usize * 4)); if (rd_u8(c, 3) & 1) != (self.tx_cq_phase & 1) { return None; // tx cdesc flags.phase (byte3 bit0) } let caller = self.tx_caller[(self.tx_cq_head % TX_FILL) as usize]; self.tx_cq_head = self.tx_cq_head.wrapping_add(1); - if self.tx_cq_head % self.depth == 0 { + if self.tx_cq_head.is_multiple_of(self.depth) { self.tx_cq_phase ^= 1; } Some(caller) @@ -628,10 +684,19 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { // Record which ENA variant we bound (PCI config dword 0 = vendor | device<<16). let mut vd: u32 = 0; let _ = (pci.pci.read)(pci, PciWidth::U32, 0, 1, (&mut vd as *mut u32).cast()); - log!("PCI vendor=0x{:04x} device=0x{:04x}", vd as u16, (vd >> 16) as u16); + log!( + "PCI vendor=0x{:04x} device=0x{:04x}", + vd as u16, + (vd >> 16) as u16 + ); // Enable bus mastering before any DMA. - let st = (pci.attributes)(pci, PciAttrOp::Enable, PCI_ATTR_BUS_MASTER, core::ptr::null_mut()); + let st = (pci.attributes)( + pci, + PciAttrOp::Enable, + PCI_ATTR_BUS_MASTER, + core::ptr::null_mut(), + ); log!("bus-master enable: {st:?}"); // Leave the function's MSI-X capability enabled (we still poll; CQ vector = NONE). @@ -650,7 +715,7 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { reg_write32(pci, regs::MMIO_RESP_LO, resp.dev as u32); reg_write32(pci, regs::MMIO_RESP_HI, (resp.dev >> 32) as u32); fence(Ordering::SeqCst); - core::mem::forget(resp); // keep mapped for the device's lifetime + // Dma has no destructor, so `resp` stays mapped for the device lifetime (intentional leak). let mut admin = AdminQueue::create(pci)?; @@ -672,8 +737,15 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { let mut mac = [0u8; 6]; mac.copy_from_slice(&resp[32..38]); let mtu = u32::from_le_bytes([resp[40], resp[41], resp[42], resp[43]]); - log!("DEVICE_ATTRIBUTES: mac={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} max_mtu={mtu}", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + log!( + "DEVICE_ATTRIBUTES: mac={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} max_mtu={mtu}", + mac[0], + mac[1], + mac[2], + mac[3], + mac[4], + mac[5] + ); // Init SET_FEATUREs the firmware requires before IO-queue creation (matches iPXE): // configure async-event groups, then register the host-info page (whose driver_version @@ -705,7 +777,8 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { let (txd, rxd) = (u32at(32), u32at(40)); log!( "MAX_QUEUES_EXT: tx_cq_num={} rx_cq_num={} tx_cq_depth={txd} rx_cq_depth={rxd}", - u32at(16), u32at(24) + u32at(16), + u32at(24) ); dev_cq_depth = txd.min(rxd).min(u32::from(u16::MAX)) as u16; } @@ -758,10 +831,22 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { log!("creating IO queues (depth {depth})..."); let tx_cq = create_cq(pci, &mut admin, depth, TX_CDESC_WORDS, ENA_MSIX_NONE)?; let tx_sq = create_sq(pci, &mut admin, depth, tx_cq.idx, SQ_DIR_TX)?; - log!(" tx: cq idx={} db=0x{:x} sq idx={} db=0x{:x}", tx_cq.idx, tx_cq.db_off, tx_sq.idx, tx_sq.db_off); + log!( + " tx: cq idx={} db=0x{:x} sq idx={} db=0x{:x}", + tx_cq.idx, + tx_cq.db_off, + tx_sq.idx, + tx_sq.db_off + ); let rx_cq = create_cq(pci, &mut admin, depth, RX_CDESC_WORDS, ENA_MSIX_NONE)?; let rx_sq = create_sq(pci, &mut admin, depth, rx_cq.idx, SQ_DIR_RX)?; - log!(" rx: cq idx={} db=0x{:x} sq idx={} db=0x{:x}", rx_cq.idx, rx_cq.db_off, rx_sq.idx, rx_sq.db_off); + log!( + " rx: cq idx={} db=0x{:x} sq idx={} db=0x{:x}", + rx_cq.idx, + rx_cq.db_off, + rx_sq.idx, + rx_sq.db_off + ); // RX + TX buffers: a small fixed fill, decoupled from the (possibly deep) ring. let mut rx_bufs = Vec::with_capacity(RX_FILL); @@ -772,7 +857,11 @@ pub unsafe fn probe(pci: &PciIoProtocol) -> Result { for _ in 0..TX_FILL { tx_bufs.push(Dma::alloc(pci, BUF_SIZE)?); } - log!("IO queues created OK; {} rx + {} tx buffers allocated", rx_bufs.len(), tx_bufs.len()); + log!( + "IO queues created OK; {} rx + {} tx buffers allocated", + rx_bufs.len(), + tx_bufs.len() + ); Ok(Ena { pci: pci as *const PciIoProtocol, @@ -804,11 +893,19 @@ unsafe fn reset(pci: &PciIoProtocol, caps: u32) -> Result<(), Status> { let timeout_units = ((caps & CAPS_RESET_TIMEOUT_MASK) >> CAPS_RESET_TIMEOUT_SHIFT).max(1); let timeout_ms = timeout_units as usize * 100; let sts0 = reg_read32(pci, regs::DEV_STS); - log!("reset: DEV_STS=0x{sts0:08x} (ready={}) timeout={timeout_ms}ms", sts0 & DEV_STS_READY); + log!( + "reset: DEV_STS=0x{sts0:08x} (ready={}) timeout={timeout_ms}ms", + sts0 & DEV_STS_READY + ); reg_write32(pci, regs::DEV_CTL, DEV_CTL_RESET); fence(Ordering::SeqCst); - if !wait_sts(pci, DEV_STS_RESET_IN_PROGRESS, DEV_STS_RESET_IN_PROGRESS, timeout_ms) { + if !wait_sts( + pci, + DEV_STS_RESET_IN_PROGRESS, + DEV_STS_RESET_IN_PROGRESS, + timeout_ms, + ) { log!("!! reset never entered RESET_IN_PROGRESS"); return Err(Status::DEVICE_ERROR); } @@ -821,7 +918,10 @@ unsafe fn reset(pci: &PciIoProtocol, caps: u32) -> Result<(), Status> { return Err(Status::DEVICE_ERROR); } let sts = reg_read32(pci, regs::DEV_STS); - log!("reset: done DEV_STS=0x{sts:08x} (ready={})", sts & DEV_STS_READY); + log!( + "reset: done DEV_STS=0x{sts:08x} (ready={})", + sts & DEV_STS_READY + ); Ok(()) } diff --git a/crates/ena/src/pci.rs b/crates/ena/src/pci.rs index 4ffc2a3..397e9b9 100644 --- a/crates/ena/src/pci.rs +++ b/crates/ena/src/pci.rs @@ -27,9 +27,9 @@ pub enum PciWidth { #[repr(u32)] #[derive(Clone, Copy)] pub enum PciMapOp { - BusMasterRead = 0, - BusMasterWrite = 1, - BusMasterCommonBuffer = 2, + Read = 0, + Write = 1, + CommonBuffer = 2, } /// `EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION`. @@ -75,7 +75,8 @@ type MapFn = unsafe extern "efiapi" fn( mapping: *mut *mut c_void, ) -> Status; -type UnmapFn = unsafe extern "efiapi" fn(this: *const PciIoProtocol, mapping: *mut c_void) -> Status; +type UnmapFn = + unsafe extern "efiapi" fn(this: *const PciIoProtocol, mapping: *mut c_void) -> Status; // alloc_type / memory_type are EFI enums; u32 matches their ABI without importing them. type AllocBufferFn = unsafe extern "efiapi" fn( @@ -87,8 +88,11 @@ type AllocBufferFn = unsafe extern "efiapi" fn( attributes: u64, ) -> Status; -type FreeBufferFn = - unsafe extern "efiapi" fn(this: *const PciIoProtocol, pages: usize, host_address: *mut c_void) -> Status; +type FreeBufferFn = unsafe extern "efiapi" fn( + this: *const PciIoProtocol, + pages: usize, + host_address: *mut c_void, +) -> Status; type AttributesFn = unsafe extern "efiapi" fn( this: *const PciIoProtocol, @@ -145,13 +149,7 @@ impl PciIo { pub fn vendor_device(&self) -> uefi::Result<(u16, u16)> { let mut dw: u32 = 0; let st = unsafe { - (self.0.pci.read)( - &self.0, - PciWidth::U32, - 0, - 1, - (&mut dw as *mut u32).cast(), - ) + (self.0.pci.read)(&self.0, PciWidth::U32, 0, 1, (&mut dw as *mut u32).cast()) }; if st.is_success() { Ok((dw as u16, (dw >> 16) as u16)) diff --git a/crates/ena/src/snp.rs b/crates/ena/src/snp.rs index ca0f33b..6ff2773 100644 --- a/crates/ena/src/snp.rs +++ b/crates/ena/src/snp.rs @@ -68,7 +68,11 @@ pub fn install(controller: Handle, ena: Ena) -> uefi::Result<()> { let p = Box::into_raw(dev); unsafe { (*p).snp.mode = &mut (*p).mode; - boot::install_protocol_interface(Some(controller), &SNP_GUID, (&(*p).snp as *const SimpleNetworkProtocol).cast())?; + boot::install_protocol_interface( + Some(controller), + &SNP_GUID, + (&(*p).snp as *const SimpleNetworkProtocol).cast(), + )?; } Ok(()) } @@ -87,7 +91,10 @@ fn build_mode(mac: [u8; 6]) -> NetworkMode { max_packet_size: MAX_PACKET, nv_ram_size: 0, nv_ram_access_size: 0, - receive_filter_mask: (ReceiveFlags::UNICAST | ReceiveFlags::BROADCAST | ReceiveFlags::MULTICAST).bits(), + receive_filter_mask: (ReceiveFlags::UNICAST + | ReceiveFlags::BROADCAST + | ReceiveFlags::MULTICAST) + .bits(), receive_filter_setting: (ReceiveFlags::UNICAST | ReceiveFlags::BROADCAST).bits(), max_mcast_filter_count: 0, mcast_filter_count: 0, @@ -210,7 +217,9 @@ unsafe extern "efiapi" fn get_status( } let completed = d.ena.poll_tx(); if !tx_buf.is_null() { - *tx_buf = completed.map(|p| p as *mut c_void).unwrap_or(core::ptr::null_mut()); + *tx_buf = completed + .map(|p| p as *mut c_void) + .unwrap_or(core::ptr::null_mut()); } if !interrupt_status.is_null() { let mut s = InterruptStatus::empty(); diff --git a/crates/stage0-sign/src/main.rs b/crates/stage0-sign/src/main.rs index a5e32aa..9edbcfd 100644 --- a/crates/stage0-sign/src/main.rs +++ b/crates/stage0-sign/src/main.rs @@ -20,17 +20,17 @@ use std::path::{Path, PathBuf}; /// A signing context — the `stage1.*` roles stage0 admits. `tag()` must match stage1's namespace. #[derive(Clone, Copy)] enum Domain { - Stage1Uki, - Stage1Args, - Stage1Manifest, + Uki, + Args, + Manifest, } impl Domain { fn tag(self) -> &'static str { match self { - Domain::Stage1Uki => "lockboot.v1.stage1.uki", - Domain::Stage1Args => "lockboot.v1.stage1.args", - Domain::Stage1Manifest => "lockboot.v1.stage1.manifest", + Domain::Uki => "lockboot.v1.stage1.uki", + Domain::Args => "lockboot.v1.stage1.args", + Domain::Manifest => "lockboot.v1.stage1.manifest", } } } @@ -39,16 +39,24 @@ impl std::str::FromStr for Domain { type Err = &'static str; fn from_str(s: &str) -> std::result::Result { Ok(match s { - "stage1.uki" => Domain::Stage1Uki, - "stage1.args" => Domain::Stage1Args, - "stage1.manifest" => Domain::Stage1Manifest, - _ => return Err("unknown signing domain (want stage1.uki / stage1.args / stage1.manifest)"), + "stage1.uki" => Domain::Uki, + "stage1.args" => Domain::Args, + "stage1.manifest" => Domain::Manifest, + _ => { + return Err( + "unknown signing domain (want stage1.uki / stage1.args / stage1.manifest)", + ) + } }) } } #[derive(Parser)] -#[command(name = "stage0-sign", version, about = "ed25519 keygen + domain-separated signatures for stage0 artifacts.")] +#[command( + name = "stage0-sign", + version, + about = "ed25519 keygen + domain-separated signatures for stage0 artifacts." +)] struct Cli { #[command(subcommand)] cmd: Cmd, @@ -108,12 +116,23 @@ fn keygen(a: KeygenArgs) -> Result<()> { } fn sign_cmd(a: SignArgs) -> Result<()> { - let domain: Domain = a.domain.parse().map_err(|e| anyhow!("--domain {}: {e}", a.domain))?; - let pem = std::fs::read_to_string(&a.key).with_context(|| format!("reading key {}", a.key.display()))?; - let bytes = std::fs::read(&a.input).with_context(|| format!("reading {}", a.input.display()))?; + let domain: Domain = a + .domain + .parse() + .map_err(|e| anyhow!("--domain {}: {e}", a.domain))?; + let pem = std::fs::read_to_string(&a.key) + .with_context(|| format!("reading key {}", a.key.display()))?; + let bytes = + std::fs::read(&a.input).with_context(|| format!("reading {}", a.input.display()))?; let (sig, pubkey) = sign_bytes(&pem, domain, &bytes)?; std::fs::write(&a.out, &sig).with_context(|| format!("writing {}", a.out.display()))?; - println!("signed {} [{}] -> {} (pubkey {})", a.input.display(), domain.tag(), a.out.display(), pubkey); + println!( + "signed {} [{}] -> {} (pubkey {})", + a.input.display(), + domain.tag(), + a.out.display(), + pubkey + ); Ok(()) } @@ -137,8 +156,14 @@ fn sign_bytes(pem: &str, domain: Domain, message: &[u8]) -> Result<(Vec, Str /// Extract the 32-byte ed25519 seed from a PKCS#8 PEM (RFC 8410): `... 04 22 04 20 `. fn seed_from_pkcs8_pem(pem: &str) -> Result<[u8; 32]> { - let b64: String = pem.lines().filter(|l| !l.starts_with("-----")).collect::>().concat(); - let der = STANDARD.decode(b64.trim()).context("private key PEM body is not valid base64")?; + let b64: String = pem + .lines() + .filter(|l| !l.starts_with("-----")) + .collect::>() + .concat(); + let der = STANDARD + .decode(b64.trim()) + .context("private key PEM body is not valid base64")?; let pos = der .windows(4) .position(|w| w == [0x04u8, 0x22, 0x04, 0x20]) @@ -159,7 +184,10 @@ fn pem_from_seed(seed: &[u8; 32]) -> String { 0x20, ]; der.extend_from_slice(seed); - format!("-----BEGIN PRIVATE KEY-----\n{}\n-----END PRIVATE KEY-----\n", STANDARD.encode(&der)) + format!( + "-----BEGIN PRIVATE KEY-----\n{}\n-----END PRIVATE KEY-----\n", + STANDARD.encode(&der) + ) } /// 32 CSPRNG bytes from the kernel via `/dev/urandom` (std only — no getrandom/libc/C toolchain). @@ -184,7 +212,8 @@ fn write_private(path: &Path, bytes: &[u8]) -> Result<()> { .mode(0o600) .open(path) .with_context(|| format!("creating {}", path.display()))?; - f.write_all(bytes).with_context(|| format!("writing {}", path.display())) + f.write_all(bytes) + .with_context(|| format!("writing {}", path.display())) } #[cfg(test)] @@ -196,8 +225,12 @@ mod tests { /// vice-versa for the shared `stage1.*` roles. Drift in either repo's framing fails CI. #[test] fn golden_kat() { - let (sig, pubkey) = - sign_bytes(&pem_from_seed(&[7u8; 32]), Domain::Stage1Manifest, b"lockboot-kat").unwrap(); + let (sig, pubkey) = sign_bytes( + &pem_from_seed(&[7u8; 32]), + Domain::Manifest, + b"lockboot-kat", + ) + .unwrap(); assert_eq!(pubkey, "6kpsY+KcUgq+9VB7Ey7F+ZVHdq6+vnuSQh7qaRRG0iw="); assert_eq!( STANDARD.encode(&sig), diff --git a/crates/stage0-test-payload/src/main.rs b/crates/stage0-test-payload/src/main.rs index 1e4ccf2..eaf8f89 100644 --- a/crates/stage0-test-payload/src/main.rs +++ b/crates/stage0-test-payload/src/main.rs @@ -21,9 +21,9 @@ use anyhow::{anyhow, bail, Result}; use uefi::boot::{self, ScopedProtocol}; use uefi::prelude::*; use uefi::println; -use uefi::runtime::{self, ResetType}; use uefi::proto::loaded_image::LoadedImage; use uefi::proto::tcg::v2::Tcg; +use uefi::runtime::{self, ResetType}; use vaportpm_attest::{PcrOps, TpmTransport}; struct Tcg2Transport { @@ -124,7 +124,11 @@ fn print_pcrs() -> Result<()> { println!("payload: ===PCR-DUMP-BEGIN==="); for (idx, alg, value) in &pcrs { - println!("payload: PCR {} {idx:02} {}", alg.name(), hex::encode(value)); + println!( + "payload: PCR {} {idx:02} {}", + alg.name(), + hex::encode(value) + ); } println!("payload: ===PCR-DUMP-END==="); Ok(()) diff --git a/crates/stage0/src/dns4.rs b/crates/stage0/src/dns4.rs index 6d42f73..af6a496 100644 --- a/crates/stage0/src/dns4.rs +++ b/crates/stage0/src/dns4.rs @@ -44,7 +44,10 @@ pub fn resolve(host: &str) -> Result<[u8; 4], Status> { for srv in &servers { crate::sdbg!( "stage0: DNS: asking {}.{}.{}.{} for {host}", - srv.0[0], srv.0[1], srv.0[2], srv.0[3] + srv.0[0], + srv.0[1], + srv.0[2], + srv.0[3] ); let resp = match crate::udp4::query(srv.0, DNS_PORT, &query, DNS_TRIES, DNS_TRY_MS) { Ok(r) => r, @@ -53,7 +56,10 @@ pub fn resolve(host: &str) -> Result<[u8; 4], Status> { if let Ok(ip) = parse_response(&resp, id) { crate::sdbg!( "stage0: resolved {host} -> {}.{}.{}.{}", - ip[0], ip[1], ip[2], ip[3] + ip[0], + ip[1], + ip[2], + ip[3] ); return Ok(ip); } @@ -131,7 +137,10 @@ fn skip_name(msg: &[u8], mut pos: usize) -> Result { return Ok(pos + 1); } if len & 0xc0 == 0xc0 { - return pos.checked_add(2).filter(|&p| p <= msg.len()).ok_or(Status::PROTOCOL_ERROR); + return pos + .checked_add(2) + .filter(|&p| p <= msg.len()) + .ok_or(Status::PROTOCOL_ERROR); } pos = pos .checked_add(1 + len as usize) diff --git a/crates/stage0/src/main.rs b/crates/stage0/src/main.rs index 9803885..6348063 100644 --- a/crates/stage0/src/main.rs +++ b/crates/stage0/src/main.rs @@ -44,6 +44,9 @@ use uefi::proto::loaded_image::LoadedImage; use uefi::runtime::{self, ResetType, VariableVendor}; use uefi::{CStr16, CString16}; +/// Admission result: the admitted binary, its SHA-256 digest, and the optional load-options string. +type Admitted = Result<(Vec, [u8; 32], Option), Status>; + /// PCR extended with SHA-256 of the loaded payload (matches stage1's binary PCR). const PCR_BINARY: u8 = 14; @@ -70,7 +73,10 @@ fn main() -> Status { // platform (payload, firmware) can no longer be trusted. Fail CLOSED by powering // the machine off. Hold first (see FAIL_CLOSED_DRAIN_US) so the cloud serial // console actually captures the error before the instance halts. - crate::slog!("stage0: powering off in {}s (fail-closed)", FAIL_CLOSED_DRAIN_US / 1_000_000); + crate::slog!( + "stage0: powering off in {}s (fail-closed)", + FAIL_CLOSED_DRAIN_US / 1_000_000 + ); boot::stall(FAIL_CLOSED_DRAIN_US); runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None) } @@ -199,7 +205,7 @@ fn download_first(urls: &[String]) -> Result, Status> { /// (url,hash) is a cycle and fails closed. stage0 forwards no document (the UKI re-fetches its own /// metadata), so the merged doc drives only re-evaluation. Returns the admitted binary, its digest, /// and the load options (signed args, else inline `args` joined by spaces). -fn resolve_payload(json: &[u8]) -> Result<(Vec, [u8; 32], Option), Status> { +fn resolve_payload(json: &[u8]) -> Admitted { // Validate the document up front (clear error) + confirm this arch is present, then drive // resolution off a Value so a signed manifest fragment can be deep-merged and re-evaluated. let ud = config::parse(json).map_err(|m| { @@ -210,7 +216,11 @@ fn resolve_payload(json: &[u8]) -> Result<(Vec, [u8; 32], Option), S crate::slog!("stage0: no _stage1 config for this architecture"); Status::UNSUPPORTED })?; - let arch = if cfg!(target_arch = "aarch64") { "aarch64" } else { "x86_64" }; + let arch = if cfg!(target_arch = "aarch64") { + "aarch64" + } else { + "x86_64" + }; let mut doc: Value = serde_json::from_slice(json).map_err(|_| Status::INVALID_PARAMETER)?; let mut history: Vec = Vec::new(); @@ -235,7 +245,10 @@ fn resolve_payload(json: &[u8]) -> Result<(Vec, [u8; 32], Option), S })?; let (binary, digest, signed_args) = admit_payload(&p.url.0, &mode)?; let opts = signed_args.or_else(|| { - p.args.as_deref().filter(|a| !a.is_empty()).map(|a| a.join(" ")) + p.args + .as_deref() + .filter(|a| !a.is_empty()) + .map(|a| a.join(" ")) }); return Ok((binary, digest, opts)); } @@ -245,10 +258,9 @@ fn resolve_payload(json: &[u8]) -> Result<(Vec, [u8; 32], Option), S Status::INVALID_PARAMETER })?; let (murl, bytes, hash) = fetch_manifest(&m)?; - if history - .iter() - .any(|r| r.sha256.as_deref() == Some(hash.as_str()) && r.url.0 == [murl.clone()]) - { + if history.iter().any(|r| { + r.sha256.as_deref() == Some(hash.as_str()) && r.url.0 == [murl.clone()] + }) { crate::slog!("stage0: manifest resolution cycle at {murl}"); return Err(Status::SECURITY_VIOLATION); } @@ -260,7 +272,11 @@ fn resolve_payload(json: &[u8]) -> Result<(Vec, [u8; 32], Option), S }); // Consume the pointer, then deep-merge the manifest fragment (manifest wins). The // merged entry re-populates with a `payload` (stop) or a fresh `manifest` (delegate). - if let Some(e) = doc.get_mut("_stage1").and_then(|s| s.get_mut(arch)).and_then(Value::as_object_mut) { + if let Some(e) = doc + .get_mut("_stage1") + .and_then(|s| s.get_mut(arch)) + .and_then(Value::as_object_mut) + { e.remove("manifest"); } let manifest_doc: Value = serde_json::from_slice(&bytes).map_err(|_| { @@ -303,11 +319,14 @@ fn try_fetch_manifest(m: &ManifestRef, url: &str) -> Result<(Vec, String), S None => alloc::vec![alloc::format!("{url}.sig")], }; let signature = download_first(&sig_urls)?; - sig::verify(&m.ed25519, sig::Domain::Stage1Manifest, &bytes, &signature).map_err(|e| { + sig::verify(&m.ed25519, sig::Domain::Manifest, &bytes, &signature).map_err(|e| { crate::slog!("stage0: manifest verification failed: {e}"); Status::SECURITY_VIOLATION })?; - crate::slog!("stage0: manifest verified: sha256:{hash} (ed25519 key:{})", m.ed25519); + crate::slog!( + "stage0: manifest verified: sha256:{hash} (ed25519 key:{})", + m.ed25519 + ); Ok((bytes, hash)) } @@ -332,7 +351,7 @@ fn deep_merge(base: &mut Value, overlay: &Value) { /// Try each payload URL until one downloads and admits (content is pinned, so any mirror /// that yields verifying bytes is acceptable). Returns the bytes, their SHA-256 digest, /// and any verified signed load options. -fn admit_payload(urls: &[String], mode: &Admit) -> Result<(Vec, [u8; 32], Option), Status> { +fn admit_payload(urls: &[String], mode: &Admit) -> Admitted { let mut last = Status::NOT_FOUND; for url in urls { match admit_from(url, mode) { @@ -347,7 +366,7 @@ fn admit_payload(urls: &[String], mode: &Admit) -> Result<(Vec, [u8; 32], Op } /// Download one payload candidate and run admission control (a gate — never measured). -fn admit_from(url: &str, mode: &Admit) -> Result<(Vec, [u8; 32], Option), Status> { +fn admit_from(url: &str, mode: &Admit) -> Admitted { crate::sdbg!("stage0: downloading payload from {url}"); let binary = http::download(url)?; crate::slog!("stage0: payload: {} bytes from {url}", binary.len()); @@ -362,7 +381,12 @@ fn admit_from(url: &str, mode: &Admit) -> Result<(Vec, [u8; 32], Option { + Admit::Ed25519 { + pubkey, + sig_url, + args_url, + args_sig_url, + } => { // Detached signature: the `sig_url` templates with `{sha256}` replaced by the // payload digest (content-addressable), else `.sig` (co-located per mirror). let sig_urls = match sig_url { @@ -370,13 +394,18 @@ fn admit_from(url: &str, mode: &Admit) -> Result<(Vec, [u8; 32], Option alloc::vec![alloc::format!("{url}.sig")], }; let signature = download_first(&sig_urls)?; - sig::verify(pubkey, sig::Domain::Stage1Uki, &binary, &signature).map_err(|m| { + sig::verify(pubkey, sig::Domain::Uki, &binary, &signature).map_err(|m| { crate::slog!("stage0: ed25519 verification failed: {m}"); Status::SECURITY_VIOLATION })?; crate::slog!("stage0: verified: sha256:{hash} (ed25519 key:{pubkey})"); if let Some(au) = args_url { - signed_args = Some(fetch_signed_args(&au.0, args_sig_url.as_ref(), pubkey, &hash)?); + signed_args = Some(fetch_signed_args( + &au.0, + args_sig_url.as_ref(), + pubkey, + &hash, + )?); } } } @@ -396,11 +425,14 @@ fn fetch_signed_args( let args_urls = substitute(args_urls, payload_hash); let sig_urls = match args_sig_url { Some(u) => substitute(&u.0, payload_hash), - None => args_urls.iter().map(|u| alloc::format!("{u}.sig")).collect(), + None => args_urls + .iter() + .map(|u| alloc::format!("{u}.sig")) + .collect(), }; let args = download_first(&args_urls)?; let sig = download_first(&sig_urls)?; - sig::verify(pubkey, sig::Domain::Stage1Args, &args, &sig).map_err(|m| { + sig::verify(pubkey, sig::Domain::Args, &args, &sig).map_err(|m| { crate::slog!("stage0: signed args verification failed: {m}"); Status::SECURITY_VIOLATION })?; diff --git a/crates/stage0/src/net.rs b/crates/stage0/src/net.rs index b5a168d..c0ac261 100644 --- a/crates/stage0/src/net.rs +++ b/crates/stage0/src/net.rs @@ -23,16 +23,25 @@ const DHCP_TIMEOUT_MS: u64 = 30_000; /// Building stage0 requires `build//ena.efi` to exist (the Makefile builds it /// first); a missing file is a hard compile error, by design. #[cfg(target_arch = "x86_64")] -static ENA_DRIVER: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../build/x86_64/ena.efi")); +static ENA_DRIVER: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../build/x86_64/ena.efi" +)); #[cfg(target_arch = "aarch64")] -static ENA_DRIVER: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../build/aarch64/ena.efi")); +static ENA_DRIVER: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../build/aarch64/ena.efi" +)); /// Load and start any embedded UEFI NIC drivers, before connecting controllers, so a /// firmware that lacks a driver for its NIC (notably EC2/ENA) still gets an SNP /// producer. The driver is a well-behaved Driver-Model driver: inert on platforms /// whose NIC it does not match (GCP virtio-net, local QEMU). fn load_embedded_drivers() { - crate::slog!("stage0: loading embedded ena driver ({} bytes)", ENA_DRIVER.len()); + crate::slog!( + "stage0: loading embedded ena driver ({} bytes)", + ENA_DRIVER.len() + ); match crate::secauth::load_image_verified(ENA_DRIVER) { Ok(handle) => match boot::start_image(handle) { Ok(()) => crate::slog!("stage0: ena driver started"), @@ -66,7 +75,13 @@ fn dhcp_up(ip4: &mut Ip4Config2) -> Result<(), Status> { let info = ip4.get_interface_info().map_err(|e| e.status())?; if addr(info.station_addr) != [0, 0, 0, 0] { let a = addr(info.station_addr); - crate::slog!("stage0: network: OK {}.{}.{}.{} (already up)", a[0], a[1], a[2], a[3]); + crate::slog!( + "stage0: network: OK {}.{}.{}.{} (already up)", + a[0], + a[1], + a[2], + a[3] + ); return Ok(()); } @@ -82,7 +97,13 @@ fn dhcp_up(ip4: &mut Ip4Config2) -> Result<(), Status> { let a = addr(info.station_addr); if a != [0, 0, 0, 0] { let took = crate::timing::since_boot_ms().wrapping_sub(start); - crate::slog!("stage0: network: OK {}.{}.{}.{} (DHCP {took} ms)", a[0], a[1], a[2], a[3]); + crate::slog!( + "stage0: network: OK {}.{}.{}.{} (DHCP {took} ms)", + a[0], + a[1], + a[2], + a[3] + ); return Ok(()); } if crate::timing::since_boot_ms().wrapping_sub(start) >= DHCP_TIMEOUT_MS { diff --git a/crates/stage0/src/sig.rs b/crates/stage0/src/sig.rs index d02ee0a..cfed8c2 100644 --- a/crates/stage0/src/sig.rs +++ b/crates/stage0/src/sig.rs @@ -26,19 +26,19 @@ use sha2::{Digest, Sha256}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Domain { /// `_stage1` payload (the UKI). - Stage1Uki, + Uki, /// `_stage1` LoadOptions (signed args). - Stage1Args, + Args, /// `_stage1` signed manifest. - Stage1Manifest, + Manifest, } impl Domain { fn tag(self) -> &'static str { match self { - Domain::Stage1Uki => "lockboot.v1.stage1.uki", - Domain::Stage1Args => "lockboot.v1.stage1.args", - Domain::Stage1Manifest => "lockboot.v1.stage1.manifest", + Domain::Uki => "lockboot.v1.stage1.uki", + Domain::Args => "lockboot.v1.stage1.args", + Domain::Manifest => "lockboot.v1.stage1.manifest", } } } diff --git a/crates/stage0/src/tcp4.rs b/crates/stage0/src/tcp4.rs index acd3560..e174c10 100644 --- a/crates/stage0/src/tcp4.rs +++ b/crates/stage0/src/tcp4.rs @@ -240,7 +240,11 @@ fn exchange_on_child( } crate::sdbg!( "stage0: TCP4 connected to {}.{}.{}.{}:{}", - ip[0], ip[1], ip[2], ip[3], port + ip[0], + ip[1], + ip[2], + ip[3], + port ); let send_res = tcp_send(tcp_ptr, request); diff --git a/crates/stage0/src/udp4.rs b/crates/stage0/src/udp4.rs index 7185f40..270e805 100644 --- a/crates/stage0/src/udp4.rs +++ b/crates/stage0/src/udp4.rs @@ -263,7 +263,9 @@ fn udp_send(udp_ptr: *mut Udp4Protocol, data: &[u8]) -> Result<(), Status> { let mut token = CompletionToken { event, status: Status::NOT_READY, - packet: Packet { tx_data: &tx as *const TxData }, + packet: Packet { + tx_data: &tx as *const TxData, + }, }; let st = unsafe { ((*udp_ptr).transmit)(udp_ptr, &mut token) }; let st = if st == Status::SUCCESS { @@ -290,7 +292,9 @@ fn udp_recv(udp_ptr: *mut Udp4Protocol, budget_ms: u64) -> Result, Statu let mut token = CompletionToken { event, status: Status::NOT_READY, - packet: Packet { rx_data: ptr::null_mut() }, + packet: Packet { + rx_data: ptr::null_mut(), + }, }; let call = unsafe { ((*udp_ptr).receive)(udp_ptr, &mut token) }; let st = if call == Status::SUCCESS {