Skip to content

Commit 6b5aefb

Browse files
authored
Merge pull request #298 from oasisprotocol/kostko/feature/rofl-tdx
feat(cmd/rofl): Add support for building TDX ROFL apps
2 parents 8a1f197 + e872c86 commit 6b5aefb

File tree

102 files changed

+2020
-145
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2020
-145
lines changed

.github/workflows/ci-lint.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
- name: Set up Go
4242
uses: actions/setup-go@v5
4343
with:
44-
go-version: "1.21.x"
44+
go-version: "1.22.x"
4545
cache: false
4646
- name: Install gitlint
4747
run: |
@@ -63,7 +63,7 @@ jobs:
6363
# 'make lint-go'.
6464
uses: golangci/[email protected]
6565
with:
66-
version: v1.55
66+
version: v1.56
6767
# Always run this step so that all linting errors can be seen at once.
6868
if: always()
6969
- name: Ensure a clean code checkout

.github/workflows/ci-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Set up Go
3030
uses: actions/setup-go@v5
3131
with:
32-
go-version: "1.21.x"
32+
go-version: "1.22.x"
3333
- name: Cache Go dependencies
3434
uses: actions/cache@v4
3535
with:

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Set up Go
2323
uses: actions/setup-go@v5
2424
with:
25-
go-version: "1.21.x"
25+
go-version: "1.22.x"
2626
- name: Install GoReleaser
2727
uses: goreleaser/goreleaser-action@v5
2828
with:

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ linters-settings:
7070
- github.com/stretchr/testify
7171
- github.com/tyler-smith/go-bip39
7272
- github.com/zondax/ledger-go
73+
- github.com/foxboron/go-uefi/authenticode
74+
- golang.org/x/text
75+
- gopkg.in/yaml.v3
7376
exhaustive:
7477
# Switch statements are to be considered exhaustive if a 'default' case is
7578
# present, even if all enum members aren't listed in the switch.

build/cargo/cargo.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,44 @@ import (
66
"encoding/json"
77
"fmt"
88
"os/exec"
9+
"slices"
910
"strings"
1011
)
1112

1213
// Metadata is the cargo package metadata.
1314
type Metadata struct {
14-
Name string
15-
Version string
15+
Name string
16+
Version string
17+
Dependencies []Dependency
18+
}
19+
20+
// FindDependency finds the first dependency with the given name and returns it. Iff no such
21+
// dependency can be found, it returns nil.
22+
func (m *Metadata) FindDependency(name string) *Dependency {
23+
for _, d := range m.Dependencies {
24+
if d.Name != name {
25+
continue
26+
}
27+
28+
return &d
29+
}
30+
return nil
31+
}
32+
33+
// Dependency is the metadata about a dependency.
34+
type Dependency struct {
35+
Name string `json:"name"`
36+
Features []string `json:"features"`
37+
}
38+
39+
// HasFeature returns true iff the given feature is present among the features.
40+
func (d *Dependency) HasFeature(feature string) bool {
41+
return slices.Contains(d.Features, feature)
1642
}
1743

