@@ -58,3 +58,4 @@ CONFIG_IOH3420=y
CONFIG_I82801B11=y
CONFIG_SMBIOS=y
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
+CONFIG_MEASUREMENTS=y
@@ -1748,6 +1748,19 @@ Set QOM property @var{property} of object at location @var{path} to value @var{v
ETEXI
{
+ .name = "measurements",
+ .args_type = "",
+ .params = "",
+ .help = "Print system measurements",
+ .mhandler.cmd = print_measurements,
+ },
+STEXI
+@item measurements
+@findex measurements
+Redirect Print system measurements
+ETEXI
+
+ {
.name = "info",
.args_type = "item:s?",
.params = "[subcommand]",
@@ -55,6 +55,7 @@
#include "exec/address-spaces.h"
#include "hw/boards.h"
#include "qemu/cutils.h"
+#include "hw/misc/measurements.h"
#include <zlib.h>
@@ -1026,6 +1027,17 @@ static void rom_reset(void *unused)
}
}
+void measure_roms(void)
+{
+ Rom *rom;
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->data == NULL) {
+ continue;
+ }
+ extend_data(0, rom->data, rom->datasize);
+ }
+}
+
int rom_check_and_register_reset(void)
{
hwaddr addr = 0;
@@ -107,6 +107,7 @@ typedef struct AcpiMiscInfo {
unsigned dsdt_size;
uint16_t pvpanic_port;
uint16_t applesmc_io_base;
+ uint16_t measurements_io_base;
} AcpiMiscInfo;
typedef struct AcpiBuildPciBusHotplugState {
@@ -203,6 +204,7 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
info->tpm_version = tpm_get_version();
info->pvpanic_port = pvpanic_port();
info->applesmc_io_base = applesmc_port();
+ info->measurements_io_base = measurements_port();
}
/*
@@ -2185,6 +2187,26 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(dsdt, scope);
}
+ if (misc->measurements_io_base) {
+ scope = aml_scope("\\_SB.PCI0.ISA");
+ dev = aml_device("PCRS");
+
+ aml_append(dev, aml_name_decl("_HID", aml_string("CORE0001")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_io(AML_DECODE16, misc->measurements_io_base,
+ misc->measurements_io_base,
+ 0x01, 2)
+ );
+ aml_append(dev, aml_name_decl("_CRS", crs));
+
+ aml_append(scope, dev);
+ aml_append(dsdt, scope);
+ }
+
sb_scope = aml_scope("\\_SB");
{
build_memory_devices(sb_scope, nr_mem, pm->mem_hp_io_base,
@@ -2569,7 +2591,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
acpi_add_table(table_offsets, tables_blob);
build_hpet(tables_blob, tables->linker);
}
- if (misc.tpm_version != TPM_VERSION_UNSPEC) {
+ if (misc.tpm_version != TPM_VERSION_UNSPEC || misc.measurements_io_base) {
acpi_add_table(table_offsets, tables_blob);
build_tpm_tcpa(tables_blob, tables->linker, tables->tcpalog);
@@ -5,6 +5,7 @@ common-obj-$(CONFIG_ISA_DEBUG) += debugexit.o
common-obj-$(CONFIG_SGA) += sga.o
common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
+common-obj-$(CONFIG_MEASUREMENTS) += measurements.o
obj-$(CONFIG_VMPORT) += vmport.o
new file mode 100644
@@ -0,0 +1,205 @@
+/*
+ * QEMU boot measurement
+ *
+ * Copyright (c) 2016 CoreOS, Inc <mjg59@coreos.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "hw/isa/isa.h"
+#include "crypto/hash.h"
+#include "hw/misc/measurements.h"
+#include "monitor/monitor.h"
+#include "hw/loader.h"
+
+#define MEASUREMENT(obj) OBJECT_CHECK(MeasurementState, (obj), TYPE_MEASUREMENTS)
+
+typedef struct MeasurementState MeasurementState;
+
+struct MeasurementState {
+ ISADevice parent_obj;
+ MemoryRegion io_select;
+ MemoryRegion io_value;
+ uint16_t iobase;
+ uint8_t measurements[24][20];
+ uint8_t tmpmeasurement[20];
+ int write_count;
+ int read_count;
+ uint8_t pcr;
+};
+
+static void measurement_reset(DeviceState *dev)
+{
+ MeasurementState *s = MEASUREMENT(dev);
+
+ memset(s->measurements, 0, sizeof(s->measurements));
+ measure_roms();
+}
+
+static void measurement_select(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ MeasurementState *s = MEASUREMENT(opaque);
+ s->pcr = val;
+ s->read_count = 0;
+ s->write_count = 0;
+}
+
+static uint64_t measurement_version(void *opaque, hwaddr addr, unsigned size)
+{
+ return 0;
+}
+
+static uint64_t measurement_read(void *opaque, hwaddr addr, unsigned size)
+{
+ MeasurementState *s = MEASUREMENT(opaque);
+
+ if (s->read_count == 20) {
+ s->read_count = 0;
+ }
+ return s->measurements[s->pcr][s->read_count++];
+}
+
+static void extend(MeasurementState *s, int pcrnum, uint8_t *data)
+{
+ uint8_t *result;
+ char tmpbuf[40];
+ Error *err;
+ size_t resultlen = 0;
+
+ memcpy(tmpbuf, s->measurements[pcrnum], 20);
+ memcpy(tmpbuf + 20, data, 20);
+ qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA1, tmpbuf, 40, &result, &resultlen, &err);
+ memcpy(s->measurements[pcrnum], result, 20);
+ g_free(result);
+}
+
+static void measurement_value(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ MeasurementState *s = opaque;
+
+ s->tmpmeasurement[s->write_count++] = val;
+ if (s->write_count == 20) {
+ extend(s, s->pcr, s->tmpmeasurement);
+ s->write_count = 0;
+ }
+}
+
+void extend_data(int pcrnum, uint8_t *data, size_t len)
+{
+ uint8_t *result;
+ Error *err;
+ size_t resultlen = 0;
+ int ret;
+ Object *obj = object_resolve_path_type("", TYPE_MEASUREMENTS, NULL);
+
+ if (!obj) {
+ return;
+ }
+
+ ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA1, (char *)data, len, &result,
+ &resultlen, &err);
+ if (ret < 0) {
+ return;
+ }
+
+ extend(MEASUREMENT(obj), pcrnum, result);
+ g_free(result);
+}
+
+static const MemoryRegionOps measurement_select_ops = {
+ .write = measurement_select,
+ .read = measurement_version,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps measurement_value_ops = {
+ .write = measurement_value,
+ .read = measurement_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static void measurement_realize(DeviceState *dev, Error **errp)
+{
+ MeasurementState *s = MEASUREMENT(dev);
+
+ memory_region_init_io(&s->io_select, OBJECT(s), &measurement_select_ops, s,
+ "measurement-select", 1);
+ isa_register_ioport(&s->parent_obj, &s->io_select, s->iobase);
+ memory_region_init_io(&s->io_value, OBJECT(s), &measurement_value_ops, s,
+ "measurement-value", 1);
+ isa_register_ioport(&s->parent_obj, &s->io_value, s->iobase + 1);
+ measurement_reset(dev);
+}
+
+static Property measurement_props[] = {
+ DEFINE_PROP_UINT16(MEASUREMENTS_PROP_IO_BASE, MeasurementState, iobase, 0x620),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void measurement_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = measurement_realize;
+ dc->reset = measurement_reset;
+ dc->props = measurement_props;
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static const TypeInfo measurement = {
+ .name = TYPE_MEASUREMENTS,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(MeasurementState),
+ .class_init = measurement_class_init,
+};
+
+static void measurement_register_types(void)
+{
+ type_register_static(&measurement);
+}
+
+type_init(measurement_register_types);
+
+void print_measurements(Monitor *mon, const QDict *qdict)
+{
+ int i, j;
+ Object *obj = object_resolve_path_type("", TYPE_MEASUREMENTS, NULL);
+ MeasurementState *s;
+
+ if (!obj) {
+ return;
+ }
+
+ s = MEASUREMENT(obj);
+
+ for (i = 0; i < 24; i++) {
+ monitor_printf(mon, "0x%02x: ", i);
+ for (j = 0; j < 20; j++) {
+ monitor_printf(mon, "0x%02x ", s->measurements[i][j]);
+ }
+ monitor_printf(mon, "\n");
+ }
+}
new file mode 100644
@@ -0,0 +1,2 @@
+void print_measurements(Monitor *mon, const QDict *qdict);
+void extend_data(int pcrnum, uint8_t *data, size_t len);
@@ -24,6 +24,9 @@
#define APPLESMC_MAX_DATA_LENGTH 32
#define APPLESMC_PROP_IO_BASE "iobase"
+#define TYPE_MEASUREMENTS "measurements"
+#define MEASUREMENTS_PROP_IO_BASE "iobase"
+
static inline uint16_t applesmc_port(void)
{
Object *obj = object_resolve_path_type("", TYPE_APPLE_SMC, NULL);
@@ -34,6 +37,16 @@ static inline uint16_t applesmc_port(void)
return 0;
}
+static inline uint16_t measurements_port(void)
+{
+ Object *obj = object_resolve_path_type("", TYPE_MEASUREMENTS, NULL);
+
+ if (obj) {
+ return object_property_get_int(obj, MEASUREMENTS_PROP_IO_BASE, NULL);
+ }
+ return 0;
+}
+
#define TYPE_ISADMA "isa-dma"
#define ISADMA_CLASS(klass) \
@@ -133,6 +133,7 @@ void rom_reset_order_override(void);
int rom_copy(uint8_t *dest, hwaddr addr, size_t size);
void *rom_ptr(hwaddr addr);
void hmp_info_roms(Monitor *mon, const QDict *qdict);
+void measure_roms(void);
#define rom_add_file_fixed(_f, _a, _i) \
rom_add_file(_f, NULL, _a, _i, false, NULL)
@@ -32,6 +32,7 @@
#include "hw/pci/pci.h"
#include "sysemu/watchdog.h"
#include "hw/loader.h"
+#include "hw/misc/measurements.h"
#include "exec/gdbstub.h"
#include "net/net.h"
#include "net/slirp.h"
@@ -41,3 +41,4 @@ stub-obj-y += target-monitor-defs.o
stub-obj-y += target-get-monitor-def.o
stub-obj-y += vhost.o
stub-obj-y += iohandler.o
+stub-obj-y += measurements.o
new file mode 100644
@@ -0,0 +1,13 @@
+#include "qemu/osdep.h"
+#include "monitor/monitor.h"
+#include "hw/misc/measurements.h"
+
+void print_measurements(Monitor *mon, const QDict *qdict)
+{
+ monitor_printf(mon, "No measurement support\n");
+}
+
+void extend_data(int pcrnum, uint8_t *data, size_t len)
+{
+ return;
+}
Trusted Boot is based around having a trusted store of measurement data and a secure communications channel between that store and an attestation target. In actual hardware, that's a TPM. Since the TPM can only be accessed via the host system, this in turn requires that the TPM be able to perform reasonably complicated cryptographic functions in order to demonstrate its trusted state. In cloud environments, qemu is inherently trusted and the hypervisor infrastructure provides a trusted mechanism for extracting information from qemu and providing it to another system. This means we can skip the crypto and stick with the basic functionality - ie, providing a trusted store of measurement data. This driver provides a very small subset of TPM 1.2 functionality in the form of a bank of registers that can store SHA1 measurements of boot components. Performing a write to one of these registers will append the new 20 byte hash to the 20 bytes currently stored within the register, take a SHA1 of this 40 byte value and then replace the existing register contents with the new value. This ensures that a given value can only be obtained by performing the same sequence of writes. It also adds a monitor command to allow an external agent to extract this information from the running system and provide it over a secure communications channel. Finally, it measures each of the loaded ROMs into one of the registers at reset time. In combination with work in SeaBIOS and the kernel, this permits a fully measured boot in a virtualised environment without the overhead of a full TPM implementation. This version of the implementation depends on port io, but if there's interest I'll add mmio as well. Signed-off-by: Matthew Garrett <mjg59@coreos.com> --- default-configs/x86_64-softmmu.mak | 1 + hmp-commands.hx | 13 +++ hw/core/loader.c | 12 +++ hw/i386/acpi-build.c | 24 ++++- hw/misc/Makefile.objs | 1 + hw/misc/measurements.c | 205 +++++++++++++++++++++++++++++++++++++ hw/misc/measurements.h | 2 + include/hw/isa/isa.h | 13 +++ include/hw/loader.h | 1 + monitor.c | 1 + stubs/Makefile.objs | 1 + stubs/measurements.c | 13 +++ 12 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 hw/misc/measurements.c create mode 100644 hw/misc/measurements.h create mode 100644 stubs/measurements.c