diff --git a/devicemodel/hw/mmio/core.c b/devicemodel/hw/mmio/core.c index 05784e8fd..dab94a4a0 100644 --- a/devicemodel/hw/mmio/core.c +++ b/devicemodel/hw/mmio/core.c @@ -325,22 +325,6 @@ struct mmio_dev_ops tpm2 = { }; DEFINE_MMIO_DEV(tpm2); -/* @pre (pt_tpm2 == true) */ -uint64_t get_mmio_dev_tpm2_base_gpa(void) -{ - int i; - uint64_t base_gpa = 0UL; - - for (i = 0; i < mmio_dev_idx; i++) { - if (!strcmp(mmio_devs[i].name, "MSFT0101")) { - base_gpa = mmio_devs[i].dev.res[0].user_vm_pa; - break; - } - } - - return base_gpa; -} - struct mmio_dev_ops pt_mmiodev = { .name = "MMIODEV", /* ToDo: we may allocate the gpa MMIO resource in a reserved MMIO region diff --git a/devicemodel/hw/platform/acpi/acpi.c b/devicemodel/hw/platform/acpi/acpi.c index 315bf3942..a0a7295b6 100644 --- a/devicemodel/hw/platform/acpi/acpi.c +++ b/devicemodel/hw/platform/acpi/acpi.c @@ -126,9 +126,6 @@ struct basl_fio { char f_name[MAXPATHLEN]; }; -#define EFPRINTF(...) fprintf(__VA_ARGS__) -#define EFFLUSH(x) fflush(x) - static bool acpi_table_is_valid(int num); static int @@ -783,39 +780,6 @@ basl_fwrite_facs(FILE *fp, struct vmctx *ctx) return 0; } -static int -basl_fwrite_tpm2(FILE *fp, struct vmctx *ctx) -{ - EFPRINTF(fp, "/*\n"); - EFPRINTF(fp, " * dm TPM2 template\n"); - EFPRINTF(fp, " */\n"); - - EFPRINTF(fp, "[0004]\t\tSignature : \"TPM2\"\n"); - EFPRINTF(fp, "[0004]\t\tTable Length : 0000004C\n"); - EFPRINTF(fp, "[0001]\t\tRevision : 00\n"); - EFPRINTF(fp, "[0001]\t\tChecksum : 00\n"); - EFPRINTF(fp, "[0006]\t\tOem ID : \"ACRNDM\"\n"); - EFPRINTF(fp, "[0008]\t\tOem Table ID : \"DMTPM2 \"\n"); - EFPRINTF(fp, "[0004]\t\tOem Revision : 00000000\n"); - - /* iasl will fill the compiler ID/revision fields */ - EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n"); - EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n"); - - EFPRINTF(fp, "[0002]\t\tPlatform Class : 0000\n"); - EFPRINTF(fp, "[0002]\t\tReserved : 0000\n"); - EFPRINTF(fp, "[0008]\t\tControl Address : %016lX\n", get_tpm_crb_mmio_addr() + (long)CRB_REGS_CTRL_REQ); - EFPRINTF(fp, "[0004]\t\tStart Method : 00000007\n"); - - EFPRINTF(fp, "[0012]\t\tMethod Parameters : 00 00 00 00 00 00 00 00 00 00 00 00\n"); - EFPRINTF(fp, "[0004]\t\tMinimum Log Length : 00000000\n"); - EFPRINTF(fp, "[0008]\t\tLog Address : 0000000000000000\n"); - - EFFLUSH(fp); - - return 0; -} - /* * Helper routines for writing to the DSDT from other modules. */ @@ -885,28 +849,6 @@ dsdt_fixed_mem32(uint32_t base, uint32_t length) dsdt_line(" )"); } -static void tpm2_crb_fwrite_dsdt(void) -{ - dsdt_line(" Scope (\\_SB)"); - dsdt_line(" {"); - dsdt_line(" Device (TPM)"); - dsdt_line(" {"); - dsdt_line(" Name (_HID, \"MSFT0101\" /* TPM 2.0 Security Device */) // _HID: Hardware ID"); - dsdt_line(" Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings"); - dsdt_line(" {"); - dsdt_indent(4); - /* TODO: consider a better framework for mmio likes pci's vdev_write_dsdt. */ - dsdt_fixed_mem32(get_tpm_crb_mmio_addr(), TPM_CRB_MMIO_SIZE); - dsdt_unindent(4); - dsdt_line(" })"); - dsdt_line(" Method (_STA, 0, NotSerialized) // _STA: Status"); - dsdt_line(" {"); - dsdt_line(" Return (0x0F)"); - dsdt_line(" }"); - dsdt_line(" }"); - dsdt_line(" }"); -} - static int basl_fwrite_dsdt(FILE *fp, struct vmctx *ctx) { @@ -953,9 +895,6 @@ basl_fwrite_dsdt(FILE *fp, struct vmctx *ctx) pm_write_dsdt(ctx, basl_ncpu); - if (ctx->tpm_dev || pt_tpm2) - tpm2_crb_fwrite_dsdt(); - dsdt_line("}"); if (dsdt_error != 0) diff --git a/devicemodel/hw/platform/tpm/tpm.c b/devicemodel/hw/platform/tpm/tpm.c index 62aec452d..b2fd2c367 100644 --- a/devicemodel/hw/platform/tpm/tpm.c +++ b/devicemodel/hw/platform/tpm/tpm.c @@ -9,7 +9,9 @@ #include #include #include +#include +#include "acpi.h" #include "vmmapi.h" #include "tpm.h" #include "tpm_internal.h" @@ -27,17 +29,145 @@ static int tpm_debug; #define STR_MAX_LEN 1024U static char *sock_path = NULL; static uint32_t vtpm_crb_mmio_addr = 0U; +struct acpi_dev_pt_ops pt_acpi_dev; + +#define TPM2_TABLE_SYSFS_PATH "/sys/firmware/acpi/tables/TPM2" + +static inline int checksum(uint8_t *buf, size_t size) +{ + size_t i; + uint8_t sum = 0; + for (i = 0; i < size; i++) + sum += buf[i]; + return sum == 0; +} + +static int read_sysfs_tpm2_table(struct acpi_table_tpm2 *tpm2_out) +{ + FILE *fp; + size_t ch_read; + struct acpi_table_tpm2 tpm2 = { 0 }; + + fp = fopen(TPM2_TABLE_SYSFS_PATH, "r"); + if (!fp) + return -errno; + + ch_read = fread(&tpm2, 1, sizeof(tpm2), fp); + fclose(fp); + + /* we may read less than sizeof(tpm2) as laml and lasa are optional */ + if (!ch_read) + return -errno; + + if (strncmp(tpm2.header.signature, "TPM2", 4) || + !checksum((uint8_t *)&tpm2, tpm2.header.length)) + return -EINVAL; + + memcpy(tpm2_out, &tpm2, ch_read); + + return 0; +} + +static int is_tpm2_eventlog_supported(struct acpi_table_tpm2 *tpm2) +{ + /* Per TCG ACPI spec ver 1.2 rev 8, if LAML and LASA field are present, length is 76 */ + return ((tpm2->header.length == 76) && tpm2->lasa && tpm2->laml); +} + +static int is_hid_tpm2_device(char *hid) +{ + int ret; + struct acpi_dev_pt_ops *ops = &pt_acpi_dev; + ret = get_more_acpi_dev_info(hid, 0, ops); + if (ret) + return false; + return (strstr(ops->modalias, "MSFT0101") != NULL); +} + +/** + * Add TPM2 device with HID hid to tpm2dev to be passed-through. + * If eventlog is also supported, it will be added to the second resource of the + * tpm2dev->dev. + * + * @pre: hid should be a valid HID of the TPM2 device being passed-through. + * @pre: tpm2dev != NULL + */ +static int init_tpm2_pt(char *hid, struct mmio_dev *tpm2dev) +{ + int err = 0; + uint64_t tpm2_buffer_hpa, tpm2_buffer_size; + uint32_t base = 0; + struct acpi_table_tpm2 tpm2 = { 0 }; + + /* TODO: Currently we did not validate if the hid is a valid one. + * We trust it to be valid as specifying --acpidev_pt is regarded + * as root user operation. + */ + if (!hid || !*hid) + return -EINVAL; + + /* parse /proc/iomem to find the address and size of tpm buffer */ + if (!get_mmio_hpa_resource(hid, &tpm2_buffer_hpa, &tpm2_buffer_size)) + return -ENODEV; + + err = read_sysfs_tpm2_table(&tpm2); + if (err) + return err; + + if ((tpm2.start_method != 6) && (tpm2.start_method != 7)) { + pr_err("TPM2 start method %d not supported.\n", tpm2.start_method); + return -EINVAL; + } + + if ((tpm2.control_address < tpm2_buffer_hpa) || + (tpm2.control_address > tpm2_buffer_hpa + tpm2_buffer_size)) { + pr_err("TPM2 control area address 0x%016lX outside of the \ + requested address region 0x%016lX-0x%016lX\n", tpm2.control_address, + tpm2_buffer_hpa, tpm2_buffer_hpa + tpm2_buffer_size); + return -EINVAL; + } + + if ((tpm2.control_address >= MMIO_DEV_BASE) && (tpm2.control_address <= MMIO_DEV_LIMIT)) { + /* we don't support if tpm2 buffer falls in MMIO region */ + return -EINVAL; + } + + strncpy(tpm2dev->name, "MSFT0101", 8); + strncpy(tpm2dev->dev.name, "tpm2", 4); + tpm2dev->dev.res[0].host_pa = tpm2_buffer_hpa; + tpm2dev->dev.res[0].user_vm_pa = tpm2_buffer_hpa; + tpm2dev->dev.res[0].size = tpm2_buffer_size; + + /* Search for eventlog */ + if (is_tpm2_eventlog_supported(&tpm2) && + !mmio_dev_alloc_gpa_resource32(&base, tpm2.laml)) { + tpm2dev->dev.res[1].host_pa = tpm2.lasa; + tpm2dev->dev.res[1].user_vm_pa = base; + tpm2dev->dev.res[1].size = tpm2.laml; + } + + pt_tpm2 = true; + + return 0; +} uint32_t get_vtpm_crb_mmio_addr(void) { return vtpm_crb_mmio_addr; } +static struct acrn_mmiodev *get_tpm2_mmio_dev() +{ + struct mmio_dev *dev = get_mmiodev("MSFT0101"); + return dev ? &dev->dev : NULL; +} + uint32_t get_tpm_crb_mmio_addr(void) { uint32_t base; if (pt_tpm2) { - base = (uint32_t)get_mmio_dev_tpm2_base_gpa(); + struct acrn_mmiodev *d = get_tpm2_mmio_dev(); + base = d ? (uint32_t)d->res[0].host_pa : 0U; } else { base = get_vtpm_crb_mmio_addr(); } @@ -45,6 +175,23 @@ uint32_t get_tpm_crb_mmio_addr(void) return base; } +static uint32_t get_tpm_crb_mmio_size(void) +{ + struct acrn_mmiodev *d = get_tpm2_mmio_dev(); + return (pt_tpm2 && d) ? d->res[0].size : TPM_CRB_MMIO_SIZE; +} + +static uint32_t get_tpm2_table_minimal_log_length(void) +{ + struct acrn_mmiodev *d = get_tpm2_mmio_dev(); + return (pt_tpm2 && d && d->res[1].host_pa) ? d->res[1].size : 0U; +} + +static uint64_t get_tpm2_table_log_address(void) +{ + struct acrn_mmiodev *d = get_tpm2_mmio_dev(); + return (pt_tpm2 && d && d->res[1].host_pa) ? d->res[1].user_vm_pa : 0UL; +} enum { SOCK_PATH_OPT = 0 @@ -109,5 +256,64 @@ void deinit_vtpm2(struct vmctx *ctx) } } -struct acpi_dev_pt_ops pt_tpm2_dev_ops = { 0 }; +static void tpm_write_dsdt(struct vmctx *ctx) +{ + if (ctx->tpm_dev || pt_tpm2) { + dsdt_line(" Scope (\\_SB)"); + dsdt_line(" {"); + dsdt_line(" Device (TPM)"); + dsdt_line(" {"); + dsdt_line(" Name (_HID, \"MSFT0101\" /* TPM 2.0 Security Device */) // _HID: Hardware ID"); + dsdt_line(" Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings"); + dsdt_line(" {"); + dsdt_indent(4); + dsdt_fixed_mem32(get_tpm_crb_mmio_addr(), get_tpm_crb_mmio_size()); + dsdt_unindent(4); + dsdt_line(" })"); + dsdt_line(" Method (_STA, 0, NotSerialized) // _STA: Status"); + dsdt_line(" {"); + dsdt_line(" Return (0x0F)"); + dsdt_line(" }"); + dsdt_line(" }"); + dsdt_line(" }"); + } +} + +int basl_fwrite_tpm2(FILE *fp, struct vmctx *ctx) +{ + EFPRINTF(fp, "/*\n"); + EFPRINTF(fp, " * dm TPM2 template\n"); + EFPRINTF(fp, " */\n"); + + EFPRINTF(fp, "[0004]\t\tSignature : \"TPM2\"\n"); + EFPRINTF(fp, "[0004]\t\tTable Length : 0000004C\n"); + EFPRINTF(fp, "[0001]\t\tRevision : 00\n"); + EFPRINTF(fp, "[0001]\t\tChecksum : 00\n"); + EFPRINTF(fp, "[0006]\t\tOem ID : \"ACRNDM\"\n"); + EFPRINTF(fp, "[0008]\t\tOem Table ID : \"DMTPM2 \"\n"); + EFPRINTF(fp, "[0004]\t\tOem Revision : 00000000\n"); + + /* iasl will fill the compiler ID/revision fields */ + EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n"); + EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n"); + + EFPRINTF(fp, "[0002]\t\tPlatform Class : 0000\n"); + EFPRINTF(fp, "[0002]\t\tReserved : 0000\n"); + EFPRINTF(fp, "[0008]\t\tControl Address : %016lX\n", get_tpm_crb_mmio_addr() + (long)CRB_REGS_CTRL_REQ); + EFPRINTF(fp, "[0004]\t\tStart Method : 00000007\n"); + + EFPRINTF(fp, "[0012]\t\tMethod Parameters : 00 00 00 00 00 00 00 00 00 00 00 00\n"); + EFPRINTF(fp, "[0004]\t\tMinimum Log Length : %08X\n", get_tpm2_table_minimal_log_length()); + EFPRINTF(fp, "[0008]\t\tLog Address : %016lX\n", get_tpm2_table_log_address()); + + EFFLUSH(fp); + + return 0; +} + +struct acpi_dev_pt_ops pt_tpm2_dev_ops = { + .match = is_hid_tpm2_device, + .init = init_tpm2_pt, + .write_dsdt = tpm_write_dsdt, +}; DEFINE_ACPI_PT_DEV(pt_tpm2_dev_ops); diff --git a/devicemodel/include/acpi.h b/devicemodel/include/acpi.h index 4bf14b77e..04a939905 100644 --- a/devicemodel/include/acpi.h +++ b/devicemodel/include/acpi.h @@ -65,6 +65,9 @@ struct acpi_table_hdr { /* All dynamic table entry no. */ #define NHLT_ENTRY_NO 8 +#define EFPRINTF(...) fprintf(__VA_ARGS__) +#define EFFLUSH(x) fflush(x) + void acpi_table_enable(int num); uint32_t get_acpi_base(void); uint32_t get_acpi_table_length(void); diff --git a/devicemodel/include/mmio_dev.h b/devicemodel/include/mmio_dev.h index 455788a2d..90f118656 100644 --- a/devicemodel/include/mmio_dev.h +++ b/devicemodel/include/mmio_dev.h @@ -38,7 +38,6 @@ int init_mmio_devs(struct vmctx *ctx); void deinit_mmio_devs(struct vmctx *ctx); int mmio_dev_alloc_gpa_resource32(uint32_t *addr, uint32_t size_in); -uint64_t get_mmio_dev_tpm2_base_gpa(void); #define MMIO_DEV_BASE 0xF0000000U #define MMIO_DEV_LIMIT 0xFE000000U diff --git a/devicemodel/include/tpm.h b/devicemodel/include/tpm.h index 82b948141..f0b751e1b 100644 --- a/devicemodel/include/tpm.h +++ b/devicemodel/include/tpm.h @@ -8,11 +8,26 @@ #ifndef _TPM_H_ #define _TPM_H_ +#include "mmio_dev.h" +#include "acpi.h" + #define TPM_CRB_MMIO_ADDR 0xFED40000UL #define TPM_CRB_MMIO_SIZE 0x5000U uint32_t get_vtpm_crb_mmio_addr(void); uint32_t get_tpm_crb_mmio_addr(void); +int basl_fwrite_tpm2(FILE *fp, struct vmctx *ctx); + +struct acpi_table_tpm2 { + struct acpi_table_hdr header; + uint16_t platform_class; + uint16_t reserved; + uint64_t control_address; + uint32_t start_method; + uint8_t start_method_spec_para[12]; + uint32_t laml; + uint64_t lasa; +} __attribute__((packed)); /* TPM CRB registers */ enum {