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
|
(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 {
|
pub struct Tpm {
|
||||||
emulator: Emulator,
|
emulator: Emulator,
|
||||||
regs: [u32; TPM_CRB_R_MAX],
|
regs: [u32; TPM_CRB_R_MAX],
|
||||||
@@ -257,7 +270,7 @@ impl Tpm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn request_completed(&mut self, success: bool) {
|
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 {
|
if !success {
|
||||||
set_reg_field(
|
set_reg_field(
|
||||||
&mut self.regs,
|
&mut self.regs,
|
||||||
@@ -353,7 +366,7 @@ impl BusDevice for Tpm {
|
|||||||
let read_len: usize = data.len();
|
let read_len: usize = data.len();
|
||||||
|
|
||||||
if offset >= CRB_DATA_BUFFER
|
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
|
// Read from Data Buffer
|
||||||
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
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]);
|
data[..].clone_from_slice(&self.data_buff[start..end]);
|
||||||
} else {
|
} else {
|
||||||
offset &= 0xff;
|
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
|
// Per the TCG PC Client Platform TPM Profile (PTP) spec, bit 0
|
||||||
// of TPM_LOC_STATE_x is `tpmEstablished`. Reflect the live value
|
// of TPM_LOC_STATE_x is `tpmEstablished`. Reflect the live value
|
||||||
// from the backend rather than the (zero-initialised) shadow.
|
// 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;
|
val |= 0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.len() <= 4 {
|
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 {
|
} else {
|
||||||
error!(
|
error!(
|
||||||
"Invalid tpm read: offset {:#X}, data length {:?}",
|
"Invalid tpm read: offset {:#X}, data length {:?}",
|
||||||
@@ -403,7 +420,7 @@ impl BusDevice for Tpm {
|
|||||||
let write_len = data.len();
|
let write_len = data.len();
|
||||||
|
|
||||||
if offset >= CRB_DATA_BUFFER
|
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);
|
let start: usize = (offset as usize) - (CRB_DATA_BUFFER as usize);
|
||||||
if start == 0 {
|
if start == 0 {
|
||||||
@@ -426,25 +443,28 @@ impl BusDevice for Tpm {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut input: [u8; 4] = [0; 4];
|
let write_offset = offset;
|
||||||
input.copy_from_slice(&data[0..4]);
|
let reg_data_len = cmp::min(write_len, 4 - (offset as usize & 0x3));
|
||||||
let v = u32::from_le_bytes(input);
|
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 {
|
match offset {
|
||||||
CRB_CTRL_CMD_SIZE_REG => {
|
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 => {
|
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 => {
|
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 => {
|
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 => {
|
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 => match v {
|
||||||
CRB_CTRL_REQ_CMD_READY => {
|
CRB_CTRL_REQ_CMD_READY => {
|
||||||
@@ -556,4 +576,14 @@ mod unit_tests {
|
|||||||
concat!("Test: ", stringify!(set_get_reg_field))
|
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