Skip to content

Commit

Permalink
td-shim: Migrate the bin directory
Browse files Browse the repository at this point in the history
Signed-off-by: haowei <[email protected]>
  • Loading branch information
haowx authored and haowqs committed Mar 22, 2022
1 parent e7c4501 commit 1edfa3b
Show file tree
Hide file tree
Showing 26 changed files with 2,984 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Cargo.lock
target/
*.bin
*.obj
# afl fuzz
out
*.profraw
# Intellj working directory
.idea
Expand Down
115 changes: 115 additions & 0 deletions doc/design.md
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.
7 changes: 0 additions & 7 deletions td-shim/.gitignore

This file was deleted.

4 changes: 4 additions & 0 deletions td-shim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ edition = "2018"
# add build process
build = "build.rs"

[[bin]]
name = "td-shim"
required-features = ["main"]

[build-dependencies]
anyhow = "1.0.55"
cc = { git = "https://github.com/jyao1/cc-rs.git", branch = "uefi_support" }
Expand Down
177 changes: 177 additions & 0 deletions td-shim/src/bin/td-shim/acpi.rs
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
);
}
}
Loading

0 comments on commit 1edfa3b

Please sign in to comment.