diff --git a/bin/propolis-server/src/lib/initializer.rs b/bin/propolis-server/src/lib/initializer.rs index 6f5ac4209..b51b7b98e 100644 --- a/bin/propolis-server/src/lib/initializer.rs +++ b/bin/propolis-server/src/lib/initializer.rs @@ -107,11 +107,6 @@ struct StorageBackendInstance { crucible: Option<(uuid::Uuid, Arc)>, } -#[derive(Default)] -pub struct MachineInitializerState { - rom_size_bytes: Option, -} - pub struct MachineInitializer<'a> { pub(crate) log: slog::Logger, pub(crate) machine: &'a Machine, @@ -122,7 +117,6 @@ pub struct MachineInitializer<'a> { pub(crate) properties: &'a InstanceProperties, pub(crate) toml_config: &'a crate::server::VmTomlConfig, pub(crate) producer_registry: Option, - pub(crate) state: MachineInitializerState, pub(crate) kstat_sampler: Option, } @@ -130,7 +124,7 @@ impl<'a> MachineInitializer<'a> { pub fn initialize_rom( &mut self, path: &std::path::Path, - ) -> Result<(), Error> { + ) -> Result { fn open_bootrom(path: &std::path::Path) -> Result<(File, usize)> { let fp = File::open(path)?; let len = fp.metadata()?.len(); @@ -162,8 +156,7 @@ impl<'a> MachineInitializer<'a> { // TODO: Handle short read return Err(Error::new(ErrorKind::InvalidData, "short read")); } - self.state.rom_size_bytes = Some(rom_len); - Ok(()) + Ok(rom_len) } pub fn initialize_rtc( @@ -929,48 +922,15 @@ impl<'a> MachineInitializer<'a> { Ok(()) } - fn generate_smbios(&self) -> smbios::TableBytes { - use propolis::cpuid; - use smbios::table::{type0, type1, type16, type4}; - - let rom_size = - self.state.rom_size_bytes.expect("ROM is already populated"); - let bios_version = self + fn generate_smbios(&self, rom_size: usize) -> smbios::TableBytes { + let rom_version: String = self .toml_config .bootrom_version .as_deref() .unwrap_or("v0.8") - .try_into() - .expect("bootrom version string doesn't contain NUL bytes"); - let smb_type0 = smbios::table::Type0 { - vendor: "Oxide".try_into().unwrap(), - bios_version, - bios_release_date: "The Aftermath 30, 3185 YOLD" - .try_into() - .unwrap(), - bios_rom_size: ((rom_size / (64 * 1024)) - 1) as u8, - bios_characteristics: type0::BiosCharacteristics::UNSUPPORTED, - bios_ext_characteristics: type0::BiosExtCharacteristics::ACPI - | type0::BiosExtCharacteristics::UEFI - | type0::BiosExtCharacteristics::IS_VM, - ..Default::default() - }; - - let smb_type1 = smbios::table::Type1 { - manufacturer: "Oxide".try_into().unwrap(), - product_name: "OxVM".try_into().unwrap(), + .to_string(); - serial_number: self - .properties - .id - .to_string() - .try_into() - .unwrap_or_default(), - uuid: self.properties.id.to_bytes_le(), - - wake_up_type: type1::WakeUpType::PowerSwitch, - ..Default::default() - }; + use propolis::cpuid; // Once CPUID profiles are integrated, these will need to take that into // account, rather than blindly querying from the host @@ -982,100 +942,48 @@ impl<'a> MachineInitializer<'a> { cpuid::host_query(cpuid::Ident(0x8000_0004, None)), ]; - let family = match cpuid_ident.eax & 0xf00 { - // If family ID is 0xf, extended family is added to it - 0xf00 => (cpuid_ident.eax >> 20 & 0xff) + 0xf, - // ... otherwise base family ID is used - base => base >> 8, + let smbios_params = propolis::firmware::smbios::SmbiosParams { + memory_size: (self.properties.memory as usize) * MB, + rom_size, + rom_release_date: "The Aftermath 30, 3185 YOLD".to_string(), + rom_version, + num_cpus: self.properties.vcpus, + cpuid_vendor, + cpuid_ident, + cpuid_procname, + system_id: self.properties.id, }; - let vendor = cpuid::VendorKind::try_from(cpuid_vendor); - let proc_manufacturer = match vendor { - Ok(cpuid::VendorKind::Intel) => "Intel", - Ok(cpuid::VendorKind::Amd) => "Advanced Micro Devices, Inc.", - _ => "", - } - .try_into() - .unwrap(); - let proc_family = match (vendor, family) { - // Explicitly match for Zen-based CPUs - // - // Although this family identifier is not valid in SMBIOS 2.7, - // having been defined in 3.x, we pass it through anyways. - (Ok(cpuid::VendorKind::Amd), family) if family >= 0x17 => 0x6b, - - // Emit Unknown for everything else - _ => 0x2, - }; - let proc_id = - u64::from(cpuid_ident.eax) | u64::from(cpuid_ident.edx) << 32; - let proc_version = - cpuid::parse_brand_string(cpuid_procname).unwrap_or("".to_string()); - - let smb_type4 = smbios::table::Type4 { - proc_type: type4::ProcType::Central, - proc_family, - proc_manufacturer, - proc_id, - proc_version: proc_version.try_into().unwrap_or_default(), - status: type4::ProcStatus::Enabled, - // unknown - proc_upgrade: 0x2, - // make core and thread counts equal for now - core_count: self.properties.vcpus, - core_enabled: self.properties.vcpus, - thread_count: self.properties.vcpus, - proc_characteristics: type4::Characteristics::IS_64_BIT - | type4::Characteristics::MULTI_CORE, - ..Default::default() - }; - - let memsize_bytes = (self.properties.memory as usize) * MB; - let mut smb_type16 = smbios::table::Type16 { - location: type16::Location::SystemBoard, - array_use: type16::ArrayUse::System, - error_correction: type16::ErrorCorrection::Unknown, - num_mem_devices: 1, - ..Default::default() - }; - smb_type16.set_max_capacity(memsize_bytes); - let phys_mem_array_handle = 0x1600.into(); - - let mut smb_type17 = smbios::table::Type17 { - phys_mem_array_handle, - // Unknown - form_factor: 0x2, - // Unknown - memory_type: 0x2, - ..Default::default() - }; - smb_type17.set_size(Some(memsize_bytes)); - - let smb_type32 = smbios::table::Type32::default(); - // With "only" types 0, 1, 4, 16, 17, and 32, we are technically missing // some (types 3, 7, 9, 19) of the data required by the 2.7 spec. The // data provided here were what we determined was a reasonable // collection to start with. Should further requirements arise, we may // expand on it. let mut smb_tables = smbios::Tables::new(0x7f00.into()); - smb_tables.add(0x0000.into(), &smb_type0).unwrap(); - smb_tables.add(0x0100.into(), &smb_type1).unwrap(); - smb_tables.add(0x0300.into(), &smb_type4).unwrap(); - smb_tables.add(phys_mem_array_handle, &smb_type16).unwrap(); - smb_tables.add(0x1700.into(), &smb_type17).unwrap(); - smb_tables.add(0x3200.into(), &smb_type32).unwrap(); + smb_tables.add(0x0000.into(), &smbios_params.table_type0()).unwrap(); + smb_tables.add(0x0100.into(), &smbios_params.table_type1()).unwrap(); + smb_tables.add(0x0300.into(), &smbios_params.table_type4()).unwrap(); + let phys_mem_array_handle = 0x1600.into(); + smb_tables + .add(phys_mem_array_handle, &smbios_params.table_type16()) + .unwrap(); + smb_tables + .add( + 0x1700.into(), + &smbios_params.table_type17(phys_mem_array_handle), + ) + .unwrap(); + smb_tables.add(0x3200.into(), &smbios_params.table_type32()).unwrap(); smb_tables.commit() } /// Initialize qemu `fw_cfg` device, and populate it with data including CPU /// count, SMBIOS tables, and attached RAM-FB device. - /// - /// Should not be called before [`Self::initialize_rom()`]. pub fn initialize_fwcfg( &mut self, cpus: u8, + rom_size: usize, ) -> Result, Error> { let fwcfg = fwcfg::FwCfg::new(); fwcfg @@ -1086,7 +994,7 @@ impl<'a> MachineInitializer<'a> { .unwrap(); let smbios::TableBytes { entry_point, structure_table } = - self.generate_smbios(); + self.generate_smbios(rom_size); fwcfg .insert_named( "etc/smbios/smbios-tables", diff --git a/bin/propolis-server/src/lib/vm/ensure.rs b/bin/propolis-server/src/lib/vm/ensure.rs index 65c97fd14..95c24564e 100644 --- a/bin/propolis-server/src/lib/vm/ensure.rs +++ b/bin/propolis-server/src/lib/vm/ensure.rs @@ -37,9 +37,7 @@ use propolis_api_types::{ use slog::{debug, info}; use crate::{ - initializer::{ - build_instance, MachineInitializer, MachineInitializerState, - }, + initializer::{build_instance, MachineInitializer}, stats::create_kstat_sampler, vm::request_queue::InstanceAutoStart, }; @@ -180,7 +178,6 @@ impl<'a> VmEnsureNotStarted<'a> { properties, toml_config: &options.toml_config, producer_registry: options.oximeter_registry.clone(), - state: MachineInitializerState::default(), kstat_sampler: initialize_kstat_sampler( self.log, properties, @@ -189,7 +186,8 @@ impl<'a> VmEnsureNotStarted<'a> { ), }; - init.initialize_rom(options.toml_config.bootrom.as_path())?; + let rom_size = + init.initialize_rom(options.toml_config.bootrom.as_path())?; let chipset = init.initialize_chipset( &(event_queue.clone() as Arc), @@ -221,7 +219,8 @@ impl<'a> VmEnsureNotStarted<'a> { init.initialize_storage_devices(&chipset, options.nexus_client.clone()) .await?; - let ramfb = init.initialize_fwcfg(v0_spec.devices.board.cpus)?; + let ramfb = + init.initialize_fwcfg(v0_spec.devices.board.cpus, rom_size)?; init.initialize_cpus().await?; diff --git a/bin/propolis-standalone/src/main.rs b/bin/propolis-standalone/src/main.rs index 1f210b661..eb0cdef23 100644 --- a/bin/propolis-standalone/src/main.rs +++ b/bin/propolis-standalone/src/main.rs @@ -833,132 +833,6 @@ fn populate_rom( Ok(()) } -struct SmbiosParams { - memory_size: usize, - rom_size: usize, - rom_version: String, - num_cpus: u8, - cpuid_ident: Option, - cpuid_procname: Option<[cpuid::Entry; 3]>, -} -fn generate_smbios(params: SmbiosParams) -> anyhow::Result { - use smbios::table::{type0, type1, type16, type4}; - let bios_version = params - .rom_version - .try_into() - .expect("bootrom version string doesn't contain NUL bytes"); - let smb_type0 = smbios::table::Type0 { - vendor: "Oxide".try_into().unwrap(), - bios_version, - bios_release_date: "Bureaucracy 41, 3186 YOLD".try_into().unwrap(), - bios_rom_size: ((params.rom_size / (64 * 1024)) - 1) as u8, - bios_characteristics: type0::BiosCharacteristics::UNSUPPORTED, - bios_ext_characteristics: type0::BiosExtCharacteristics::ACPI - | type0::BiosExtCharacteristics::UEFI - | type0::BiosExtCharacteristics::IS_VM, - ..Default::default() - }; - - let smb_type1 = smbios::table::Type1 { - manufacturer: "Oxide".try_into().unwrap(), - product_name: "OxVM".try_into().unwrap(), - wake_up_type: type1::WakeUpType::PowerSwitch, - ..Default::default() - }; - - let cpuid_vendor = cpuid::host_query(cpuid::Ident(0x0, None)); - let cpuid_ident = params - .cpuid_ident - .unwrap_or_else(|| cpuid::host_query(cpuid::Ident(0x1, None))); - let family = match cpuid_ident.eax & 0xf00 { - // If family ID is 0xf, extended family is added to it - 0xf00 => (cpuid_ident.eax >> 20 & 0xff) + 0xf, - // ... otherwise base family ID is used - base => base >> 8, - }; - - let vendor = cpuid::VendorKind::try_from(cpuid_vendor); - let proc_manufacturer = match vendor { - Ok(cpuid::VendorKind::Intel) => "Intel", - Ok(cpuid::VendorKind::Amd) => "Advanced Micro Devices, Inc.", - _ => "", - } - .try_into() - .unwrap(); - let proc_family = match (vendor, family) { - // Zen - (Ok(cpuid::VendorKind::Amd), family) if family >= 0x17 => 0x6b, - //unknown - _ => 0x2, - }; - let proc_id = u64::from(cpuid_ident.eax) | u64::from(cpuid_ident.edx) << 32; - let procname_entries = params.cpuid_procname.or_else(|| { - if cpuid::host_query(cpuid::Ident(0x8000_0000, None)).eax >= 0x8000_0004 - { - Some([ - cpuid::host_query(cpuid::Ident(0x8000_0002, None)), - cpuid::host_query(cpuid::Ident(0x8000_0003, None)), - cpuid::host_query(cpuid::Ident(0x8000_0004, None)), - ]) - } else { - None - } - }); - let proc_version = procname_entries - .and_then(|e| cpuid::parse_brand_string(e).ok()) - .unwrap_or("".to_string()); - - let smb_type4 = smbios::table::Type4 { - proc_type: type4::ProcType::Central, - proc_family, - proc_manufacturer, - proc_id, - proc_version: proc_version.as_str().try_into().unwrap_or_default(), - status: type4::ProcStatus::Enabled, - // unknown - proc_upgrade: 0x2, - // make core and thread counts equal for now - core_count: params.num_cpus, - core_enabled: params.num_cpus, - thread_count: params.num_cpus, - proc_characteristics: type4::Characteristics::IS_64_BIT - | type4::Characteristics::MULTI_CORE, - ..Default::default() - }; - - let mut smb_type16 = smbios::table::Type16 { - location: type16::Location::SystemBoard, - array_use: type16::ArrayUse::System, - error_correction: type16::ErrorCorrection::Unknown, - num_mem_devices: 1, - ..Default::default() - }; - smb_type16.set_max_capacity(params.memory_size); - let phys_mem_array_handle = 0x1600.into(); - - let mut smb_type17 = smbios::table::Type17 { - phys_mem_array_handle, - // Unknown - form_factor: 0x2, - // Unknown - memory_type: 0x2, - ..Default::default() - }; - smb_type17.set_size(Some(params.memory_size)); - - let smb_type32 = smbios::table::Type32::default(); - - let mut smb_tables = smbios::Tables::new(0x7f00.into()); - smb_tables.add(0x0000.into(), &smb_type0).unwrap(); - smb_tables.add(0x0100.into(), &smb_type1).unwrap(); - smb_tables.add(0x0300.into(), &smb_type4).unwrap(); - smb_tables.add(phys_mem_array_handle, &smb_type16).unwrap(); - smb_tables.add(0x1700.into(), &smb_type17).unwrap(); - smb_tables.add(0x3200.into(), &smb_type32).unwrap(); - - Ok(smb_tables.commit()) -} - fn generate_bootorder(config: &config::Config) -> anyhow::Result { let names = config.main.boot_order.as_ref().unwrap(); @@ -1261,6 +1135,7 @@ fn setup_instance( let cpuid_profile = config::parse_cpuid(&config)?; + let cpuid_vendor = cpuid::host_query(cpuid::Ident(0x0, None)); let cpuid_ident = cpuid_profile .as_ref() .and_then(|p| p.get(cpuid::Ident(0x1, None))) @@ -1277,8 +1152,27 @@ fn setup_instance( }); // generate SMBIOS data and expose via fw_cfg - let smbios::TableBytes { entry_point, structure_table } = - generate_smbios(SmbiosParams { + let smbios::TableBytes { entry_point, structure_table } = { + let cpuid_ident = cpuid_ident + .unwrap_or_else(|| cpuid::host_query(cpuid::Ident(0x1, None))); + let cpuid_procname = cpuid_procname.unwrap_or_else(|| { + if cpuid::host_query(cpuid::Ident(0x8000_0000, None)).eax + >= 0x8000_0004 + { + [ + cpuid::host_query(cpuid::Ident(0x8000_0002, None)), + cpuid::host_query(cpuid::Ident(0x8000_0003, None)), + cpuid::host_query(cpuid::Ident(0x8000_0004, None)), + ] + } else { + [ + cpuid::Entry::zero(), + cpuid::Entry::zero(), + cpuid::Entry::zero(), + ] + } + }); + let smbios_params = propolis::firmware::smbios::SmbiosParams { memory_size: memsize, rom_size: rom_len, rom_version: config @@ -1286,11 +1180,32 @@ fn setup_instance( .bootrom_version .clone() .unwrap_or_else(|| "v0.0.1-alpha 1".to_string()), + rom_release_date: "Bureaucracy 41, 3186 YOLD".to_string(), num_cpus: cpus, + cpuid_vendor, cpuid_ident, cpuid_procname, - }) - .unwrap(); + system_id: uuid::Uuid::default(), + }; + + let mut smb_tables = smbios::Tables::new(0x7f00.into()); + smb_tables.add(0x0000.into(), &smbios_params.table_type0()).unwrap(); + smb_tables.add(0x0100.into(), &smbios_params.table_type1()).unwrap(); + smb_tables.add(0x0300.into(), &smbios_params.table_type4()).unwrap(); + let phys_mem_array_handle = 0x1600.into(); + smb_tables + .add(phys_mem_array_handle, &smbios_params.table_type16()) + .unwrap(); + smb_tables + .add( + 0x1700.into(), + &smbios_params.table_type17(phys_mem_array_handle), + ) + .unwrap(); + smb_tables.add(0x3200.into(), &smbios_params.table_type32()).unwrap(); + + smb_tables.commit() + }; fwcfg .insert_named( "etc/smbios/smbios-tables", diff --git a/lib/propolis/src/firmware/smbios/mod.rs b/lib/propolis/src/firmware/smbios/mod.rs index 0e8047232..226ac9a69 100644 --- a/lib/propolis/src/firmware/smbios/mod.rs +++ b/lib/propolis/src/firmware/smbios/mod.rs @@ -5,6 +5,7 @@ use std::collections::BTreeMap; use std::fmt; +use crate::cpuid; use table::{Table, Type127}; mod bits; @@ -196,3 +197,148 @@ impl TryFrom for SmbString { #[derive(thiserror::Error, Debug)] #[error("String contains NUL byte")] pub struct SmbStringNulError(); + +pub struct SmbiosParams { + pub memory_size: usize, + pub rom_size: usize, + pub rom_release_date: String, + pub rom_version: String, + pub num_cpus: u8, + pub cpuid_vendor: cpuid::Entry, + pub cpuid_ident: cpuid::Entry, + pub cpuid_procname: [cpuid::Entry; 3], + pub system_id: uuid::Uuid, +} + +impl SmbiosParams { + pub fn table_type0(&self) -> table::Type0 { + use table::type0; + + let bios_version = self + .rom_version + .as_str() + .try_into() + .expect("bootrom version string doesn't contain NUL bytes"); + table::Type0 { + vendor: "Oxide".try_into().unwrap(), + bios_version, + bios_release_date: self + .rom_release_date + .as_str() + .try_into() + .unwrap(), + bios_rom_size: ((self.rom_size / (64 * 1024)) - 1) as u8, + bios_characteristics: type0::BiosCharacteristics::UNSUPPORTED, + bios_ext_characteristics: type0::BiosExtCharacteristics::ACPI + | type0::BiosExtCharacteristics::UEFI + | type0::BiosExtCharacteristics::IS_VM, + ..Default::default() + } + } + + pub fn table_type1(&self) -> table::Type1 { + use table::type1; + + table::Type1 { + manufacturer: "Oxide".try_into().unwrap(), + product_name: "OxVM".try_into().unwrap(), + + serial_number: self + .system_id + .to_string() + .try_into() + .unwrap_or_default(), + uuid: self.system_id.to_bytes_le(), + + wake_up_type: type1::WakeUpType::PowerSwitch, + ..Default::default() + } + } + + pub fn table_type4(&self) -> table::Type4 { + use table::type4; + + let family = match self.cpuid_ident.eax & 0xf00 { + // If family ID is 0xf, extended family is added to it + 0xf00 => (self.cpuid_ident.eax >> 20 & 0xff) + 0xf, + // ... otherwise base family ID is used + base => base >> 8, + }; + + let vendor = cpuid::VendorKind::try_from(self.cpuid_vendor); + let proc_manufacturer = match vendor { + Ok(cpuid::VendorKind::Intel) => "Intel", + Ok(cpuid::VendorKind::Amd) => "Advanced Micro Devices, Inc.", + _ => "", + } + .try_into() + .unwrap(); + let proc_family = match (vendor, family) { + // Explicitly match for Zen-based CPUs + // + // Although this family identifier is not valid in SMBIOS 2.7, + // having been defined in 3.x, we pass it through anyways. + (Ok(cpuid::VendorKind::Amd), family) if family >= 0x17 => 0x6b, + + // Emit Unknown for everything else + _ => 0x2, + }; + let proc_id = u64::from(self.cpuid_ident.eax) + | u64::from(self.cpuid_ident.edx) << 32; + // TODO(ixi): do not ignore the error here + let proc_version = cpuid::parse_brand_string(self.cpuid_procname) + .unwrap_or_else(|_| "".to_string()); + + table::Type4 { + proc_type: type4::ProcType::Central, + proc_family, + proc_manufacturer, + proc_id, + proc_version: proc_version.as_str().try_into().unwrap_or_default(), + status: type4::ProcStatus::Enabled, + // unknown + proc_upgrade: 0x2, + // make core and thread counts equal for now + core_count: self.num_cpus, + core_enabled: self.num_cpus, + thread_count: self.num_cpus, + proc_characteristics: type4::Characteristics::IS_64_BIT + | type4::Characteristics::MULTI_CORE, + ..Default::default() + } + } + + pub fn table_type16(&self) -> table::Type16 { + use table::type16; + + let mut smb_type16 = table::Type16 { + location: type16::Location::SystemBoard, + array_use: type16::ArrayUse::System, + error_correction: type16::ErrorCorrection::Unknown, + num_mem_devices: 1, + ..Default::default() + }; + smb_type16.set_max_capacity(self.memory_size); + smb_type16 + } + + pub fn table_type17(&self, table16_handle: Handle) -> table::Type17 { + let phys_mem_array_handle = table16_handle.into(); + + let mut smb_type17 = table::Type17 { + phys_mem_array_handle, + // Unknown + form_factor: 0x2, + // Unknown + memory_type: 0x2, + ..Default::default() + }; + smb_type17.set_size(Some(self.memory_size)); + smb_type17 + } + + pub fn table_type32(&self) -> table::Type32 { + // We don't yet set anything interesting into table type 32. + table::Type32::default() + } +}