pvmfw: Support instance.img for storing DICE salt
On boot, attempt to read the DICE hidden input (salt) from persistent
storage, in order to provide persistence of secrets across reboots of
the same kernel/ramdisk images. To ensure that the salt is only re-used
with the same preloaded images signed by the same authority, store the
other DICE inputs so that they can be verified to match on the next
boot.
Implement support for parsing GUID Partition Tables (GPT) on top of
virtio-blk, to locate, read from, and write to named partitions.
Implement support for the instance.img format, defined by the AVF team,
and conceptually acting as a filesystem within the GPT partition named
"vm-instance", where each stage of the pVM boot process owns an entry
(sometimes called a partition), identified through a UUID.
As the host is in charge of providing the file backing the virtual disk,
the content of the instance.img entry will be encrypted in a following
commit.
Bug: 262344886
Test: atest MicrodroidHostTests
Change-Id: Ic43bb7780b5e106002590f9c97cd900c3ff5e3d9
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 7561800..193ffa9 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -21,7 +21,9 @@
"libonce_cell_nostd",
"libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
+ "libstatic_assertions",
"libtinyvec_nostd",
+ "libuuid_nostd",
"libvirtio_drivers",
"libvmbase",
"libzeroize_nostd",
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index 14f522f..3ceb8ef 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -42,9 +42,9 @@
}
pub struct PartialInputs {
- code_hash: Hash,
- auth_hash: Hash,
- mode: DiceMode,
+ pub code_hash: Hash,
+ pub auth_hash: Hash,
+ pub mode: DiceMode,
}
impl PartialInputs {
diff --git a/pvmfw/src/gpt.rs b/pvmfw/src/gpt.rs
new file mode 100644
index 0000000..6af3047
--- /dev/null
+++ b/pvmfw/src/gpt.rs
@@ -0,0 +1,253 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Support for parsing GUID partition tables.
+
+use crate::helpers::ceiling_div;
+use crate::virtio::pci::VirtIOBlk;
+use core::cmp::min;
+use core::fmt;
+use core::mem::size_of;
+use core::ops::RangeInclusive;
+use core::slice;
+use static_assertions::const_assert;
+use static_assertions::const_assert_eq;
+use uuid::Uuid;
+use virtio_drivers::device::blk::SECTOR_SIZE;
+
+pub enum Error {
+ /// VirtIO error during read operation.
+ FailedRead(virtio_drivers::Error),
+ /// VirtIO error during write operation.
+ FailedWrite(virtio_drivers::Error),
+ /// Invalid GPT header.
+ InvalidHeader,
+ /// Invalid partition block index.
+ BlockOutsidePartition(usize),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::FailedRead(e) => write!(f, "Failed to read from disk: {e}"),
+ Self::FailedWrite(e) => write!(f, "Failed to write to disk: {e}"),
+ Self::InvalidHeader => write!(f, "Found invalid GPT header"),
+ Self::BlockOutsidePartition(i) => write!(f, "Accessed invalid block index {i}"),
+ }
+ }
+}
+
+pub type Result<T> = core::result::Result<T, Error>;
+
+pub struct Partition {
+ partitions: Partitions,
+ indices: RangeInclusive<usize>,
+}
+
+impl Partition {
+ pub fn get_by_name(device: VirtIOBlk, name: &str) -> Result<Option<Self>> {
+ Partitions::new(device)?.get_partition_by_name(name)
+ }
+
+ fn new(partitions: Partitions, entry: &Entry) -> Self {
+ let first = entry.first_lba().try_into().unwrap();
+ let last = entry.last_lba().try_into().unwrap();
+
+ Self { partitions, indices: first..=last }
+ }
+
+ pub fn indices(&self) -> RangeInclusive<usize> {
+ self.indices.clone()
+ }
+
+ pub fn read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()> {
+ let index = self.block_index(index).ok_or(Error::BlockOutsidePartition(index))?;
+ self.partitions.read_block(index, blk)
+ }
+
+ pub fn write_block(&mut self, index: usize, blk: &[u8]) -> Result<()> {
+ let index = self.block_index(index).ok_or(Error::BlockOutsidePartition(index))?;
+ self.partitions.write_block(index, blk)
+ }
+
+ fn block_index(&self, index: usize) -> Option<usize> {
+ if self.indices.contains(&index) {
+ Some(index)
+ } else {
+ None
+ }
+ }
+}
+
+pub struct Partitions {
+ device: VirtIOBlk,
+ entries_count: usize,
+}
+
+impl Partitions {
+ pub const LBA_SIZE: usize = SECTOR_SIZE;
+
+ fn new(mut device: VirtIOBlk) -> Result<Self> {
+ let mut blk = [0; Self::LBA_SIZE];
+ device.read_block(Header::LBA, &mut blk).map_err(Error::FailedRead)?;
+ let (header_bytes, _) = blk.split_at(size_of::<Header>());
+ let header = Header::from_bytes(header_bytes).ok_or(Error::InvalidHeader)?;
+ let entries_count = usize::try_from(header.entries_count()).unwrap();
+
+ Ok(Self { device, entries_count })
+ }
+
+ fn get_partition_by_name(mut self, name: &str) -> Result<Option<Partition>> {
+ const_assert_eq!(Partitions::LBA_SIZE.rem_euclid(size_of::<Entry>()), 0);
+ let entries_per_blk = Partitions::LBA_SIZE.checked_div(size_of::<Entry>()).unwrap();
+
+ // Create a UTF-16 reference against which we'll compare partition names. Note that unlike
+ // the C99 wcslen(), this comparison will cover bytes past the first L'\0' character.
+ let mut needle = [0; Entry::NAME_SIZE / size_of::<u16>()];
+ for (dest, src) in needle.iter_mut().zip(name.encode_utf16()) {
+ *dest = src;
+ }
+
+ let mut blk = [0; Self::LBA_SIZE];
+ let mut rem = self.entries_count;
+ let num_blocks = ceiling_div(self.entries_count, entries_per_blk).unwrap();
+ for i in Header::ENTRIES_LBA..Header::ENTRIES_LBA.checked_add(num_blocks).unwrap() {
+ self.read_block(i, &mut blk)?;
+ let entries = blk.as_ptr().cast::<Entry>();
+ // SAFETY - blk is assumed to be properly aligned for Entry and its size is assert-ed
+ // above. All potential values of the slice will produce valid Entry values.
+ let entries = unsafe { slice::from_raw_parts(entries, min(rem, entries_per_blk)) };
+ for entry in entries {
+ let entry_name = entry.name;
+ if entry_name == needle {
+ return Ok(Some(Partition::new(self, entry)));
+ }
+ rem -= 1;
+ }
+ }
+ Ok(None)
+ }
+
+ fn read_block(&mut self, index: usize, blk: &mut [u8]) -> Result<()> {
+ self.device.read_block(index, blk).map_err(Error::FailedRead)
+ }
+
+ fn write_block(&mut self, index: usize, blk: &[u8]) -> Result<()> {
+ self.device.write_block(index, blk).map_err(Error::FailedWrite)
+ }
+}
+
+type Lba = u64;
+
+/// Structure as defined in release 2.10 of the UEFI Specification (5.3.2 GPT Header).
+#[repr(C, packed)]
+struct Header {
+ signature: u64,
+ revision: u32,
+ header_size: u32,
+ header_crc32: u32,
+ reserved0: u32,
+ current_lba: Lba,
+ backup_lba: Lba,
+ first_lba: Lba,
+ last_lba: Lba,
+ disk_guid: Uuid,
+ entries_lba: Lba,
+ entries_count: u32,
+ entry_size: u32,
+ entries_crc32: u32,
+}
+const_assert!(size_of::<Header>() < Partitions::LBA_SIZE);
+
+impl Header {
+ const SIGNATURE: u64 = u64::from_le_bytes(*b"EFI PART");
+ const REVISION_1_0: u32 = 1 << 16;
+ const LBA: usize = 1;
+ const ENTRIES_LBA: usize = 2;
+
+ fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+ let bytes = bytes.get(..size_of::<Self>())?;
+ // SAFETY - We assume that bytes is properly aligned for Header and have verified above
+ // that it holds enough bytes. All potential values of the slice will produce a valid
+ // Header.
+ let header = unsafe { &*bytes.as_ptr().cast::<Self>() };
+
+ if header.is_valid() {
+ Some(header)
+ } else {
+ None
+ }
+ }
+
+ fn is_valid(&self) -> bool {
+ self.signature() == Self::SIGNATURE
+ && self.header_size() == size_of::<Self>().try_into().unwrap()
+ && self.revision() == Self::REVISION_1_0
+ && self.entry_size() == size_of::<Entry>().try_into().unwrap()
+ && self.current_lba() == Self::LBA.try_into().unwrap()
+ && self.entries_lba() == Self::ENTRIES_LBA.try_into().unwrap()
+ }
+
+ fn signature(&self) -> u64 {
+ u64::from_le(self.signature)
+ }
+
+ fn entries_count(&self) -> u32 {
+ u32::from_le(self.entries_count)
+ }
+
+ fn header_size(&self) -> u32 {
+ u32::from_le(self.header_size)
+ }
+
+ fn revision(&self) -> u32 {
+ u32::from_le(self.revision)
+ }
+
+ fn entry_size(&self) -> u32 {
+ u32::from_le(self.entry_size)
+ }
+
+ fn entries_lba(&self) -> Lba {
+ Lba::from_le(self.entries_lba)
+ }
+
+ fn current_lba(&self) -> Lba {
+ Lba::from_le(self.current_lba)
+ }
+}
+
+/// Structure as defined in release 2.10 of the UEFI Specification (5.3.3 GPT Partition Entry
+/// Array).
+#[repr(C, packed)]
+struct Entry {
+ type_guid: Uuid,
+ guid: Uuid,
+ first_lba: Lba,
+ last_lba: Lba,
+ flags: u64,
+ name: [u16; Entry::NAME_SIZE / size_of::<u16>()], // UTF-16
+}
+
+impl Entry {
+ const NAME_SIZE: usize = 72;
+
+ fn first_lba(&self) -> Lba {
+ Lba::from_le(self.first_lba)
+ }
+
+ fn last_lba(&self) -> Lba {
+ Lba::from_le(self.last_lba)
+ }
+}
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index 40266f7..e6e3406 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -47,6 +47,17 @@
}
}
+/// Performs an integer division rounding up.
+///
+/// Note: Returns None if den isn't a power of two.
+pub const fn ceiling_div(num: usize, den: usize) -> Option<usize> {
+ let Some(r) = align_up(num, den) else {
+ return None;
+ };
+
+ r.checked_div(den)
+}
+
/// Aligns the given address to the given alignment, if it is a power of two.
///
/// Returns `None` if the alignment isn't a power of two.
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
new file mode 100644
index 0000000..3657fb6
--- /dev/null
+++ b/pvmfw/src/instance.rs
@@ -0,0 +1,305 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Support for reading and writing to the instance.img.
+
+use crate::dice::PartialInputs;
+use crate::gpt;
+use crate::gpt::Partition;
+use crate::gpt::Partitions;
+use crate::helpers::ceiling_div;
+use crate::virtio::pci::VirtIOBlkIterator;
+use core::fmt;
+use core::mem::size_of;
+use core::slice;
+use diced_open_dice::DiceMode;
+use diced_open_dice::Hash;
+use diced_open_dice::Hidden;
+use log::trace;
+use uuid::Uuid;
+use virtio_drivers::transport::pci::bus::PciRoot;
+
+pub enum Error {
+ /// Unexpected I/O error while accessing the underlying disk.
+ FailedIo(gpt::Error),
+ /// Impossible to create a new instance.img entry.
+ InstanceImageFull,
+ /// Badly formatted instance.img header block.
+ InvalidInstanceImageHeader,
+ /// No instance.img ("vm-instance") partition found.
+ MissingInstanceImage,
+ /// The instance.img doesn't contain a header.
+ MissingInstanceImageHeader,
+ /// Authority hash found in the pvmfw instance.img entry doesn't match the trusted public key.
+ RecordedAuthHashMismatch,
+ /// Code hash found in the pvmfw instance.img entry doesn't match the inputs.
+ RecordedCodeHashMismatch,
+ /// DICE mode found in the pvmfw instance.img entry doesn't match the current one.
+ RecordedDiceModeMismatch,
+ /// Size of the instance.img entry being read or written is not supported.
+ UnsupportedEntrySize(usize),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::FailedIo(e) => write!(f, "Failed I/O to disk: {e}"),
+ Self::InstanceImageFull => write!(f, "Failed to obtain a free instance.img partition"),
+ Self::InvalidInstanceImageHeader => write!(f, "instance.img header is invalid"),
+ Self::MissingInstanceImage => write!(f, "Failed to find the instance.img partition"),
+ Self::MissingInstanceImageHeader => write!(f, "instance.img header is missing"),
+ Self::RecordedAuthHashMismatch => write!(f, "Recorded authority hash doesn't match"),
+ Self::RecordedCodeHashMismatch => write!(f, "Recorded code hash doesn't match"),
+ Self::RecordedDiceModeMismatch => write!(f, "Recorded DICE mode doesn't match"),
+ Self::UnsupportedEntrySize(sz) => write!(f, "Invalid entry size: {sz}"),
+ }
+ }
+}
+
+pub type Result<T> = core::result::Result<T, Error>;
+
+pub fn get_or_generate_instance_salt(
+ pci_root: &mut PciRoot,
+ dice_inputs: &PartialInputs,
+) -> Result<(bool, Hidden)> {
+ let mut instance_img = find_instance_img(pci_root)?;
+
+ let entry = locate_entry(&mut instance_img)?;
+ trace!("Found pvmfw instance.img entry: {entry:?}");
+
+ let mut blk = [0; BLK_SIZE];
+ match entry {
+ PvmfwEntry::Existing { header_index, payload_size } => {
+ if payload_size > blk.len() {
+ // We currently only support single-blk entries.
+ return Err(Error::UnsupportedEntrySize(payload_size));
+ }
+ let payload_index = header_index + 1;
+ instance_img.read_block(payload_index, &mut blk).map_err(Error::FailedIo)?;
+
+ let payload = &blk[..payload_size]; // TODO(b/249723852): Decrypt entries.
+
+ let body: &EntryBody = payload.as_ref();
+ if body.code_hash != dice_inputs.code_hash {
+ Err(Error::RecordedCodeHashMismatch)
+ } else if body.auth_hash != dice_inputs.auth_hash {
+ Err(Error::RecordedAuthHashMismatch)
+ } else if body.mode() != dice_inputs.mode {
+ Err(Error::RecordedDiceModeMismatch)
+ } else {
+ Ok((false, body.salt))
+ }
+ }
+ PvmfwEntry::New { header_index } => {
+ let salt = [0; size_of::<Hidden>()]; // TODO(b/262393451): Generate using TRNG.
+ let entry_body = EntryBody::new(dice_inputs, &salt);
+ let body = entry_body.as_ref();
+ // We currently only support single-blk entries.
+ assert!(body.len() < blk.len());
+
+ let payload_size = body.len();
+ blk[..payload_size].copy_from_slice(body); // TODO(b/249723852): Encrypt entries.
+ let payload_index = header_index + 1;
+ instance_img.write_block(payload_index, &blk).map_err(Error::FailedIo)?;
+
+ let header = EntryHeader::new(PvmfwEntry::UUID, payload_size);
+ let (blk_header, blk_rest) = blk.split_at_mut(size_of::<EntryHeader>());
+ blk_header.copy_from_slice(header.as_ref());
+ blk_rest.fill(0);
+ instance_img.write_block(header_index, &blk).map_err(Error::FailedIo)?;
+
+ Ok((true, salt))
+ }
+ }
+}
+
+#[repr(C, packed)]
+struct Header {
+ magic: [u8; Header::MAGIC.len()],
+ version: u16,
+}
+
+impl Header {
+ const MAGIC: &[u8] = b"Android-VM-instance";
+ const VERSION_1: u16 = 1;
+
+ pub fn is_valid(&self) -> bool {
+ self.magic == Self::MAGIC && self.version() == Self::VERSION_1
+ }
+
+ fn version(&self) -> u16 {
+ u16::from_le(self.version)
+ }
+
+ fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+ let header: &Self = bytes.as_ref();
+
+ if header.is_valid() {
+ Some(header)
+ } else {
+ None
+ }
+ }
+}
+
+impl AsRef<Header> for [u8] {
+ fn as_ref(&self) -> &Header {
+ // SAFETY - Assume that the alignement and size match Header.
+ unsafe { &*self.as_ptr().cast::<Header>() }
+ }
+}
+
+fn find_instance_img(pci_root: &mut PciRoot) -> Result<Partition> {
+ for device in VirtIOBlkIterator::new(pci_root) {
+ match Partition::get_by_name(device, "vm-instance") {
+ Ok(Some(p)) => return Ok(p),
+ Ok(None) => {}
+ Err(e) => log::warn!("error while reading from disk: {e}"),
+ };
+ }
+
+ Err(Error::MissingInstanceImage)
+}
+
+#[derive(Debug)]
+enum PvmfwEntry {
+ Existing { header_index: usize, payload_size: usize },
+ New { header_index: usize },
+}
+
+const BLK_SIZE: usize = Partitions::LBA_SIZE;
+
+impl PvmfwEntry {
+ const UUID: Uuid = Uuid::from_u128(0x90d2174a038a4bc6adf3824848fc5825);
+}
+
+fn locate_entry(partition: &mut Partition) -> Result<PvmfwEntry> {
+ let mut blk = [0; BLK_SIZE];
+ let mut indices = partition.indices();
+ let header_index = indices.next().ok_or(Error::MissingInstanceImageHeader)?;
+ partition.read_block(header_index, &mut blk).map_err(Error::FailedIo)?;
+ // The instance.img header is only used for discovery/validation.
+ let _ = Header::from_bytes(&blk).ok_or(Error::InvalidInstanceImageHeader)?;
+
+ while let Some(header_index) = indices.next() {
+ partition.read_block(header_index, &mut blk).map_err(Error::FailedIo)?;
+
+ let header: &EntryHeader = blk[..size_of::<EntryHeader>()].as_ref();
+ match (header.uuid(), header.payload_size()) {
+ (uuid, _) if uuid.is_nil() => return Ok(PvmfwEntry::New { header_index }),
+ (PvmfwEntry::UUID, payload_size) => {
+ return Ok(PvmfwEntry::Existing { header_index, payload_size })
+ }
+ (uuid, payload_size) => {
+ trace!("Skipping instance.img entry {uuid}: {payload_size:?} bytes");
+ let n = ceiling_div(payload_size, BLK_SIZE).unwrap();
+ if n > 0 {
+ let _ = indices.nth(n - 1); // consume
+ }
+ }
+ };
+ }
+
+ Err(Error::InstanceImageFull)
+}
+
+/// Marks the start of an instance.img entry.
+///
+/// Note: Virtualization/microdroid_manager/src/instance.rs uses the name "partition".
+#[repr(C)]
+struct EntryHeader {
+ uuid: u128,
+ payload_size: u64,
+}
+
+impl EntryHeader {
+ fn new(uuid: Uuid, payload_size: usize) -> Self {
+ Self { uuid: uuid.as_u128(), payload_size: u64::try_from(payload_size).unwrap().to_le() }
+ }
+
+ fn uuid(&self) -> Uuid {
+ Uuid::from_u128(self.uuid)
+ }
+
+ fn payload_size(&self) -> usize {
+ usize::try_from(u64::from_le(self.payload_size)).unwrap()
+ }
+}
+
+impl AsRef<EntryHeader> for [u8] {
+ fn as_ref(&self) -> &EntryHeader {
+ assert_eq!(self.len(), size_of::<EntryHeader>());
+ // SAFETY - The size of the slice was checked and any value may be considered valid.
+ unsafe { &*self.as_ptr().cast::<EntryHeader>() }
+ }
+}
+
+impl AsRef<[u8]> for EntryHeader {
+ fn as_ref(&self) -> &[u8] {
+ let s = self as *const Self;
+ // SAFETY - Transmute the (valid) bytes into a slice.
+ unsafe { slice::from_raw_parts(s.cast::<u8>(), size_of::<Self>()) }
+ }
+}
+
+#[repr(C)]
+struct EntryBody {
+ code_hash: Hash,
+ auth_hash: Hash,
+ salt: Hidden,
+ mode: u8,
+}
+
+impl EntryBody {
+ fn new(dice_inputs: &PartialInputs, salt: &Hidden) -> Self {
+ let mode = match dice_inputs.mode {
+ DiceMode::kDiceModeNotInitialized => 0,
+ DiceMode::kDiceModeNormal => 1,
+ DiceMode::kDiceModeDebug => 2,
+ DiceMode::kDiceModeMaintenance => 3,
+ };
+
+ Self {
+ code_hash: dice_inputs.code_hash,
+ auth_hash: dice_inputs.auth_hash,
+ salt: *salt,
+ mode,
+ }
+ }
+
+ fn mode(&self) -> DiceMode {
+ match self.mode {
+ 1 => DiceMode::kDiceModeNormal,
+ 2 => DiceMode::kDiceModeDebug,
+ 3 => DiceMode::kDiceModeMaintenance,
+ _ => DiceMode::kDiceModeNotInitialized,
+ }
+ }
+}
+
+impl AsRef<EntryBody> for [u8] {
+ fn as_ref(&self) -> &EntryBody {
+ assert_eq!(self.len(), size_of::<EntryBody>());
+ // SAFETY - The size of the slice was checked and members are validated by accessors.
+ unsafe { &*self.as_ptr().cast::<EntryBody>() }
+ }
+}
+
+impl AsRef<[u8]> for EntryBody {
+ fn as_ref(&self) -> &[u8] {
+ let s = self as *const Self;
+ // SAFETY - Transmute the (valid) bytes into a slice.
+ unsafe { slice::from_raw_parts(s.cast::<u8>(), size_of::<Self>()) }
+ }
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 223c24e..6545a07 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -26,9 +26,11 @@
mod entry;
mod exceptions;
mod fdt;
+mod gpt;
mod heap;
mod helpers;
mod hvc;
+mod instance;
mod memory;
mod mmio_guard;
mod mmu;
@@ -42,10 +44,11 @@
use crate::fdt::modify_for_next_stage;
use crate::helpers::flush;
use crate::helpers::GUEST_PAGE_SIZE;
+use crate::instance::get_or_generate_instance_salt;
use crate::memory::MemoryTracker;
use crate::virtio::pci;
-use crate::virtio::pci::find_virtio_devices;
-use diced_open_dice::{bcc_handover_main_flow, bcc_handover_parse, HIDDEN_SIZE};
+use diced_open_dice::bcc_handover_main_flow;
+use diced_open_dice::bcc_handover_parse;
use fdtpci::{PciError, PciInfo};
use libfdt::Fdt;
use log::{debug, error, info, trace};
@@ -80,7 +83,6 @@
let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
debug!("PCI: {:#x?}", pci_info);
let mut pci_root = pci::initialise(pci_info, memory)?;
- find_virtio_devices(&mut pci_root).map_err(handle_pci_error)?;
let verified_boot_data = verify_payload(signed_kernel, ramdisk, PUBLIC_KEY).map_err(|e| {
error!("Failed to verify the payload: {e}");
@@ -98,7 +100,13 @@
error!("Failed to compute partial DICE inputs: {e:?}");
RebootReason::InternalError
})?;
- let (new_instance, salt) = (false, [0; HIDDEN_SIZE]); // TODO(b/249723852): instance.img.
+ let (new_instance, salt) =
+ get_or_generate_instance_salt(&mut pci_root, &dice_inputs).map_err(|e| {
+ error!("Failed to get instance.img salt: {e}");
+ RebootReason::InternalError
+ })?;
+ trace!("Got salt from instance.img: {salt:x?}");
+
let dice_inputs = dice_inputs.into_input_values(&salt).map_err(|e| {
error!("Failed to generate DICE inputs: {e:?}");
RebootReason::InternalError
diff --git a/pvmfw/src/virtio/pci.rs b/pvmfw/src/virtio/pci.rs
index b61403b..58bc07e 100644
--- a/pvmfw/src/virtio/pci.rs
+++ b/pvmfw/src/virtio/pci.rs
@@ -17,11 +17,11 @@
use super::hal::HalImpl;
use crate::{entry::RebootReason, memory::MemoryTracker};
use alloc::boxed::Box;
-use fdtpci::{PciError, PciInfo};
-use log::{debug, error, info};
+use fdtpci::PciInfo;
+use log::{debug, error};
use once_cell::race::OnceBox;
use virtio_drivers::{
- device::blk::VirtIOBlk,
+ device::blk,
transport::{
pci::{
bus::{BusDeviceIterator, PciRoot},
@@ -69,7 +69,9 @@
Ok(())
}
-struct VirtIOBlkIterator<'a> {
+pub type VirtIOBlk = blk::VirtIOBlk<HalImpl, PciTransport>;
+
+pub struct VirtIOBlkIterator<'a> {
pci_root: &'a mut PciRoot,
bus: BusDeviceIterator,
}
@@ -82,7 +84,7 @@
}
impl<'a> Iterator for VirtIOBlkIterator<'a> {
- type Item = VirtIOBlk<HalImpl, PciTransport>;
+ type Item = VirtIOBlk;
fn next(&mut self) -> Option<Self::Item> {
loop {
@@ -112,14 +114,3 @@
}
}
}
-
-/// Finds VirtIO PCI devices.
-pub fn find_virtio_devices(pci_root: &mut PciRoot) -> Result<(), PciError> {
- for mut blk in VirtIOBlkIterator::new(pci_root) {
- info!("Found {} KiB block device.", blk.capacity() * 512 / 1024);
- let mut data = [0; 512];
- blk.read_block(0, &mut data).expect("Failed to read block device");
- }
-
- Ok(())
-}