1844
// GetMetadata queries `cargo` for metadata of the package in the current working directory.
1945
func GetMetadata() (*Metadata, error) {
20-
cmd := exec.Command("cargo", "metadata", "--no-deps")
46+
cmd := exec.Command("cargo", "metadata", "--no-deps", "--format-version", "1")
2147
stdout, err := cmd.StdoutPipe()
2248
if err != nil {
2349
return nil, fmt.Errorf("failed to initialize metadata process: %w", err)
@@ -29,8 +55,12 @@ func GetMetadata() (*Metadata, error) {
2955
dec := json.NewDecoder(stdout)
3056
var rawMeta struct {
3157
Packages []struct {
32-
Name string `json:"name"`
33-
Version string `json:"version"`
58+
Name string `json:"name"`
59+
Version string `json:"version"`
60+
Dependencies []struct {
61+
Name string `json:"name"`
62+
Features []string `json:"features"`
63+
} `json:"dependencies"`
3464
} `json:"packages"`
3565
}
3666
if err = dec.Decode(&rawMeta); err != nil {
@@ -43,10 +73,19 @@ func GetMetadata() (*Metadata, error) {
4373
return nil, fmt.Errorf("no cargo packages found")
4474
}
4575

46-
return &Metadata{
76+
meta := &Metadata{
4777
Name: rawMeta.Packages[0].Name,
4878
Version: rawMeta.Packages[0].Version,
49-
}, nil
79+
}
80+
for _, dep := range rawMeta.Packages[0].Dependencies {
81+
d := Dependency{
82+
Name: dep.Name,
83+
Features: dep.Features,
84+
}
85+
meta.Dependencies = append(meta.Dependencies, d)
86+
}
87+
88+
return meta, nil
5089
}
5190

5291
// Build builds a Rust program using `cargo` in the current working directory.

build/measurement/acpi/acpi.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package acpi
2+
3+
import (
4+
"bytes"
5+
"embed"
6+
"encoding/binary"
7+
"encoding/hex"
8+
"encoding/json"
9+
"fmt"
10+
11+
"github.com/oasisprotocol/oasis-core/go/runtime/bundle"
12+
)
13+
14+
//go:embed *.hex *.json
15+
var templates embed.FS
16+
17+
// OffsetData is the offset data file format.
18+
type OffsetData struct {
19+
Memory MemoryOffsetData `json:"memory"`
20+
}
21+
22+
type MemoryOffsetData struct {
23+
RangeMinimumOffset int `json:"range_minimum_offset"`
24+
LengthOffset int `json:"length_offset"`
25+
}
26+
27+
// GenerateTablesQemu generates ACPI tables for the given TD configuration.
28+
//
29+
// Returns the raw ACPI tables, RSDP and QEMU table loader command blob.
30+
func GenerateTablesQemu(resources *bundle.TDXResources) ([]byte, []byte, []byte, error) {
31+
// Fetch template based on CPU count.
32+
fn := fmt.Sprintf("template_qemu_cpu%d.hex", resources.CPUCount)
33+
tplHex, err := templates.ReadFile(fn)
34+
if err != nil {
35+
return nil, nil, nil, fmt.Errorf("template for ACPI tables is not available")
36+
}
37+
38+
tpl, err := hex.DecodeString(string(tplHex))
39+
if err != nil {
40+
return nil, nil, nil, fmt.Errorf("malformed ACPI table template")
41+
}
42+
43+
// Fetch corresponding offset data.
44+
fn = fmt.Sprintf("template_qemu_cpu%d.json", resources.CPUCount)
45+
offsetData, err := templates.ReadFile(fn)
46+
if err != nil {
47+
return nil, nil, nil, fmt.Errorf("offset data for ACPI tables is not available")
48+
}
49+
50+
var od OffsetData
51+
if err = json.Unmarshal(offsetData, &od); err != nil {
52+
return nil, nil, nil, fmt.Errorf("malformed ACPI table offset data")
53+
}
54+
55+
// Handle memory split at 2816 MiB (0xB0000000).
56+
if resources.Memory >= 2816 {
57+
binary.LittleEndian.PutUint32(tpl[od.Memory.RangeMinimumOffset:], 0x80000000)
58+
binary.LittleEndian.PutUint32(tpl[od.Memory.LengthOffset:], 0x60000000)
59+
} else {
60+
memSizeBytes := uint32(resources.Memory * 1024 * 1024)
61+
binary.LittleEndian.PutUint32(tpl[od.Memory.RangeMinimumOffset:], memSizeBytes)
62+
binary.LittleEndian.PutUint32(tpl[od.Memory.LengthOffset:], 0xe0000000-memSizeBytes)
63+
}
64+
65+
// Generate RSDP.
66+
rsdp := append([]byte{},
67+
0x52, 0x53, 0x44, 0x20, 0x50, 0x54, 0x52, 0x20, // Signature ("RSDP PTR ").
68+
0x00, // Checksum.
69+
0x42, 0x4F, 0x43, 0x48, 0x53, 0x20, // OEM ID ("BOCHS ").
70+
0x00, // Revision.
71+
)
72+
73+
// Find all required ACPI tables.
74+
dsdtOffset, dsdtCsum, dsdtLen, err := findAcpiTable(tpl, "DSDT")
75+
if err != nil {
76+
return nil, nil, nil, err
77+
}
78+
facpOffset, facpCsum, facpLen, err := findAcpiTable(tpl, "FACP")
79+
if err != nil {
80+
return nil, nil, nil, err
81+
}
82+
apicOffset, apicCsum, apicLen, err := findAcpiTable(tpl, "APIC")
83+
if err != nil {
84+
return nil, nil, nil, err
85+
}
86+
mcfgOffset, mcfgCsum, mcfgLen, err := findAcpiTable(tpl, "MCFG")
87+
if err != nil {
88+
return nil, nil, nil, err
89+
}
90+
waetOffset, waetCsum, waetLen, err := findAcpiTable(tpl, "WAET")
91+
if err != nil {
92+
return nil, nil, nil, err
93+
}
94+
rsdtOffset, rsdtCsum, rsdtLen, err := findAcpiTable(tpl, "RSDT")
95+
if err != nil {
96+
return nil, nil, nil, err
97+
}
98+
99+
// Update RSDP with RSDT address.
100+
var rsdtAddress [4]byte
101+
binary.LittleEndian.PutUint32(rsdtAddress[:], rsdtOffset)
102+
rsdp = append(rsdp, rsdtAddress[:]...)
103+
104+
// Generate table loader commands.
105+
const ldrLength = 4096
106+
ldr := qemuLoaderAppend(nil, &qemuLoaderCmdAllocate{"etc/acpi/rsdp", 16, 2})
107+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAllocate{"etc/acpi/tables", 64, 1})
108+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", dsdtCsum, dsdtOffset, dsdtLen}) // DSDT
109+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 36, 4})
110+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 40, 4})
111+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", facpOffset + 140, 8})
112+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", facpCsum, facpOffset, facpLen}) // FACP
113+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", apicCsum, apicOffset, apicLen}) // APIC
114+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", mcfgCsum, mcfgOffset, mcfgLen}) // MCFG
115+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", waetCsum, waetOffset, waetLen}) // WAET
116+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 36, 4})
117+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 40, 4})
118+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 44, 4})
119+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/tables", "etc/acpi/tables", rsdtOffset + 48, 4})
120+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/tables", rsdtCsum, rsdtOffset, rsdtLen}) // RSDT
121+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddPtr{"etc/acpi/rsdp", "etc/acpi/tables", 16, 4}) // RSDT address
122+
ldr = qemuLoaderAppend(ldr, &qemuLoaderCmdAddChecksum{"etc/acpi/rsdp", 8, 0, 20}) // RSDP
123+
if len(ldr) < ldrLength {
124+
ldr = append(ldr, bytes.Repeat([]byte{0x00}, ldrLength-len(ldr))...)
125+
}
126+
127+
return tpl, rsdp, ldr, nil
128+
}
129+
130+
// findAcpiTable searches for the ACPI table with the given signature and returns its offset,
131+
// checksum offset and length.
132+
func findAcpiTable(tables []byte, signature string) (uint32, uint32, uint32, error) {
133+
// Walk the tables to find the right one.
134+
var offset int
135+
for {
136+
if offset >= len(tables) {
137+
return 0, 0, 0, fmt.Errorf("ACPI table '%s' not found", signature)
138+
}
139+
140+
tblSig := string(tables[offset : offset+4])
141+
tblLen := int(binary.LittleEndian.Uint32(tables[offset+4 : offset+8]))
142+
if tblSig == signature {
143+
return uint32(offset), uint32(offset + 9), uint32(tblLen), nil
144+
}
145+
146+
// Skip other tables.
147+
offset += tblLen
148+
}
149+
}
150+
151+
type qemuLoaderCmdAllocate struct {
152+
file string
153+
alignment uint32
154+
zone uint8
155+
}
156+
157+
type qemuLoaderCmdAddPtr struct {
158+
pointerFile string
159+
pointeeFile string
160+
pointerOffset uint32
161+
pointerSize uint8
162+
}
163+
164+
type qemuLoaderCmdAddChecksum struct {
165+
file string
166+
resultOffset uint32
167+
start uint32
168+
length uint32
169+
}
170+
171+
func qemuLoaderAppend(data []byte, cmd interface{}) []byte {
172+
appendFixedString := func(str string) {
173+
const fixedLength = 56
174+
data = append(data, []byte(str)...)
175+
if len(str) < fixedLength {
176+
data = append(data, bytes.Repeat([]byte{0x00}, fixedLength-len(str))...)
177+
}
178+
}
179+
180+
switch c := cmd.(type) {
181+
case *qemuLoaderCmdAllocate:
182+
data = append(data, 0x01, 0x00, 0x00, 0x00)
183+
184+
appendFixedString(c.file)
185+
186+
var val [4]byte
187+
binary.LittleEndian.PutUint32(val[:], c.alignment)
188+
data = append(data, val[:]...)
189+
190+
data = append(data, c.zone)
191+
data = append(data, bytes.Repeat([]byte{0x00}, 63)...) // Padding.
192+
case *qemuLoaderCmdAddPtr:
193+
data = append(data, 0x02, 0x00, 0x00, 0x00)
194+
195+
appendFixedString(c.pointerFile)
196+
appendFixedString(c.pointeeFile)
197+
198+
var val [4]byte
199+
binary.LittleEndian.PutUint32(val[:], c.pointerOffset)
200+
data = append(data, val[:]...)
201+
data = append(data, c.pointerSize)
202+
data = append(data, bytes.Repeat([]byte{0x00}, 7)...) // Padding.
203+
case *qemuLoaderCmdAddChecksum:
204+
data = append(data, 0x03, 0x00, 0x00, 0x00)
205+
206+
appendFixedString(c.file)
207+
208+
var val [4]byte
209+
binary.LittleEndian.PutUint32(val[:], c.resultOffset)
210+
data = append(data, val[:]...)
211+
212+
binary.LittleEndian.PutUint32(val[:], c.start)
213+
data = append(data, val[:]...)
214+
215+
binary.LittleEndian.PutUint32(val[:], c.length)
216+
data = append(data, val[:]...)
217+
218+
data = append(data, bytes.Repeat([]byte{0x00}, 56)...) // Padding.
219+
default:
220+
panic("unsupported command")
221+
}
222+
return data
223+
}

build/measurement/acpi/template_qemu_cpu1.hex

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"memory": {
3+
"range_minimum_offset": 7489,
4+
"length_offset": 7501
5+
}
6+
}

build/measurement/acpi/template_qemu_cpu10.hex

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"memory": {
3+
"range_minimum_offset": 8261,
4+
"length_offset": 8273
5+
}
6+
}

0 commit comments

Comments
 (0)