mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
devices: fix TPM CRB register byte-sized access
Windows can access CRB registers with byte-sized writes and reads. The TPM device model used the byte offset as a u32 register index, which corrupted unaligned accesses and could expose invalid CRB state. Preserve the containing register on partial writes, read from the correct byte lane, allow exact-end buffer accesses. Assisted-by: Copilot:GPT-5.5 Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
@@ -219,6 +219,19 @@ fn locality_from_addr(addr: u32) -> u8 {
|
||||
(addr >> 12) as u8
|
||||
}
|
||||
|
||||
fn update_reg(regs: &mut [u32; TPM_CRB_R_MAX], offset: u32, data: &[u8]) -> u32 {
|
||||
let reg_offset = (offset & !0x3) as usize;
|
||||
let byte_offset = (offset & 0x3) as usize;
|
||||
let mut input = [0; 4];
|
||||
|
||||
input[..data.len()].copy_from_slice(data);
|
||||
let value = u32::from_le_bytes(input);
|
||||
let mask = (u32::MAX >> (32 - data.len() * 8)) << (byte_offset * 8);
|
||||
|
||||
regs[reg_offset] = (regs[reg_offset] & !mask) | ((value << (byte_offset * 8)) & mask);
|
||||
regs[reg_offset]
|
||||
}
|
||||
|
||||
pub struct Tpm {
|
||||
emulator: Emulator,
|
||||
regs: [u32; TPM_CRB_R_MAX],
|
||||
@@ -257,7 +270,7 @@ impl Tpm {
|
||||
}
|
||||
|
||||
fn request_completed(&mut self, success: bool) {
|
||||
self.regs[CRB_CTRL_START as usize] = !CRB_START_INVOKE;
|
||||
self.regs[CRB_CTRL_START as usize] &= !CRB_START_INVOKE;
|
||||
if !success {
|
||||
set_reg_field(
|
||||
&mut self.regs,
|
||||
@@ -353,7 +366,7 @@ impl BusDevice for Tpm {
|
||||
let read_len: usize = data.len();
|
||||
|
||||
if offset >= CRB_DATA_BUFFER
|
||||
&& (offset + read_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
&& (offset + read_len as u32) <= (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
{
|
||||
// Read from Data Buffer
|
||||
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||
@@ -361,17 +374,21 @@ impl BusDevice for Tpm {
|
||||
data[..].clone_from_slice(&self.data_buff[start..end]);
|
||||
} else {
|
||||
offset &= 0xff;
|
||||
let mut val = self.regs[offset as usize];
|
||||
let reg_offset = offset & !0x3;
|
||||
let mut val = self.regs[reg_offset as usize];
|
||||
|
||||
// Per the TCG PC Client Platform TPM Profile (PTP) spec, bit 0
|
||||
// of TPM_LOC_STATE_x is `tpmEstablished`. Reflect the live value
|
||||
// from the backend rather than the (zero-initialised) shadow.
|
||||
if offset == CRB_LOC_STATE && self.emulator.get_established_bit() {
|
||||
if reg_offset == CRB_LOC_STATE && self.emulator.get_established_bit() {
|
||||
val |= 0x1;
|
||||
}
|
||||
|
||||
if data.len() <= 4 {
|
||||
data.clone_from_slice(val.to_ne_bytes()[0..read_len].as_ref());
|
||||
let byte_offset = (offset & 0x3) as usize;
|
||||
data.clone_from_slice(
|
||||
(val >> (byte_offset * 8)).to_ne_bytes()[0..read_len].as_ref(),
|
||||
);
|
||||
} else {
|
||||
error!(
|
||||
"Invalid tpm read: offset {:#X}, data length {:?}",
|
||||
@@ -403,7 +420,7 @@ impl BusDevice for Tpm {
|
||||
let write_len = data.len();
|
||||
|
||||
if offset >= CRB_DATA_BUFFER
|
||||
&& (offset + write_len as u32) < (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
&& (offset + write_len as u32) <= (CRB_DATA_BUFFER + self.data_buff.len() as u32)
|
||||
{
|
||||
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||
if start == 0 {
|
||||
@@ -426,25 +443,28 @@ impl BusDevice for Tpm {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut input: [u8; 4] = [0; 4];
|
||||
input.copy_from_slice(&data[0..4]);
|
||||
let v = u32::from_le_bytes(input);
|
||||
let write_offset = offset;
|
||||
let reg_data_len = cmp::min(write_len, 4 - (offset as usize & 0x3));
|
||||
let mut input = [0; 4];
|
||||
input[..reg_data_len].copy_from_slice(&data[..reg_data_len]);
|
||||
let v = u32::from_le_bytes(input) << ((offset & 0x3) * 8);
|
||||
offset &= !0x3;
|
||||
|
||||
match offset {
|
||||
CRB_CTRL_CMD_SIZE_REG => {
|
||||
self.regs[CRB_CTRL_CMD_SIZE_REG as usize] = v;
|
||||
update_reg(&mut self.regs, write_offset, &data[..reg_data_len]);
|
||||
}
|
||||
CRB_CTRL_CMD_LADDR => {
|
||||
self.regs[CRB_CTRL_CMD_LADDR as usize] = v;
|
||||
update_reg(&mut self.regs, write_offset, &data[..reg_data_len]);
|
||||
}
|
||||
CRB_CTRL_CMD_HADDR => {
|
||||
self.regs[CRB_CTRL_CMD_HADDR as usize] = v;
|
||||
update_reg(&mut self.regs, write_offset, &data[..reg_data_len]);
|
||||
}
|
||||
CRB_CTRL_RSP_SIZE => {
|
||||
self.regs[CRB_CTRL_RSP_SIZE as usize] = v;
|
||||
update_reg(&mut self.regs, write_offset, &data[..reg_data_len]);
|
||||
}
|
||||
CRB_CTRL_RSP_ADDR => {
|
||||
self.regs[CRB_CTRL_RSP_ADDR as usize] = v;
|
||||
update_reg(&mut self.regs, write_offset, &data[..reg_data_len]);
|
||||
}
|
||||
CRB_CTRL_REQ => match v {
|
||||
CRB_CTRL_REQ_CMD_READY => {
|
||||
@@ -556,4 +576,14 @@ mod unit_tests {
|
||||
concat!("Test: ", stringify!(set_get_reg_field))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_reg_partial_write() {
|
||||
let mut regs: [u32; TPM_CRB_R_MAX] = [0; TPM_CRB_R_MAX];
|
||||
|
||||
update_reg(&mut regs, CRB_CTRL_CMD_SIZE_REG, &[0x78, 0x56, 0x34, 0x12]);
|
||||
update_reg(&mut regs, CRB_CTRL_CMD_SIZE_REG + 1, &[0xaa]);
|
||||
|
||||
assert_eq!(regs[CRB_CTRL_CMD_SIZE_REG as usize], 0x1234_aa78);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user