Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions bin/propolis-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use propolis_client::types::{
InstanceEnsureRequest, InstanceInitializationMethod, InstanceMetadata,
InstanceSpecGetResponse,
};
use propolis_config_toml::spec::toml_cpuid_to_spec_cpuid;
use propolis_config_toml::spec::SpecConfig;
use serde::{Deserialize, Serialize};
use slog::{o, Drain, Level, Logger};
Expand Down Expand Up @@ -66,6 +67,9 @@ struct Opt {
cmd: Command,
}

// `New`, via `VmConfig`, is large enough to trip this lint. This enum is
// created exactly once, so we don't need to be picky about the layout..
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
enum Command {
/// Create a new propolis instance
Expand Down Expand Up @@ -180,6 +184,12 @@ struct VmConfig {
#[clap(short, default_value = "1024", action, requires = "config_toml")]
memory: u64,

/// CPUID profile to use.
///
/// The named profile must be defined in `config_toml`.
#[clap(long, requires = "config_toml")]
cpuid_profile: Option<String>,

/// A path to a file containing a config TOML
#[clap(short = 't', long, action, group = "config_group", requires_all = ["vcpus", "memory"])]
config_toml: Option<PathBuf>,
Expand Down Expand Up @@ -288,22 +298,39 @@ impl VmConfig {
return parse_json_file(path);
}

let from_toml = &self
let parsed_toml = self
.config_toml
.as_ref()
.map(propolis_config_toml::parse)
.transpose()?
.as_ref()
.map(SpecConfig::try_from)
.transpose()?;

let from_toml =
parsed_toml.as_ref().map(SpecConfig::try_from).transpose()?;

let enable_pcie =
from_toml.as_ref().map(|cfg| cfg.enable_pcie).unwrap_or(false);

let cpuid_profile = parsed_toml
.as_ref()
.and_then(|cfg| {
self.cpuid_profile.as_ref().map(|profile| {
cfg.cpuid_profiles
.get(profile)
.ok_or_else(|| {
anyhow!("CPUID profile not defined: {}", profile)
})
.and_then(|profile| {
toml_cpuid_to_spec_cpuid(profile)
.map_err(Into::into)
})
})
})
.transpose()?;

let mut spec = InstanceSpecV0 {
board: Board {
chipset: Chipset::I440Fx(I440Fx { enable_pcie }),
cpuid: None,
cpuid: cpuid_profile,
cpus: self.vcpus,
memory_mb: self.memory,
guest_hv_interface: if self.hyperv {
Expand Down
4 changes: 3 additions & 1 deletion crates/propolis-config-toml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use std::str::FromStr;
use serde_derive::{Deserialize, Serialize};
use thiserror::Error;

pub use cpuid_profile_config::CpuidProfile;
pub use cpuid_profile_config::{
CpuVendor, CpuidEntry, CpuidParseError, CpuidProfile,
};

pub mod spec;

Expand Down
45 changes: 41 additions & 4 deletions crates/propolis-config-toml/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use std::{

use propolis_client::{
instance_spec::{
ComponentV0, DlpiNetworkBackend, FileStorageBackend,
MigrationFailureInjector, NvmeDisk, P9fs, PciPath, PciPciBridge,
SoftNpuP9, SoftNpuPciPort, SoftNpuPort, SpecKey, VirtioDisk,
VirtioNetworkBackend, VirtioNic,
ComponentV0, Cpuid, CpuidVendor, DlpiNetworkBackend,
FileStorageBackend, MigrationFailureInjector, NvmeDisk, P9fs, PciPath,
PciPciBridge, SoftNpuP9, SoftNpuPciPort, SoftNpuPort, SpecKey,
VirtioDisk, VirtioNetworkBackend, VirtioNic,
},
support::nvme_serial_from_str,
};
Expand Down Expand Up @@ -430,3 +430,40 @@ fn parse_p9fs_from_config(
pci_path,
})
}

/// Translate a parsed TOML-provided `CpuidEntry` into a `propolis-server`
/// API-style `CpuidEntry`.
///
/// The transformation here is trivial. Using the API-style `CpuidEntry` for the
/// TOML definition would make for clumsier text, though, so they're defined
/// slightly differently for the different use cases.
fn translate_cpuid_entry(
toml_entry: super::CpuidEntry,
) -> propolis_client::instance_spec::CpuidEntry {
let super::CpuidEntry { func, idx, values: [eax, ebx, ecx, edx] } =
toml_entry;

propolis_client::instance_spec::CpuidEntry {
leaf: func,
subleaf: idx,
eax,
ebx,
ecx,
edx,
}
}

/// Not a `TryFrom` or `TryInto` because we're re-exporting types from
/// `cpuid-profile-config`, so they're actually defined in a foreign crate.
pub fn toml_cpuid_to_spec_cpuid(
profile: &super::CpuidProfile,
) -> Result<Cpuid, super::CpuidParseError> {
let entries = Vec::<super::CpuidEntry>::try_from(profile)?;
let entries = entries.into_iter().map(translate_cpuid_entry).collect();

let vendor = match profile.vendor {
super::CpuVendor::Amd => CpuidVendor::Amd,
super::CpuVendor::Intel => CpuidVendor::Intel,
};
Ok(Cpuid { entries, vendor })
}
1 change: 1 addition & 0 deletions lib/propolis-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ progenitor::generate_api!(
ReplacementComponent = crate::instance_spec::ReplacementComponent,
InstanceSpecV0 = crate::instance_spec::InstanceSpecV0,
VersionedInstanceSpec = crate::instance_spec::VersionedInstanceSpec,
CpuidEntry = crate::instance_spec::CpuidEntry,
},
// Automatically derive JsonSchema for instance spec-related types so that
// they can be reused in sled-agent's API. This can't be done with a
Expand Down
Loading