-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: haowei <[email protected]>
- Loading branch information
Showing
26 changed files
with
2,984 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,8 @@ | |
Cargo.lock | ||
target/ | ||
*.bin | ||
*.obj | ||
# afl fuzz | ||
out | ||
*.profraw | ||
# Intellj working directory | ||
.idea | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# td-shim design | ||
|
||
`td-shim` is a lightweight shim firmware for Intel [TDX](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html). It is the initial code in a Trust Domain (TD) - the TDX confidential computing environment. | ||
|
||
By design, a hypervisor (such as KVM, cloud-hypervisor, etc) launches `td-shim`, as the initial guest component for a Trust Domain (TD). | ||
|
||
`td-shim` owns the reset vector (GPA 0xFFFFFFF0) of the guest TD. It performs below action: | ||
* initialize the guest TD according to TDX requirement, such as rendez-vous all application processors (AP), switch from 32bit protected mode to 64bit long mode, accept the TD private memory, and extend untrusted VMM input to the TDX Runtime Measurement Register (RTMR) with TD event log for TDX attestation. (Please refer to [Intel TDX Module 1.0 Specification](https://www.intel.com/content/dam/develop/external/us/en/documents/tdx-module-1.0-public-spec-v0.931.pdf) and [Intel TDX Virtual Firmware Design Guide](https://www.intel.com/content/dam/develop/external/us/en/documents/tdx-virtual-firmware-design-guide-rev-1.01.pdf) for detail). | ||
* enable defense in depth, such as data execution prevention (DEP), control flow enforcement technology (CET). (Please refer to [td-shim threat model](threat_model.md) for detail). | ||
* provide requires information for the next stage payload, such as memory map, ACPI table. (Please refer to [td-shim specification](tdshim_spec.md) for detail) | ||
* finally, launch the next stage payload - `td-payload`. | ||
|
||
`td-payload` could be a bare-metal environment, a UEFI virtual firmware, an OS loader, a OS kernel, etc. `td-shim` supports the multiple td-payload type, which is described by `PAYLOAD_IMAGE_TYPE` in [TD Payload Info GUID Extension HOB](tdshim_spec.md#td-payload-info-guid-extension-hob), such as a PE/COFF or ELF executable image, BzImage/VmLinux image fowllowing Linux Boot Protocol. | ||
|
||
Below figure shows the high level design. | ||
|
||
``` | ||
+----------------------+ | ||
| td-payload | | ||
| | | ||
+----------------------+ | ||
^ | ||
| <-------------------- td-shim spec | ||
+----------------------+ | ||
| td-shim | | ||
| ^ | | ||
| | | | ||
| (reset vector) | | ||
Guest +----------------------+ | ||
============================================ <--- td-shim spec | ||
Host +----------------------+ | ||
| VMM | | ||
+----------------------+ | ||
``` | ||
|
||
The [td-shim specification](tdshim_spec.md) defines the interface between td-shim and vmm, and the interface between td-shim and td-payload. | ||
|
||
The [td-shim threat model](threat_model.md) defines the threat model for the td-shim. | ||
|
||
This repo includes a full `td-shim`, and sample `td-payload`. The consumer may create other td-payload. For example, to support [TDX](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html) [migration TD](https://www.intel.com/content/dam/develop/external/us/en/documents/tdx-migration-td-design-guide-348987-001.pdf), a rust-migtd can include a td-shim and a migtd-payload. | ||
|
||
## td-shim | ||
|
||
[rust-tdshim](../td-shim) is a core of td-shim. The entrypoint is `_start()` at [main](../td-shim/src/bin/td-shim/main.rs). It will initialize the td-shim and switch to td-payload at `switch_stack_call()` of [main](../td-shim/src/bin/td-shim/main.rs). | ||
|
||
The VMM may pass a TD Hand-Off Block (HOB) to the `td-shim` as parameter. The TD HOB is measured and event log is created at `create_td_event()` in [tcg.rs](../td-shim/src/tcg.rs). | ||
|
||
Data Execution Prevention (DEP) is setup at `find_and_report_entry_point()` in [ipl.rs](../td-shim/src/ipl.rs). The primitive `set_nx_bit()` and `set_write_protect()` are provided by [memory.rs](../td-shim/src/memory.rs). | ||
|
||
Control flow Enforcement Technology (CET) Shadow Stack is setup at `enable_cet_ss()` in [cet_ss.rs](../td-shim/src/bin/td-shim/cet_ss.rs). | ||
|
||
Stack guard is setup at `stack_guard_enable()` in [stack_guard.rs](../td-shim/src/stack_guard.rs). | ||
|
||
### Reset vector | ||
|
||
[ResetVector](../td-shim/ResetVector) is the reset vector inside of the `td-shim`. It owns the first instruction in the TD at address 0xFFFFFFF0. This is implemented in the IA32 code named [resetVector](../td-shim/ResetVector/Ia32/ResetVectorVtf0.asm). The code then switches to long mode, parks application processors (APs), initializes the stack, copies the `td-shim` core to low memory (1MB) and call to `rust-tdshim` via an indirect call `call rsi` at [main](../td-shim/ResetVector/Main.asm) | ||
|
||
### TDX related lib | ||
|
||
[tdx-exception](../td-exception) provides execution handler in TD. | ||
|
||
[tdx-logger](../td-logger) provides debug logger in TD. | ||
|
||
[tdx-tdcall](../td-logger) provides TDCALL function. | ||
|
||
### Generic lib | ||
|
||
[td-loader](../td-loader) is an PE/ELF image loader. | ||
|
||
[fw-pci](../fw-pci) provides the access to PCI space. | ||
|
||
[fw-virtio](../fw-virtio) provides virtio interface. | ||
|
||
[fw-vsock](../fw-vsock) provides vsock interface. | ||
|
||
[r-uefi-pi](../r-uefi-pi) defines uefi-pi data structure. | ||
|
||
[uefi-pi](../uefi-pi) provide uefi-pi structure access function. | ||
|
||
[td-paging](../td-paging) provides function to manage the page table. | ||
|
||
### External dependency | ||
|
||
[ring](https://github.com/jyao1/ring/tree/uefi_support) is crypto function. The SHA384 function is used to calculate measurement. | ||
|
||
## build | ||
|
||
### tools | ||
|
||
[rust-td-tool](../td-shim-ld) is the tool to assembly all components into a TD.bin. | ||
|
||
### layout | ||
|
||
[td-layout](../td-layout) defines the layout of a TD. | ||
|
||
## sample td-payload | ||
|
||
[rust-td-payload](../td-payload) is a sample payload. It supports benchmark collection, json parsing. | ||
|
||
## test tools | ||
|
||
[benchmark](../devtools/td-benchmark) is to help collect benchmark information, such as stack usage, heap usage, execution time. | ||
|
||
[fuzzing-test](../fuzzing) includes sample fuzzing test. Refer to [fuzzing](doc/fuzzing.md) doc for more detail. | ||
|
||
[test-coverage](doc/unit_test_coverage.md) describes how to collect the coverage data. | ||
|
||
[static_analyzer](doc/static_analyzer.md) describes how to scan the vulnerable rust code, such as [rudra](https://github.com/sslab-gatech/Rudra). | ||
|
||
[cargo-deny](../.github/workflows/deny.yml) is used to scan the vulnerable rust crate dependency according to [rustsec](https://rustsec.org/). | ||
|
||
[no_std_test](../no_std_test) is used to run test for no_std code. | ||
|
||
[test_lib](../test_lib) is to provide support function for unit test. |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// Copyright (c) 2021 Intel Corporation | ||
// Copyright (c) 2022 Alibaba Cloud | ||
// | ||
// SPDX-License-Identifier: BSD-2-Clause-Patent | ||
extern crate alloc; | ||
|
||
use alloc::vec::Vec; | ||
use td_shim::acpi::{calculate_checksum, Rsdp, Xsdt}; | ||
|
||
use super::*; | ||
|
||
#[derive(Default)] | ||
pub struct AcpiTables<'a> { | ||
acpi_memory: &'a mut [u8], | ||
pa: u64, | ||
size: usize, | ||
fadt: Option<(usize, usize)>, // FADT offset in acpi memory | ||
dsdt: Option<usize>, // DSDT offset in acpi memory | ||
table_offset: Vec<usize>, | ||
} | ||
|
||
impl<'a> AcpiTables<'a> { | ||
pub fn new(td_acpi_mem: &'a mut [u8], pa: u64) -> Self { | ||
AcpiTables { | ||
acpi_memory: td_acpi_mem, | ||
pa, | ||
..Default::default() | ||
} | ||
} | ||
|
||
pub fn finish(&mut self) -> u64 { | ||
let mut xsdt = Xsdt::new(); | ||
|
||
// The Fixed ACPI Description Table (FADT) should always be the first table in XSDT. | ||
if let Some((fadt_off, fadt_len)) = self.fadt { | ||
// Safe because DSDT is loaded in acpi_memory which is below 4G | ||
let dsdt = self | ||
.dsdt | ||
.as_ref() | ||
.map(|v| self.offset_to_address(*v)) | ||
.unwrap_or_default() as u32; | ||
let fadt = &mut self.acpi_memory[fadt_off..fadt_off + fadt_len]; | ||
// The Differentiated System Description Table (DSDT) is referred by the FADT table. | ||
if dsdt != 0 { | ||
// The DSDT field of FADT [40..44] | ||
dsdt.write_to(&mut fadt[40..44]); | ||
} | ||
|
||
// Update FADT checksum | ||
fadt[9] = 0; | ||
fadt[9] = calculate_checksum(fadt); | ||
xsdt.add_table(self.offset_to_address(fadt_off)); | ||
} | ||
|
||
for offset in &self.table_offset { | ||
xsdt.add_table(self.offset_to_address(*offset)); | ||
} | ||
|
||
let xsdt_addr = self.offset_to_address(self.size); | ||
xsdt.checksum(); | ||
xsdt.write_to(&mut self.acpi_memory[self.size..self.size + size_of::<Xsdt>()]); | ||
self.size += size_of::<Xsdt>(); | ||
|
||
let rsdp_addr = self.offset_to_address(self.size); | ||
let rsdp = Rsdp::new(xsdt_addr); | ||
rsdp.write_to(&mut self.acpi_memory[self.size..self.size + size_of::<Rsdp>()]); | ||
self.size += size_of::<Rsdp>(); | ||
|
||
rsdp_addr as u64 | ||
} | ||
|
||
pub fn install(&mut self, table: &[u8]) { | ||
// Also reserve space for Xsdt and Rsdp | ||
let total_size = self.size + table.len() + size_of::<Xsdt>() + size_of::<Rsdp>(); | ||
if self.acpi_memory.len() < total_size { | ||
log::error!( | ||
"ACPI content size exceeds limit 0x{:X}", | ||
self.acpi_memory.len(), | ||
); | ||
return; | ||
} else if table.len() < size_of::<GenericSdtHeader>() { | ||
log::error!("ACPI table with length 0x{:X} is invalid", table.len()); | ||
return; | ||
} | ||
|
||
// Safe because we have checked buffer size. | ||
let header = GenericSdtHeader::read_from(&table[..size_of::<GenericSdtHeader>()]).unwrap(); | ||
if header.length as usize > table.len() { | ||
log::error!( | ||
"invalid ACPI table, header length {} is bigger than data length {}", | ||
header.length as usize, | ||
table.len() | ||
); | ||
return; | ||
} | ||
|
||
if &header.signature == b"FACP" { | ||
// We will write to the `dsdt` fields at [40-44) | ||
if header.length < 44 { | ||
log::error!("invalid ACPI FADT table"); | ||
return; | ||
} | ||
self.fadt = Some((self.size, header.length as usize)); | ||
} else if &header.signature == b"DSDT" { | ||
self.dsdt = Some(self.size); | ||
} else { | ||
for offset in &self.table_offset { | ||
// Safe because it's reading data from our own buffer. | ||
let table_header = GenericSdtHeader::read_from( | ||
&self.acpi_memory[*offset..*offset + size_of::<GenericSdtHeader>()], | ||
) | ||
.unwrap(); | ||
if table_header.signature == header.signature { | ||
log::info!( | ||
"ACPI: {} has been installed, use first\n", | ||
core::str::from_utf8(&header.signature).unwrap_or_default() | ||
); | ||
return; | ||
} | ||
} | ||
self.table_offset.push(self.size); | ||
} | ||
|
||
self.acpi_memory[self.size..self.size + table.len()].copy_from_slice(table); | ||
self.size += table.len(); | ||
} | ||
|
||
fn offset_to_address(&self, offset: usize) -> u64 { | ||
self.pa + offset as u64 | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_acpi_tables() { | ||
let mut buff = [0u8; 500]; | ||
let mut tables = AcpiTables::new(&mut buff, 0x100000); | ||
|
||
assert_eq!(tables.offset_to_address(0x1000), 0x101000); | ||
assert_eq!(tables.size, 0); | ||
|
||
tables.install(&[]); | ||
assert_eq!(tables.size, 0); | ||
tables.install(&[0u8]); | ||
assert_eq!(tables.size, 0); | ||
tables.install(&[0u8; 269]); | ||
assert_eq!(tables.size, 0); | ||
|
||
let hdr = GenericSdtHeader::new(b"FACP", 44, 2); | ||
let mut buf = [0u8; 44]; | ||
buf[0..size_of::<GenericSdtHeader>()].copy_from_slice(hdr.as_bytes()); | ||
tables.install(&buf); | ||
assert_eq!(tables.fadt, Some((0, 44))); | ||
assert_eq!(tables.size, 44); | ||
|
||
let hdr = GenericSdtHeader::new(b"DSDT", size_of::<GenericSdtHeader>() as u32, 2); | ||
tables.install(hdr.as_bytes()); | ||
assert_eq!(tables.size, 44 + size_of::<GenericSdtHeader>()); | ||
|
||
let hdr = GenericSdtHeader::new(b"TEST", size_of::<GenericSdtHeader>() as u32, 2); | ||
tables.install(hdr.as_bytes()); | ||
assert_eq!(tables.size, 44 + 2 * size_of::<GenericSdtHeader>()); | ||
|
||
let hdr = GenericSdtHeader::new(b"TEST", size_of::<GenericSdtHeader>() as u32, 2); | ||
tables.install(hdr.as_bytes()); | ||
assert_eq!(tables.size, 44 + 2 * size_of::<GenericSdtHeader>()); | ||
|
||
let addr = tables.finish(); | ||
assert_eq!( | ||
addr, | ||
0x100000 + 240 + 2 * size_of::<GenericSdtHeader>() as u64 | ||
); | ||
} | ||
} |
Oops, something went wrong.