diff mbox

hw/misc: Add simple measurement hardware

Message ID 1466725019-3228-1-git-send-email-mjg59@coreos.com
State New
Headers show

Commit Message

Matthew Garrett June 23, 2016, 11:36 p.m. UTC
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

Comments

Matthew Garrett July 14, 2016, 7:32 a.m. UTC | #1
Any feedback on this?
Daniel P. Berrangé July 14, 2016, 2:54 p.m. UTC | #2
On Thu, Jun 23, 2016 at 04:36:59PM -0700, Matthew Garrett wrote:
> 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.

Will it be capable of workubg with edk2/OVMF/AVMF as well as SeaBIOS ?

> This version of the implementation depends on port io, but if there's interest I'll
> add mmio as well.

So I guess this is the reason you've only enabled it for x86_64. Since we're
inventing an entirely new type of device here, not copying existing hardware,
I think it'd desirable if we created one that was supported across arches,
particularly aarch64, since that's the new hotness.  With the convergance
of both x86_64 and aarch64 to EFI, it'd be nice to aim for parity for this
trusted boot support too if practical.


> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 98b4b1a..6a31392 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -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
> +
> +    {

Thus since is just reporting info about a device, from a HMP POV,
it would fit better as an 'info' sub-command, eg 'info measurements'.
The QMP equivalent would be a 'query-measurements' command


> +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");
> +    }
> +}

The preferred approach to supporting monitor commands these
days is to first define a schema for the information to be
output in qapi-schema.json.  Then implement a QMP command
that returns an instance of the data structure you defined.
Finally the HMP command, would invoke the QMP command to
get the data to be printed.

For a fairly clear example of best practice, take a look at
these 2 commits:

  commit dc3dd0d2bed6edf3b60041f31200c674348168e9
  Author: Stefan Hajnoczi <stefanha@redhat.com>
  Date:   Thu Feb 27 11:48:42 2014 +0100

    qmp: add query-iothreads command

  commit 62313160cb5b6bdfbd77a063e94a5a7d25e59f2b
  Author: Ting Wang <kathy.wangting@huawei.com>
  Date:   Fri Jun 26 16:07:13 2015 +0800

    hmp: add info iothreads command

between them, they illustrate the various files to modify
and the current preferred implementation style for monitor
commands.

> diff --git a/hw/misc/measurements.h b/hw/misc/measurements.h
> new file mode 100644
> index 0000000..65ad246
> --- /dev/null
> +++ b/hw/misc/measurements.h
> @@ -0,0 +1,2 @@
> +void print_measurements(Monitor *mon, const QDict *qdict);
> +void extend_data(int pcrnum, uint8_t *data, size_t len);

'extend_data' is rather too generic a name, for expose across
QEMU. Just add a "measurements_" prefix for any exported methods
from the measurements device.


Regards,
Daniel
Matthew Garrett July 15, 2016, 12:10 a.m. UTC | #3
On Thu, Jul 14, 2016 at 11:54 PM, Daniel P. Berrange <berrange@redhat.com>
wrote:

> On Thu, Jun 23, 2016 at 04:36:59PM -0700, Matthew Garrett wrote:
> > 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.
>
> Will it be capable of workubg with edk2/OVMF/AVMF as well as SeaBIOS ?
>
> Yes, that will work fine.


> > This version of the implementation depends on port io, but if there's
> interest I'll
> > add mmio as well.
>
> So I guess this is the reason you've only enabled it for x86_64. Since
> we're
> inventing an entirely new type of device here, not copying existing
> hardware,
> I think it'd desirable if we created one that was supported across arches,
> particularly aarch64, since that's the new hotness.  With the convergance
> of both x86_64 and aarch64 to EFI, it'd be nice to aim for parity for this
> trusted boot support too if practical.
>

Fair. I can redo this so it's mmio everywhere.

>
> > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > index 98b4b1a..6a31392 100644
> > --- a/hmp-commands.hx
> > +++ b/hmp-commands.hx
> > @@ -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
> > +
> > +    {
>
> Thus since is just reporting info about a device, from a HMP POV,
> it would fit better as an 'info' sub-command, eg 'info measurements'.
> The QMP equivalent would be a 'query-measurements' command
>

Ok.


>
> > +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");
> > +    }
> > +}
>
> The preferred approach to supporting monitor commands these
> days is to first define a schema for the information to be
> output in qapi-schema.json.  Then implement a QMP command
> that returns an instance of the data structure you defined.
> Finally the HMP command, would invoke the QMP command to
> get the data to be printed.
>

Ok, thanks for the pointers!

>
> > diff --git a/hw/misc/measurements.h b/hw/misc/measurements.h
> > new file mode 100644
> > index 0000000..65ad246
> > --- /dev/null
> > +++ b/hw/misc/measurements.h
> > @@ -0,0 +1,2 @@
> > +void print_measurements(Monitor *mon, const QDict *qdict);
> > +void extend_data(int pcrnum, uint8_t *data, size_t len);
>
> 'extend_data' is rather too generic a name, for expose across
> QEMU. Just add a "measurements_" prefix for any exported methods
> from the measurements device.
>

Will do. Thanks for the feedback!
diff mbox

Patch

diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 6e3b312..6f0fcc3 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -58,3 +58,4 @@  CONFIG_IOH3420=y
 CONFIG_I82801B11=y
 CONFIG_SMBIOS=y
 CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
+CONFIG_MEASUREMENTS=y
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 98b4b1a..6a31392 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -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]",
diff --git a/hw/core/loader.c b/hw/core/loader.c
index 53e0e41..e1b7af7 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -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;
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 8ca2032..b148e27 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -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);
 
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ffb49c1..e7a784b 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -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
 
diff --git a/hw/misc/measurements.c b/hw/misc/measurements.c
new file mode 100644
index 0000000..ec142b6
--- /dev/null
+++ b/hw/misc/measurements.c
@@ -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");
+    }
+}
diff --git a/hw/misc/measurements.h b/hw/misc/measurements.h
new file mode 100644
index 0000000..65ad246
--- /dev/null
+++ b/hw/misc/measurements.h
@@ -0,0 +1,2 @@ 
+void print_measurements(Monitor *mon, const QDict *qdict);
+void extend_data(int pcrnum, uint8_t *data, size_t len);
diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h
index c87fbad..00694fd 100644
--- a/include/hw/isa/isa.h
+++ b/include/hw/isa/isa.h
@@ -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) \
diff --git a/include/hw/loader.h b/include/hw/loader.h
index 4879b63..cc3157d 100644
--- a/include/hw/loader.h
+++ b/include/hw/loader.h
@@ -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)
diff --git a/monitor.c b/monitor.c
index 6f960f1..6aa7ebc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -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"
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index 4b258a6..2ad7f81 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -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
diff --git a/stubs/measurements.c b/stubs/measurements.c
new file mode 100644
index 0000000..0485d2e
--- /dev/null
+++ b/stubs/measurements.c
@@ -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;
+}