Skip to content

Commit

Permalink
feat(cmd/rofl): Add support for building TDX ROFL apps
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Nov 12, 2024
1 parent e6899c4 commit 9f90623
Show file tree
Hide file tree
Showing 45 changed files with 1,531 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ linters-settings:
- github.com/stretchr/testify
- github.com/tyler-smith/go-bip39
- github.com/zondax/ledger-go
- github.com/foxboron/go-uefi/authenticode
- golang.org/x/text
exhaustive:
# Switch statements are to be considered exhaustive if a 'default' case is
# present, even if all enum members aren't listed in the switch.
Expand Down
223 changes: 223 additions & 0 deletions build/measurement/acpi/acpi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package acpi

import (
"bytes"
"embed"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"

"github.com/oasisprotocol/oasis-core/go/runtime/bundle"
)

//go:embed *.hex *.json
var templates embed.FS

// OffsetData is the offset data file format.
type OffsetData struct {
Memory MemoryOffsetData `json:"memory"`
}

type MemoryOffsetData struct {
RangeMinimumOffset int `json:"range_minimum_offset"`
LengthOffset int `json:"length_offset"`
}

// GenerateTablesQemu generates ACPI tables for the given TD configuration.
//
// Returns the raw ACPI tables, RSDP and QEMU table loader command blob.
func GenerateTablesQemu(resources *bundle.TDXResources) ([]byte, []byte, []byte, error) {
// Fetch template based on CPU count.
fn := fmt.Sprintf("template_qemu_cpu%d.hex", resources.CPUCount)
tplHex, err := templates.ReadFile(fn)
if err != nil {
return nil, nil, nil, fmt.Errorf("template for ACPI tables is not available")
}

tpl, err := hex.DecodeString(string(tplHex))
if err != nil {
return nil, nil, nil, fmt.Errorf("malformed ACPI table template")
}

// Fetch corresponding offset data.
fn = fmt.Sprintf("template_qemu_cpu%d.json", resources.CPUCount)
offsetData, err := templates.ReadFile(fn)
if err != nil {
return nil, nil, nil, fmt.Errorf("offset data for ACPI tables is not available")
}

var od OffsetData
if err = json.Unmarshal(offsetData, &od); err != nil {
return nil, nil, nil, fmt.Errorf("malformed ACPI table offset data")
}

// Handle memory split at 2816 MiB (0xB0000000).
if resources.Memory >= 2816 {
binary.LittleEndian.PutUint32(tpl[od.Memory.RangeMinimumOffset:], 0x80000000)
binary.LittleEndian.PutUint32(tpl[od.Memory.LengthOffset:], 0x60000000)
} else {
memSizeBytes := uint32(resources.Memory * 1024 * 1024)
binary.LittleEndian.PutUint32(tpl[od.Memory.RangeMinimumOffset:], memSizeBytes)
binary.LittleEndian.PutUint32(tpl[od.Memory.LengthOffset:], 0xe0000000-memSizeBytes)
}

// Generate RSDP.
rsdp := append([]byte{},
0x52, 0x53, 0x44, 0x20, 0x50, 0x54, 0x52, 0x20, // Signature ("RSDP PTR ").
0x00, // Checksum.
0x42, 0x4F, 0x43, 0x48, 0x53, 0x20, // OEM ID ("BOCHS ").
0x00, // Revision.
)

// Find all required ACPI tables.
dsdtOffset, dsdtCsum, dsdtLen, err := findAcpiTable(tpl, "DSDT")
if err != nil {
return nil, nil, nil, err
}
facpOffset, facpCsum, facpLen, err := findAcpiTable(tpl, "FACP")
if err != nil {
return nil, nil, nil, err
}
apicOffset, apicCsum, apicLen, err := findAcpiTable(tpl, "APIC")
if err != nil {
return nil, nil, nil, err
}
mcfgOffset, mcfgCsum, mcfgLen, err := findAcpiTable(tpl, "MCFG")
if err != nil {
return nil, nil, nil, err
}
waetOffset, waetCsum, waetLen, err := findAcpiTable(tpl, "WAET")
if err != nil {
return nil, nil, nil, err
}
rsdtOffset, rsdtCsum, rsdtLen, err := findAcpiTable(tpl, "RSDT")
if err != nil {
return nil, nil, nil, err
}

// Update RSDP with RSDT address.
var rsdtAddress [4]byte
binary.LittleEndian.PutUint32(rsdtAddress[:], rsdtOffset)
rsdp = append(rsdp, rsdtAddress[:]...)

// Generate table loader commands.
const ldrLength = 4096
ldr := qemuLoaderAppend(nil, &qemuLoaderCmdAllocate{"etc/acpi/rsdp", 16, 2})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAllocate{"etc/acpi/tables", 64, 1})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", dsdtCsum, dsdtOffset, dsdtLen}) // DSDT
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 36, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 40, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 140, 8})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", facpCsum, facpOffset, facpLen}) // FACP
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", apicCsum, apicOffset, apicLen}) // APIC
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", mcfgCsum, mcfgOffset, mcfgLen}) // MCFG
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", waetCsum, waetOffset, waetLen}) // WAET
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 36, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 40, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 44, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 48, 4})
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", rsdtCsum, rsdtOffset, rsdtLen}) // RSDT
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/rsdp", "etc/acpi/tables", 16, 4}) // RSDT address
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/rsdp", 8, 0, 20}) // RSDP
if len(ldr) < ldrLength {
ldr = append(ldr, bytes.Repeat([]byte{0x00}, ldrLength-len(ldr))...)
}

