Skip to content

Commit b2c5ffe

Browse files
OuyangHang33jyao1
authored andcommitted
Add support for caculating kernel eventlog digist when using OVMF+QEMU
Signed-off-by: OuyangHang33 <[email protected]>
1 parent 6c780e6 commit b2c5ffe

File tree

2 files changed

+149
-4
lines changed

2 files changed

+149
-4
lines changed

td-shim-tools/src/bin/td-payload-reference-calculator/README.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ The test bzImage can be fetched via
1010
```bash
1111
wget https://github.com/Xynnn007/td-payload-reference-provider/raw/main/tests/bzImage
1212
```
13+
Note: The protocol version of bzImage should be 2.06+.
1314

1415
Test with the example bzImage
1516
```
16-
cargo run -- kernel -k bzImage -s 0x10000000
17+
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- kernel -k bzImage -s 0x10000000
1718
```
1819

1920
The `kernel-size` parameter here means `KERNEL_SIZE` defined in guest firmware, s.t. [TD-SHIM](https://github.com/confidential-containers/td-shim)
@@ -25,11 +26,23 @@ Will get the result
2526

2627
which is from https://github.com/confidential-containers/attestation-service/pull/33/files#diff-1a4e5ad4c3b043c019c00bc3b3072fd6e1e5b03a5ce8c498e1c0acaf697d9d3fR265
2728

29+
When using [TDVF](https://github.com/tianocore/edk2-staging/tree/2024-tdvf-ww01.4/OvmfPkg) + [QEMU](https://github.com/qemu/qemu) Kernel Direct Boot, the kernel eventlog digest will be pre-processed by QEMU Kernel Direct Boot logic. Which is from https://github.com/confidential-containers/td-shim/issues/633
30+
31+
Test with the example bzImage
32+
```
33+
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- kernel -k bzImage -q
34+
```
35+
36+
Will get the result
37+
```
38+
a5e921ae5bde7ab989216da059057741688eae9114b854ce60733824f93ade8a848f19c719f3fdd5c4f0d7178164a5e2
39+
```
40+
2841
### Kernel Parameter
2942

3043
Test
3144
```
32-
cargo run -- param -p "root=/dev/vda1 console=hvc0 rw" -s 0x1000
45+
cargo run -p td-shim-tools --bin td-payload-reference-calculator -- param -p "root=/dev/vda1 console=hvc0 rw" -s 0x1000
3346
```
3447

3548
Will get the result

td-shim-tools/src/bin/td-payload-reference-calculator/main.rs

+134-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ use std::{convert::TryFrom, path::Path};
1414
pub const KERNEL_SIZE: &str = "0x2000000";
1515
pub const KERNEL_PARAM_SIZE: &str = "0x1000";
1616

17+
// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only,
18+
// file offset specified at offset 0x3c,
19+
// size of PE signature is 4: "PE\0\0"
20+
const IMAGE_PE_OFFSET: usize = 0x003c;
21+
const PE_SIGNATURE_SIZE: u32 = 4;
22+
const IMGAE_BEGIN_CHECKSUM_ADDR: usize = 0x0000;
23+
const IMGAE_BEGIN_CHECKSUM_SIZE: usize = 0x00da;
24+
const IMGAE_CERT_TABLE_ADDR: usize = 0x00de;
25+
const IMGAE_CERT_TABLE_SIZE: usize = 0x004c;
26+
const IMGAE_HEADERS_ADDR: usize = 0x0132;
27+
const IMGAE_HEADERS_SIZE: usize = 0x00ce;
28+
29+
// Refer to https://www.kernel.org/doc/html/latest/arch/x86/boot.html#details-of-header-fields
30+
// Protocol version addr: 0x206, size: 2
31+
const IMAGE_PROTOCOL_ADDR: usize = 0x0206;
32+
1733
fn kernel(path: &str, size: &str) -> Result<String> {
1834
let path = Path::new(path).to_path_buf();
1935
let siz = parse::<u64>(size)?;
@@ -22,6 +38,11 @@ fn kernel(path: &str, size: &str) -> Result<String> {
2238
bail!("File size should be less than `kernel-size`");
2339
}
2440
let buf = std::fs::read(path)?;
41+
let protocol = ((buf[IMAGE_PROTOCOL_ADDR as usize + 1] as u16) << 8)
42+
| buf[IMAGE_PROTOCOL_ADDR as usize] as u16;
43+
if protocol < 0x206 {
44+
bail!("Protocol version should be 2.06+");
45+
}
2546
padding_digest(buf, siz as usize)
2647
}
2748

@@ -31,6 +52,111 @@ fn param(param: &str, size: &str) -> Result<String> {
3152
padding_digest(param, siz)
3253
}
3354

55+
fn qemu(path: &str, size: &str) -> Result<String> {
56+
let path = Path::new(path).to_path_buf();
57+
let siz = parse::<u64>(size)?;
58+
let file_size = std::fs::metadata(&path)?.len();
59+
if file_size > siz {
60+
bail!("File size should be less than `kernel-size`");
61+
}
62+
let buf = std::fs::read(path)?;
63+
let protocol = ((buf[IMAGE_PROTOCOL_ADDR as usize + 1] as u16) << 8)
64+
| buf[IMAGE_PROTOCOL_ADDR as usize] as u16;
65+
if protocol < 0x206 {
66+
bail!("Protocol version should be 2.06+");
67+
}
68+
qemu_patch(buf)
69+
}
70+
71+
fn qemu_patch(mut buf: Vec<u8>) -> Result<String> {
72+
// refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L999
73+
// patching type_of_loader @0x210
74+
buf[0x210] = 0xb0;
75+
76+
// refer to https://github.com/qemu/qemu/blob/f48c205fb42be48e2e47b7e1cd9a2802e5ca17b0/hw/i386/x86.c#L1003
77+
// patching loadflags @0x211
78+
buf[0x211] = 0x81;
79+
80+
// refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L1004
81+
// patching heap_end_ptr @0x224 cmdline_addr - real_addr - 0x200 = 0xfe00
82+
buf[0x224] = 0x00;
83+
buf[0x225] = 0xfe;
84+
85+
// refer to https://github.com/qemu/qemu/blob/9c74490bff6c8886a922008d0c9ce6cae70dd17e/hw/i386/x86.c#L962
86+
// patching cmd_line_ptr @0x228 cmdline_addr = 0x20000
87+
buf[0x228] = 0x00;
88+
buf[0x229] = 0x00;
89+
buf[0x22A] = 0x02;
90+
buf[0x22B] = 0x00;
91+
92+
let mut hasher = sha2::Sha384::new();
93+
let (number_of_region_entry, regions_base, regions_size) = get_image_regions(&buf);
94+
95+
for index in 0..number_of_region_entry {
96+
hasher.update(&buf[regions_base[index]..regions_base[index] + regions_size[index]]);
97+
}
98+
99+
let res = hasher.finalize();
100+
Ok(hex::encode(res))
101+
}
102+
103+
fn get_image_regions(buf: &[u8]) -> (usize, Vec<usize>, Vec<usize>) {
104+
// These 3 regions are known.
105+
let mut number_of_region_entry = 3;
106+
let mut regions_base = vec![
107+
IMGAE_BEGIN_CHECKSUM_ADDR,
108+
IMGAE_CERT_TABLE_ADDR,
109+
IMGAE_HEADERS_ADDR,
110+
];
111+
let mut regions_size = vec![
112+
IMGAE_BEGIN_CHECKSUM_SIZE,
113+
IMGAE_CERT_TABLE_SIZE,
114+
IMGAE_HEADERS_SIZE,
115+
];
116+
117+
// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image,
118+
// After the signature of an image file is COFF File Header, size is 20 bytes.
119+
// the NumberOfSections' offset is 2 and size is 2 bytes.
120+
let size_of_coff_file_header: u32 = 20;
121+
122+
let coff_file_header_offset = ((buf[IMAGE_PE_OFFSET + 3] as u32) << 24)
123+
| ((buf[IMAGE_PE_OFFSET + 2] as u32) << 16)
124+
| ((buf[IMAGE_PE_OFFSET + 1] as u32) << 8)
125+
| (buf[IMAGE_PE_OFFSET] as u32) + PE_SIGNATURE_SIZE;
126+
127+
let number_of_pecoff_entry = ((buf[coff_file_header_offset as usize + 3] as u16) << 8)
128+
| buf[coff_file_header_offset as usize + 2] as u16;
129+
number_of_region_entry += number_of_pecoff_entry as usize;
130+
131+
// the SizeOfOptionalHeader's offset is 16 and size is 2 bytes
132+
let size_of_optional_header = ((buf[coff_file_header_offset as usize + 17] as u16) << 8)
133+
| buf[coff_file_header_offset as usize + 16] as u16;
134+
135+
// Refer to https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
136+
// Size Of each Section is 40 bytes
137+
// SizeOfRawData Offset: 16 Size:4
138+
// PointerToRawData Offset: 20 Size:4
139+
let mut p = (coff_file_header_offset
140+
+ size_of_coff_file_header
141+
+ size_of_optional_header as u32) as usize;
142+
for _i in 0..number_of_pecoff_entry {
143+
p += 16;
144+
let size = ((buf[p + 3] as u32) << 24)
145+
| ((buf[p + 2] as u32) << 16)
146+
| ((buf[p + 1] as u32) << 8)
147+
| buf[p] as u32;
148+
p += 4;
149+
let base = ((buf[p + 3] as u32) << 24)
150+
| ((buf[p + 2] as u32) << 16)
151+
| ((buf[p + 1] as u32) << 8)
152+
| buf[p] as u32;
153+
regions_base.push(base as usize);
154+
regions_size.push(size as usize);
155+
p += 20;
156+
}
157+
(number_of_region_entry, regions_base, regions_size)
158+
}
159+
34160
fn padding_digest(mut buf: Vec<u8>, len: usize) -> Result<String> {
35161
let diff = len - buf.len();
36162

@@ -56,7 +182,8 @@ fn main() {
56182
.required(false)
57183
.default_value(KERNEL_SIZE)
58184
.action(ArgAction::Set),
59-
),
185+
)
186+
.arg(arg!(-q --"qemu" "QEMU Kernel Direct Boot patch string").required(false)),
60187
)
61188
.subcommand(
62189
command!("param")
@@ -78,7 +205,12 @@ fn main() {
78205
Some(("kernel", args)) => {
79206
let path = args.get_one::<String>("kernel").unwrap();
80207
let siz = args.get_one::<String>("size").unwrap();
81-
kernel(path, siz)
208+
// let qflag = args.get_one::<String>("qemu").unwrap();
209+
if args.get_flag("qemu") {
210+
qemu(path, siz)
211+
} else {
212+
kernel(path, siz)
213+
}
82214
}
83215
Some(("param", args)) => {
84216
let parameter = args.get_one::<String>("parameter").unwrap();

0 commit comments

Comments
 (0)