@@ -28,12 +28,18 @@ use propolis::hw::ibmpc;
2828use propolis:: hw:: pci;
2929use propolis:: hw:: ps2:: ctrl:: PS2Ctrl ;
3030use propolis:: hw:: qemu:: pvpanic:: QemuPvpanic ;
31- use propolis:: hw:: qemu:: { debug:: QemuDebugPort , fwcfg, ramfb} ;
31+ use propolis:: hw:: qemu:: {
32+ debug:: QemuDebugPort ,
33+ fwcfg:: { self , Entry } ,
34+ ramfb,
35+ } ;
3236use propolis:: hw:: uart:: LpcUart ;
3337use propolis:: hw:: { nvme, virtio} ;
3438use propolis:: intr_pins;
3539use propolis:: vmm:: { self , Builder , Machine } ;
36- use propolis_api_types:: instance_spec:: { self , v0:: InstanceSpecV0 } ;
40+ use propolis_api_types:: instance_spec:: {
41+ self , v0:: BootDeclaration , v0:: InstanceSpecV0 ,
42+ } ;
3743use propolis_api_types:: InstanceProperties ;
3844use slog:: info;
3945
@@ -117,6 +123,7 @@ pub struct MachineInitializer<'a> {
117123 pub ( crate ) properties : & ' a InstanceProperties ,
118124 pub ( crate ) toml_config : & ' a crate :: server:: VmTomlConfig ,
119125 pub ( crate ) producer_registry : Option < ProducerRegistry > ,
126+ pub ( crate ) boot_order : Option < Vec < BootDeclaration > > ,
120127 pub ( crate ) state : MachineInitializerState ,
121128}
122129
@@ -994,6 +1001,121 @@ impl<'a> MachineInitializer<'a> {
9941001 smb_tables. commit ( )
9951002 }
9961003
1004+ fn generate_bootorder ( & self ) -> Result < Option < Entry > , Error > {
1005+ eprintln ! (
1006+ "generating bootorder with order: {:?}" ,
1007+ self . boot_order. as_ref( )
1008+ ) ;
1009+ let Some ( boot_names) = self . boot_order . as_ref ( ) else {
1010+ return Ok ( None ) ;
1011+ } ;
1012+
1013+ let mut order = fwcfg:: formats:: BootOrder :: new ( ) ;
1014+
1015+ let parse_bdf =
1016+ |pci_path : & propolis_api_types:: instance_spec:: PciPath | {
1017+ let bdf: Result < pci:: Bdf , Error > =
1018+ pci_path. to_owned ( ) . try_into ( ) . map_err ( |e| {
1019+ Error :: new (
1020+ ErrorKind :: InvalidInput ,
1021+ format ! (
1022+ "Couldn't get PCI BDF for {}: {}" ,
1023+ pci_path, e
1024+ ) ,
1025+ )
1026+ } ) ;
1027+
1028+ bdf
1029+ } ;
1030+
1031+ for boot_entry in boot_names. iter ( ) {
1032+ if boot_entry. first_boot_only {
1033+ continue ;
1034+ }
1035+ // names may refer to a storage device or network device.
1036+ //
1037+ // realistically we won't be booting from network devices, but leave that as a question
1038+ // for plumbing on the next layer up.
1039+
1040+ let storage_backend = |spec| {
1041+ let spec: & instance_spec:: v0:: StorageDeviceV0 = spec;
1042+ match spec {
1043+ instance_spec:: v0:: StorageDeviceV0 :: VirtioDisk ( disk) => {
1044+ & disk. backend_name
1045+ }
1046+ instance_spec:: v0:: StorageDeviceV0 :: NvmeDisk ( disk) => {
1047+ & disk. backend_name
1048+ }
1049+ }
1050+ } ;
1051+
1052+ let network_backend = |spec| {
1053+ let spec: & instance_spec:: v0:: NetworkDeviceV0 = spec;
1054+ let instance_spec:: v0:: NetworkDeviceV0 :: VirtioNic ( vnic_spec) =
1055+ spec;
1056+
1057+ & vnic_spec. backend_name
1058+ } ;
1059+
1060+ if let Some ( device_spec) =
1061+ self . spec . devices . storage_devices . iter ( ) . find_map (
1062+ |( _name, spec) | {
1063+ if storage_backend ( spec) == & boot_entry. name {
1064+ Some ( spec)
1065+ } else {
1066+ None
1067+ }
1068+ } ,
1069+ )
1070+ {
1071+ match device_spec {
1072+ instance_spec:: v0:: StorageDeviceV0 :: VirtioDisk ( disk) => {
1073+ let bdf = parse_bdf ( & disk. pci_path ) ?;
1074+ // TODO: check that bus is 0. only support boot devices
1075+ // directly attached to the root bus for now.
1076+ order. add_disk ( bdf. location ) ;
1077+ }
1078+ instance_spec:: v0:: StorageDeviceV0 :: NvmeDisk ( disk) => {
1079+ let bdf = parse_bdf ( & disk. pci_path ) ?;
1080+ // TODO: check that bus is 0. only support boot devices
1081+ // directly attached to the root bus for now.
1082+ //
1083+ // TODO: separately, propolis-standalone passes an eui64
1084+ // of 0, so do that here too. is that.. ok?
1085+ order. add_nvme ( bdf. location , 0 ) ;
1086+ }
1087+ } ;
1088+ } else if let Some ( vnic_spec) =
1089+ self . spec . devices . network_devices . iter ( ) . find_map (
1090+ |( name, spec) | {
1091+ if network_backend ( spec) == & boot_entry. name {
1092+ Some ( spec)
1093+ } else {
1094+ None
1095+ }
1096+ } ,
1097+ )
1098+ {
1099+ let instance_spec:: v0:: NetworkDeviceV0 :: VirtioNic ( vnic_spec) =
1100+ vnic_spec;
1101+
1102+ let bdf = parse_bdf ( & vnic_spec. pci_path ) ?;
1103+
1104+ order. add_pci ( bdf. location , "ethernet" ) ;
1105+ } else {
1106+ eprintln ! (
1107+ "could not find {:?} in {:?} or {:?}" ,
1108+ boot_entry,
1109+ self . spec. devices. storage_devices,
1110+ self . spec. devices. network_devices
1111+ ) ;
1112+ panic ! ( "TODO: return an error; the device name doesn't exist?" ) ;
1113+ }
1114+ }
1115+
1116+ Ok ( Some ( order. finish ( ) ) )
1117+ }
1118+
9971119 /// Initialize qemu `fw_cfg` device, and populate it with data including CPU
9981120 /// count, SMBIOS tables, and attached RAM-FB device.
9991121 ///
@@ -1010,6 +1132,13 @@ impl<'a> MachineInitializer<'a> {
10101132 )
10111133 . unwrap ( ) ;
10121134
1135+ // TODO(ixi): extract `generate_smbios` and expect `smbios::TableBytes` as an argument to
1136+ // initialize_fwcfg. make `generate_smbios` accept rom size as an explicit parameter. this
1137+ // avoids the implicit panic if these are called out of order (and makes it harder to call
1138+ // out of order).
1139+ //
1140+ // one step towards sharing smbios init code between propolis-standalone and
1141+ // propolis-server.
10131142 let smbios:: TableBytes { entry_point, structure_table } =
10141143 self . generate_smbios ( ) ;
10151144 fwcfg
@@ -1025,6 +1154,10 @@ impl<'a> MachineInitializer<'a> {
10251154 )
10261155 . unwrap ( ) ;
10271156
1157+ if let Some ( boot_order) = self . generate_bootorder ( ) ? {
1158+ fwcfg. insert_named ( "bootorder" , boot_order) . unwrap ( ) ;
1159+ }
1160+
10281161 let ramfb = ramfb:: RamFb :: create (
10291162 self . log . new ( slog:: o!( "component" => "ramfb" ) ) ,
10301163 ) ;
0 commit comments