i386/tdx: Track RAM entries for TDX VM

The RAM of TDX VM can be classified into two types:

 - TDX_RAM_UNACCEPTED: default type of TDX memory, which needs to be
   accepted by TDX guest before it can be used and will be all-zeros
   after being accepted.

 - TDX_RAM_ADDED: the RAM that is ADD'ed to TD guest before running, and
   can be used directly. E.g., TD HOB and TEMP MEM that needed by TDVF.

Maintain TdxRamEntries[] which grabs the initial RAM info from e820 table
and mark each RAM range as default type TDX_RAM_UNACCEPTED.

Then turn the range of TD HOB and TEMP MEM to TDX_RAM_ADDED since these
ranges will be ADD'ed before TD runs and no need to be accepted runtime.

The TdxRamEntries[] are later used to setup the memory TD resource HOB
that passes memory info from QEMU to TDVF.

Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Link: https://lore.kernel.org/r/20250508150002.689633-22-xiaoyao.li@intel.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Xiaoyao Li 2025-05-08 10:59:27 -04:00 committed by Paolo Bonzini
parent 4420ba0ebb
commit f18672e4cf
2 changed files with 123 additions and 0 deletions

View file

@ -18,6 +18,7 @@
#include "crypto/hash.h"
#include "system/system.h"
#include "hw/i386/e820_memory_layout.h"
#include "hw/i386/tdvf.h"
#include "hw/i386/x86.h"
#include "kvm_i386.h"
@ -146,11 +147,110 @@ void tdx_set_tdvf_region(MemoryRegion *tdvf_mr)
tdx_guest->tdvf_mr = tdvf_mr;
}
static void tdx_add_ram_entry(uint64_t address, uint64_t length,
enum TdxRamType type)
{
uint32_t nr_entries = tdx_guest->nr_ram_entries;
tdx_guest->ram_entries = g_renew(TdxRamEntry, tdx_guest->ram_entries,
nr_entries + 1);
tdx_guest->ram_entries[nr_entries].address = address;
tdx_guest->ram_entries[nr_entries].length = length;
tdx_guest->ram_entries[nr_entries].type = type;
tdx_guest->nr_ram_entries++;
}
static int tdx_accept_ram_range(uint64_t address, uint64_t length)
{
uint64_t head_start, tail_start, head_length, tail_length;
uint64_t tmp_address, tmp_length;
TdxRamEntry *e;
int i = 0;
do {
if (i == tdx_guest->nr_ram_entries) {
return -1;
}
e = &tdx_guest->ram_entries[i++];
} while (address + length <= e->address || address >= e->address + e->length);
/*
* The to-be-accepted ram range must be fully contained by one
* RAM entry.
*/
if (e->address > address ||
e->address + e->length < address + length) {
return -1;
}
if (e->type == TDX_RAM_ADDED) {
return 0;
}
tmp_address = e->address;
tmp_length = e->length;
e->address = address;
e->length = length;
e->type = TDX_RAM_ADDED;
head_length = address - tmp_address;
if (head_length > 0) {
head_start = tmp_address;
tdx_add_ram_entry(head_start, head_length, TDX_RAM_UNACCEPTED);
}
tail_start = address + length;
if (tail_start < tmp_address + tmp_length) {
tail_length = tmp_address + tmp_length - tail_start;
tdx_add_ram_entry(tail_start, tail_length, TDX_RAM_UNACCEPTED);
}
return 0;
}
static int tdx_ram_entry_compare(const void *lhs_, const void* rhs_)
{
const TdxRamEntry *lhs = lhs_;
const TdxRamEntry *rhs = rhs_;
if (lhs->address == rhs->address) {
return 0;
}
if (le64_to_cpu(lhs->address) > le64_to_cpu(rhs->address)) {
return 1;
}
return -1;
}
static void tdx_init_ram_entries(void)
{
unsigned i, j, nr_e820_entries;
nr_e820_entries = e820_get_table(NULL);
tdx_guest->ram_entries = g_new(TdxRamEntry, nr_e820_entries);
for (i = 0, j = 0; i < nr_e820_entries; i++) {
uint64_t addr, len;
if (e820_get_entry(i, E820_RAM, &addr, &len)) {
tdx_guest->ram_entries[j].address = addr;
tdx_guest->ram_entries[j].length = len;
tdx_guest->ram_entries[j].type = TDX_RAM_UNACCEPTED;
j++;
}
}
tdx_guest->nr_ram_entries = j;
}
static void tdx_finalize_vm(Notifier *notifier, void *unused)
{
TdxFirmware *tdvf = &tdx_guest->tdvf;
TdxFirmwareEntry *entry;
tdx_init_ram_entries();
for_each_tdx_fw_entry(tdvf, entry) {
switch (entry->type) {
case TDVF_SECTION_TYPE_BFV:
@ -166,12 +266,21 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused)
entry->type);
exit(1);
}
if (tdx_accept_ram_range(entry->address, entry->size)) {
error_report("Failed to accept memory for TDVF section %d",
entry->type);
qemu_ram_munmap(-1, entry->mem_ptr, entry->size);
exit(1);
}
break;
default:
error_report("Unsupported TDVF section %d", entry->type);
exit(1);
}
}
qsort(tdx_guest->ram_entries, tdx_guest->nr_ram_entries,
sizeof(TdxRamEntry), &tdx_ram_entry_compare);
}
static Notifier tdx_machine_done_notify = {

View file

@ -20,6 +20,17 @@ typedef struct TdxGuestClass {
/* TDX requires bus frequency 25MHz */
#define TDX_APIC_BUS_CYCLES_NS 40
enum TdxRamType {
TDX_RAM_UNACCEPTED,
TDX_RAM_ADDED,
};
typedef struct TdxRamEntry {
uint64_t address;
uint64_t length;
enum TdxRamType type;
} TdxRamEntry;
typedef struct TdxGuest {
X86ConfidentialGuest parent_obj;
@ -34,6 +45,9 @@ typedef struct TdxGuest {
MemoryRegion *tdvf_mr;
TdxFirmware tdvf;
uint32_t nr_ram_entries;
TdxRamEntry *ram_entries;
} TdxGuest;
#ifdef CONFIG_TDX