return tpl, rsdp, ldr, nil
}

// findAcpiTable searches for the ACPI table with the given signature and returns its offset,
// checksum offset and length.
func findAcpiTable(tables []byte, signature string) (uint32, uint32, uint32, error) {
// Walk the tables to find the right one.
var offset int
for {
if offset >= len(tables) {
return 0, 0, 0, fmt.Errorf("ACPI table '%s' not found", signature)
}

tblSig := string(tables[offset : offset+4])
tblLen := int(binary.LittleEndian.Uint32(tables[offset+4 : offset+8]))
if tblSig == signature {
return uint32(offset), uint32(offset + 9), uint32(tblLen), nil
}

// Skip other tables.
offset += tblLen
}
}

type qemuLoaderCmdAllocate struct {
file string
alignment uint32
zone uint8
}

type qemuLoaderCmdAddPtr struct {
pointerFile string
pointeeFile string
pointerOffset uint32
pointerSize uint8
}

type qemuLoaderCmdAddChecksum struct {
file string
resultOffset uint32
start uint32
length uint32
}

func qemuLoaderAppend(data []byte, cmd interface{}) []byte {
appendFixedString := func(str string) {
const fixedLength = 56
data = append(data, []byte(str)...)
if len(str) < fixedLength {
data = append(data, bytes.Repeat([]byte{0x00}, fixedLength-len(str))...)
}
}

switch c := cmd.(type) {
case *qemuLoaderCmdAllocate:
data = append(data, 0x01, 0x00, 0x00, 0x00)

appendFixedString(c.file)

var val [4]byte
binary.LittleEndian.PutUint32(val[:], c.alignment)
data = append(data, val[:]...)

data = append(data, c.zone)
data = append(data, bytes.Repeat([]byte{0x00}, 63)...) // Padding.
case *qemuLoaderCmdAddPtr:
data = append(data, 0x02, 0x00, 0x00, 0x00)

appendFixedString(c.pointerFile)
appendFixedString(c.pointeeFile)

var val [4]byte
binary.LittleEndian.PutUint32(val[:], c.pointerOffset)
data = append(data, val[:]...)
data = append(data, c.pointerSize)
data = append(data, bytes.Repeat([]byte{0x00}, 7)...) // Padding.
case *qemuLoaderCmdAddChecksum:
data = append(data, 0x03, 0x00, 0x00, 0x00)

appendFixedString(c.file)

var val [4]byte
binary.LittleEndian.PutUint32(val[:], c.resultOffset)
data = append(data, val[:]...)

binary.LittleEndian.PutUint32(val[:], c.start)
data = append(data, val[:]...)

binary.LittleEndian.PutUint32(val[:], c.length)
data = append(data, val[:]...)

data = append(data, bytes.Repeat([]byte{0x00}, 56)...) // Padding.
default:
panic("unsupported command")
}
return data
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu1.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7489,
"length_offset": 7501
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu10.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu10.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8261,
"length_offset": 8273
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu11.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu11.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8347,
"length_offset": 8359
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu12.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu12.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8433,
"length_offset": 8445
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu13.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu13.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8519,
"length_offset": 8531
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu14.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu14.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8605,
"length_offset": 8617
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu15.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu15.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8691,
"length_offset": 8703
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu16.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu16.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8777,
"length_offset": 8789
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu2.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7572,
"length_offset": 7584
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu3.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7658,
"length_offset": 7670
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu4.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7744,
"length_offset": 7756
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu5.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7831,
"length_offset": 7843
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu6.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 7917,
"length_offset": 7929
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu7.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8003,
"length_offset": 8015
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu8.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu8.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8089,
"length_offset": 8101
}
}
1 change: 1 addition & 0 deletions build/measurement/acpi/template_qemu_cpu9.hex

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions build/measurement/acpi/template_qemu_cpu9.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"memory": {
"range_minimum_offset": 8175,
"length_offset": 8187
}
}
2 changes: 2 additions & 0 deletions build/measurement/measurement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package measurement implements utilities for reproducing enclave measurements.
package measurement
Loading

0 comments on commit 9f90623

Please sign in to comment.