From 7d3fe45475fe7b095df2ad95b5e9657760aca797 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Fri, 13 Feb 2026 10:09:33 -0800 Subject: [PATCH 1/4] nvme: add oxide-specific feature to expose read-only attribute for device --- lib/propolis/src/hw/nvme/admin.rs | 8 ++++++++ lib/propolis/src/hw/nvme/bits.rs | 9 +++++++++ lib/propolis/src/hw/nvme/cmds.rs | 17 ++++++++++++++++- lib/propolis/src/hw/nvme/mod.rs | 5 +++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/propolis/src/hw/nvme/admin.rs b/lib/propolis/src/hw/nvme/admin.rs index df8746a62..2a3af735b 100644 --- a/lib/propolis/src/hw/nvme/admin.rs +++ b/lib/propolis/src/hw/nvme/admin.rs @@ -403,6 +403,13 @@ impl NvmeCtrl { ) } + cmds::FeatureIdent::OxideDeviceFeatures => { + cmds::Completion::success_val( + cmds::OxideDeviceFeatures { read_only: self.read_only } + .into(), + ) + } + cmds::FeatureIdent::Reserved | cmds::FeatureIdent::LbaRangeType | cmds::FeatureIdent::SoftwareProgressMarker @@ -457,6 +464,7 @@ impl NvmeCtrl { | cmds::FeatureIdent::WriteAtomicity | cmds::FeatureIdent::AsynchronousEventConfiguration | cmds::FeatureIdent::SoftwareProgressMarker + | cmds::FeatureIdent::OxideDeviceFeatures | cmds::FeatureIdent::Vendor(_) => { cmds::Completion::generic_err(STS_INVAL_FIELD).dnr() } diff --git a/lib/propolis/src/hw/nvme/bits.rs b/lib/propolis/src/hw/nvme/bits.rs index 130bb3013..c227ae1f4 100644 --- a/lib/propolis/src/hw/nvme/bits.rs +++ b/lib/propolis/src/hw/nvme/bits.rs @@ -699,6 +699,15 @@ pub const FEAT_ID_WRITE_ATOMIC: u8 = 0x0A; /// See NVMe 1.0e Section 5.12.1.11 Asynchronous Event Configuration (Feature Identifier 0Bh) pub const FEAT_ID_ASYNC_EVENT_CFG: u8 = 0x0B; +/// Oxide-specific feature. +/// +/// Provides device-specific features beyond the standard NVMe capabilities as +/// a single Dword result: +/// Bit 0 [ReadOnly] - If set, the device will complete all writes with +/// STS_WRITE_READ_ONLY_RANGE. +/// Bits 31-1 - Reserved. +pub const FEAT_ID_OXIDE_DEVICE_FEATURES: u8 = 0xF0; + // Identify CNS values /// Identify - Namespace Structure diff --git a/lib/propolis/src/hw/nvme/cmds.rs b/lib/propolis/src/hw/nvme/cmds.rs index 88438ee3b..bb48ee1e0 100644 --- a/lib/propolis/src/hw/nvme/cmds.rs +++ b/lib/propolis/src/hw/nvme/cmds.rs @@ -452,7 +452,12 @@ pub enum FeatureIdent { /// This feature is persistent across power states. /// See NVMe 1.0e Section 7.6.1.1 Software Progress Marker SoftwareProgressMarker, - /// Vendor specific feature. + + // Vendor specific features. + /// Oxide-specific feature - returns relevant device features. + OxideDeviceFeatures, + + /// All other vendor specific features. Vendor(#[allow(dead_code)] u8), } @@ -476,6 +481,7 @@ impl From for FeatureIdent { 0xC..=0x7F => Reserved, 0x80 => SoftwareProgressMarker, 0x81..=0xBF => Reserved, + FEAT_ID_OXIDE_DEVICE_FEATURES => OxideDeviceFeatures, 0xC0..=0xFF => Vendor(fid), } } @@ -651,6 +657,15 @@ impl From for u32 { } } +pub(crate) struct OxideDeviceFeatures { + pub read_only: bool, +} +impl From for u32 { + fn from(value: OxideDeviceFeatures) -> u32 { + u32::from(value.read_only) + } +} + /// A parsed NVM Command #[allow(dead_code)] #[derive(Debug)] diff --git a/lib/propolis/src/hw/nvme/mod.rs b/lib/propolis/src/hw/nvme/mod.rs index 78d0b1ffa..9a3bf657d 100644 --- a/lib/propolis/src/hw/nvme/mod.rs +++ b/lib/propolis/src/hw/nvme/mod.rs @@ -207,6 +207,8 @@ struct NvmeCtrl { /// The Identify structure returned for Identify namespace commands ns_ident: IdentifyNamespace, + + read_only: bool, } impl NvmeCtrl { @@ -566,6 +568,8 @@ impl NvmeCtrl { .iter() .filter_map(Option::as_ref) .for_each(|sq| sq.update_params(params)); + + self.read_only = info.read_only; } /// Get Memory Page Size (MPS), expressed in bytes @@ -899,6 +903,7 @@ impl PciNvme { sqs: Default::default(), ctrl_ident, ns_ident, + read_only: false, }; let pci_state = builder From fc631c86faa86a299c76277a91ac2928d0521107 Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Fri, 13 Feb 2026 10:18:38 -0800 Subject: [PATCH 2/4] Bump bootrom to propolis/oxide-nvme-readonly edk2 branch --- packaging/package-manifest.toml | 4 ++-- phd-tests/artifacts.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packaging/package-manifest.toml b/packaging/package-manifest.toml index d33103f4c..ebd0011ec 100644 --- a/packaging/package-manifest.toml +++ b/packaging/package-manifest.toml @@ -14,6 +14,6 @@ output.type = "zone" [[package.propolis-server.source.buildomat_blobs]] repo = "edk2" series = "image_debug" -commit = "907a5fd1763ce5ddd74001261e5b52cd200a25f9" +commit = "8c60ff4c271b0442b9a506a5cf9eab04cd5554aa" artifact = "OVMF_CODE.fd" -sha256 = "ff12d5cb021e34447b44301f70434e861b07d2779c16abe2f2efef49ff02fffb" +sha256 = "9ce2266aaefb671a1977ddabb5155f8235be134f797e6c831abeb6c97c705569" diff --git a/phd-tests/artifacts.toml b/phd-tests/artifacts.toml index 4b61634e2..46d59dbd1 100644 --- a/phd-tests/artifacts.toml +++ b/phd-tests/artifacts.toml @@ -13,5 +13,5 @@ kind = "bootrom" [artifacts.ovmf.source.buildomat] repo = "oxidecomputer/edk2" series = "image_debug" -commit = "907a5fd1763ce5ddd74001261e5b52cd200a25f9" -sha256 = "ff12d5cb021e34447b44301f70434e861b07d2779c16abe2f2efef49ff02fffb" +commit = "8c60ff4c271b0442b9a506a5cf9eab04cd5554aa" +sha256 = "9ce2266aaefb671a1977ddabb5155f8235be134f797e6c831abeb6c97c705569" From 77097aa9d00777a36d579f39ee016d53f0aaccdb Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Wed, 18 Feb 2026 15:18:06 -0800 Subject: [PATCH 3/4] nvme: Be more strict with parameter for OxideDeviceFeatures vendor feat --- lib/propolis/src/hw/nvme/admin.rs | 14 ++++++++++---- lib/propolis/src/hw/nvme/cmds.rs | 14 ++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/propolis/src/hw/nvme/admin.rs b/lib/propolis/src/hw/nvme/admin.rs index 2a3af735b..085b770e8 100644 --- a/lib/propolis/src/hw/nvme/admin.rs +++ b/lib/propolis/src/hw/nvme/admin.rs @@ -404,10 +404,16 @@ impl NvmeCtrl { } cmds::FeatureIdent::OxideDeviceFeatures => { - cmds::Completion::success_val( - cmds::OxideDeviceFeatures { read_only: self.read_only } - .into(), - ) + if cmd.cdw11 != 0 { + // We don't currently accept any parameters for this feature + cmds::Completion::generic_err(STS_INVAL_FIELD) + } else { + cmds::Completion::success_val( + cmds::OxideDeviceFeatures(0) + .with_read_only(self.read_only) + .0, + ) + } } cmds::FeatureIdent::Reserved diff --git a/lib/propolis/src/hw/nvme/cmds.rs b/lib/propolis/src/hw/nvme/cmds.rs index bb48ee1e0..b11234aea 100644 --- a/lib/propolis/src/hw/nvme/cmds.rs +++ b/lib/propolis/src/hw/nvme/cmds.rs @@ -657,12 +657,14 @@ impl From for u32 { } } -pub(crate) struct OxideDeviceFeatures { - pub read_only: bool, -} -impl From for u32 { - fn from(value: OxideDeviceFeatures) -> u32 { - u32::from(value.read_only) +bitstruct! { + pub struct OxideDeviceFeatures(pub u32) { + /// Indicates the device is read-only and will complete all attempted + /// writes with `STS_WRITE_READ_ONLY_RANGE`. + pub read_only: bool = 0; + + /// Reserved + reserved: u32 = 1..32; } } From a2c84310969529a37c5914cdb02d953a5023b0ff Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Wed, 18 Feb 2026 23:45:57 -0800 Subject: [PATCH 4/4] Bump bootrom commit. --- bin/propolis-standalone/README.md | 2 +- packaging/package-manifest.toml | 4 ++-- phd-tests/artifacts.toml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/propolis-standalone/README.md b/bin/propolis-standalone/README.md index 663daf30f..e2b846d5d 100644 --- a/bin/propolis-standalone/README.md +++ b/bin/propolis-standalone/README.md @@ -61,7 +61,7 @@ are some options to get up-and-running quickly: ### Guest bootrom The current recommended and tested guest bootrom is available -[here](https://buildomat.eng.oxide.computer/public/file/oxidecomputer/edk2/image_debug/907a5fd1763ce5ddd74001261e5b52cd200a25f9/OVMF_CODE.fd). +[here](https://buildomat.eng.oxide.computer/public/file/oxidecomputer/edk2/image_debug/bf64f45b1a58e69d126a3c6ca1e4512c88668132/OVMF_CODE.fd). Other UEFI firmware images built from the [Open Virtual Machine Firmware project](https://github.com/tianocore/tianocore.github.io/wiki/OVMF) may also diff --git a/packaging/package-manifest.toml b/packaging/package-manifest.toml index ebd0011ec..809bf6bf6 100644 --- a/packaging/package-manifest.toml +++ b/packaging/package-manifest.toml @@ -14,6 +14,6 @@ output.type = "zone" [[package.propolis-server.source.buildomat_blobs]] repo = "edk2" series = "image_debug" -commit = "8c60ff4c271b0442b9a506a5cf9eab04cd5554aa" +commit = "bf64f45b1a58e69d126a3c6ca1e4512c88668132" artifact = "OVMF_CODE.fd" -sha256 = "9ce2266aaefb671a1977ddabb5155f8235be134f797e6c831abeb6c97c705569" +sha256 = "740187046a7267d0de72d3455070333547dbc0ea023531471fb2b2a61effa448" diff --git a/phd-tests/artifacts.toml b/phd-tests/artifacts.toml index 46d59dbd1..0a0789bb9 100644 --- a/phd-tests/artifacts.toml +++ b/phd-tests/artifacts.toml @@ -13,5 +13,5 @@ kind = "bootrom" [artifacts.ovmf.source.buildomat] repo = "oxidecomputer/edk2" series = "image_debug" -commit = "8c60ff4c271b0442b9a506a5cf9eab04cd5554aa" -sha256 = "9ce2266aaefb671a1977ddabb5155f8235be134f797e6c831abeb6c97c705569" +commit = "bf64f45b1a58e69d126a3c6ca1e4512c88668132" +sha256 = "740187046a7267d0de72d3455070333547dbc0ea023531471fb2b2a61effa448"