From 7f45ea5083b845490bc3db2a449b6e8b71eab6ba Mon Sep 17 00:00:00 2001 From: Juro Bystricky Date: Mon, 14 Aug 2017 12:44:34 -0700 Subject: qemu: upgrade to 2.10-rc2 In order to support Nios2 emulation by QEMU, we need at least QEMU version 2.9. (From OE-Core rev: f2d725d9268563f7bbcac770a34aceacb56cb2aa) Signed-off-by: Juro Bystricky Signed-off-by: Richard Purdie --- ...rt-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch | 719 +++++++++++++++++++++ meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb | 55 ++ 2 files changed, 774 insertions(+) create mode 100644 meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch create mode 100644 meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb diff --git a/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch b/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch new file mode 100644 index 0000000000..f1dbaffeac --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch @@ -0,0 +1,719 @@ +From 5e9dd9063f514447ea4f54046793f4f01c297ed4 Mon Sep 17 00:00:00 2001 +From: Stefan Berger +Date: Sat, 31 Dec 2016 11:23:32 -0500 +Subject: [PATCH 4/4] Add support for VM suspend/resume for TPM TIS + +Extend the TPM TIS code to support suspend/resume. In case a command +is being processed by the external TPM when suspending, wait for the command +to complete to catch the result. In case the bottom half did not run, +run the one function the bottom half is supposed to run. This then +makes the resume operation work. + +The passthrough backend does not support suspend/resume operation +and is therefore blocked from suspend/resume and migration. + +The CUSE TPM's supported capabilities are tested and if sufficient +capabilities are implemented, suspend/resume, snapshotting and +migration are supported by the CUSE TPM. + +Signed-off-by: Stefan Berger + +Upstream-Status: Pending [https://lists.nongnu.org/archive/html/qemu-devel/2016-06/msg00252.html] +Signed-off-by: Patrick Ohly +--- + hw/tpm/tpm_passthrough.c | 130 +++++++++++++++++++++++-- + hw/tpm/tpm_tis.c | 137 +++++++++++++++++++++++++- + hw/tpm/tpm_tis.h | 2 + + hw/tpm/tpm_util.c | 223 +++++++++++++++++++++++++++++++++++++++++++ + hw/tpm/tpm_util.h | 7 ++ + include/sysemu/tpm_backend.h | 12 +++ + 6 files changed, 503 insertions(+), 8 deletions(-) + +diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c +index 44739ebad2..bc8072d0bc 100644 +--- a/hw/tpm/tpm_passthrough.c ++++ b/hw/tpm/tpm_passthrough.c +@@ -34,6 +34,8 @@ + #include "tpm_tis.h" + #include "tpm_util.h" + #include "tpm_ioctl.h" ++#include "migration/migration.h" ++#include "qapi/error.h" + + #define DEBUG_TPM 0 + +@@ -49,6 +51,7 @@ + #define TYPE_TPM_CUSE "tpm-cuse" + + static const TPMDriverOps tpm_passthrough_driver; ++static const VMStateDescription vmstate_tpm_cuse; + + /* data structures */ + typedef struct TPMPassthruThreadParams { +@@ -79,6 +82,10 @@ struct TPMPassthruState { + QemuMutex state_lock; + QemuCond cmd_complete; /* singnaled once tpm_busy is false */ + bool tpm_busy; ++ ++ Error *migration_blocker; ++ ++ TPMBlobBuffers tpm_blobs; + }; + + typedef struct TPMPassthruState TPMPassthruState; +@@ -306,6 +313,10 @@ static void tpm_passthrough_shutdown(TPMPassthruState *tpm_pt) + strerror(errno)); + } + } ++ if (tpm_pt->migration_blocker) { ++ migrate_del_blocker(tpm_pt->migration_blocker); ++ error_free(tpm_pt->migration_blocker); ++ } + } + + /* +@@ -360,12 +371,14 @@ static int tpm_passthrough_cuse_check_caps(TPMPassthruState *tpm_pt) + /* + * Initialize the external CUSE TPM + */ +-static int tpm_passthrough_cuse_init(TPMPassthruState *tpm_pt) ++static int tpm_passthrough_cuse_init(TPMPassthruState *tpm_pt, ++ bool is_resume) + { + int rc = 0; +- ptm_init init = { +- .u.req.init_flags = PTM_INIT_FLAG_DELETE_VOLATILE, +- }; ++ ptm_init init; ++ if (is_resume) { ++ init.u.req.init_flags = PTM_INIT_FLAG_DELETE_VOLATILE; ++ } + + if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) { + if (ioctl(tpm_pt->tpm_fd, PTM_INIT, &init) < 0) { +@@ -394,7 +407,7 @@ static int tpm_passthrough_startup_tpm(TPMBackend *tb) + tpm_passthrough_worker_thread, + &tpm_pt->tpm_thread_params); + +- tpm_passthrough_cuse_init(tpm_pt); ++ tpm_passthrough_cuse_init(tpm_pt, false); + + return 0; + } +@@ -466,6 +479,32 @@ static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, + return rc; + } + ++static int tpm_cuse_get_state_blobs(TPMBackend *tb, ++ bool decrypted_blobs, ++ TPMBlobBuffers *tpm_blobs) ++{ ++ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); ++ ++ assert(TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)); ++ ++ return tpm_util_cuse_get_state_blobs(tpm_pt->tpm_fd, decrypted_blobs, ++ tpm_blobs); ++} ++ ++static int tpm_cuse_set_state_blobs(TPMBackend *tb, ++ TPMBlobBuffers *tpm_blobs) ++{ ++ TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); ++ ++ assert(TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)); ++ ++ if (tpm_util_cuse_set_state_blobs(tpm_pt->tpm_fd, tpm_blobs)) { ++ return 1; ++ } ++ ++ return tpm_passthrough_cuse_init(tpm_pt, true); ++} ++ + static bool tpm_passthrough_get_startup_error(TPMBackend *tb) + { + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); +@@ -488,7 +527,7 @@ static void tpm_passthrough_deliver_request(TPMBackend *tb) + { + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + +- /* TPM considered busy once TPM Request scheduled for processing */ ++ /* TPM considered busy once TPM request scheduled for processing */ + qemu_mutex_lock(&tpm_pt->state_lock); + tpm_pt->tpm_busy = true; + qemu_mutex_unlock(&tpm_pt->state_lock); +@@ -601,6 +640,25 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) + return fd; + } + ++static void tpm_passthrough_block_migration(TPMPassthruState *tpm_pt) ++{ ++ ptm_cap caps; ++ ++ if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) { ++ caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | ++ PTM_CAP_STOP; ++ if (!TPM_CUSE_IMPLEMENTS_ALL(tpm_pt, caps)) { ++ error_setg(&tpm_pt->migration_blocker, ++ "Migration disabled: CUSE TPM lacks necessary capabilities"); ++ migrate_add_blocker(tpm_pt->migration_blocker); ++ } ++ } else { ++ error_setg(&tpm_pt->migration_blocker, ++ "Migration disabled: Passthrough TPM does not support migration"); ++ migrate_add_blocker(tpm_pt->migration_blocker); ++ } ++} ++ + static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) + { + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); +@@ -642,7 +700,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) + goto err_close_tpmdev; + } + /* init TPM for probing */ +- if (tpm_passthrough_cuse_init(tpm_pt)) { ++ if (tpm_passthrough_cuse_init(tpm_pt, false)) { + goto err_close_tpmdev; + } + } +@@ -659,6 +717,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) + } + } + ++ tpm_passthrough_block_migration(tpm_pt); + + return 0; + +@@ -766,10 +825,13 @@ static void tpm_passthrough_inst_init(Object *obj) + + qemu_mutex_init(&tpm_pt->state_lock); + qemu_cond_init(&tpm_pt->cmd_complete); ++ ++ vmstate_register(NULL, -1, &vmstate_tpm_cuse, obj); + } + + static void tpm_passthrough_inst_finalize(Object *obj) + { ++ vmstate_unregister(NULL, &vmstate_tpm_cuse, obj); + } + + static void tpm_passthrough_class_init(ObjectClass *klass, void *data) +@@ -802,6 +864,60 @@ static const char *tpm_passthrough_cuse_create_desc(void) + return "CUSE TPM backend driver"; + } + ++static void tpm_cuse_pre_save(void *opaque) ++{ ++ TPMPassthruState *tpm_pt = opaque; ++ TPMBackend *tb = &tpm_pt->parent; ++ ++ qemu_mutex_lock(&tpm_pt->state_lock); ++ /* wait for TPM to finish processing */ ++ if (tpm_pt->tpm_busy) { ++ qemu_cond_wait(&tpm_pt->cmd_complete, &tpm_pt->state_lock); ++ } ++ qemu_mutex_unlock(&tpm_pt->state_lock); ++ ++ /* get the decrypted state blobs from the TPM */ ++ tpm_cuse_get_state_blobs(tb, TRUE, &tpm_pt->tpm_blobs); ++} ++ ++static int tpm_cuse_post_load(void *opaque, ++ int version_id __attribute__((unused))) ++{ ++ TPMPassthruState *tpm_pt = opaque; ++ TPMBackend *tb = &tpm_pt->parent; ++ ++ return tpm_cuse_set_state_blobs(tb, &tpm_pt->tpm_blobs); ++} ++ ++static const VMStateDescription vmstate_tpm_cuse = { ++ .name = "cuse-tpm", ++ .version_id = 1, ++ .minimum_version_id = 0, ++ .minimum_version_id_old = 0, ++ .pre_save = tpm_cuse_pre_save, ++ .post_load = tpm_cuse_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32(tpm_blobs.permanent_flags, TPMPassthruState), ++ VMSTATE_UINT32(tpm_blobs.permanent.size, TPMPassthruState), ++ VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.permanent.buffer, ++ TPMPassthruState, 1, NULL, ++ tpm_blobs.permanent.size), ++ ++ VMSTATE_UINT32(tpm_blobs.volatil_flags, TPMPassthruState), ++ VMSTATE_UINT32(tpm_blobs.volatil.size, TPMPassthruState), ++ VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.volatil.buffer, ++ TPMPassthruState, 1, NULL, ++ tpm_blobs.volatil.size), ++ ++ VMSTATE_UINT32(tpm_blobs.savestate_flags, TPMPassthruState), ++ VMSTATE_UINT32(tpm_blobs.savestate.size, TPMPassthruState), ++ VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.savestate.buffer, ++ TPMPassthruState, 1, NULL, ++ tpm_blobs.savestate.size), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ + static const TPMDriverOps tpm_cuse_driver = { + .type = TPM_TYPE_CUSE_TPM, + .opts = tpm_passthrough_cmdline_opts, +diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c +index 14d9e83ea2..9b660cf737 100644 +--- a/hw/tpm/tpm_tis.c ++++ b/hw/tpm/tpm_tis.c +@@ -368,6 +368,8 @@ static void tpm_tis_receive_bh(void *opaque) + TPMTISEmuState *tis = &s->s.tis; + uint8_t locty = s->locty_number; + ++ tis->bh_scheduled = false; ++ + qemu_mutex_lock(&s->state_lock); + + tpm_tis_sts_set(&tis->loc[locty], +@@ -415,6 +417,8 @@ static void tpm_tis_receive_cb(TPMState *s, uint8_t locty, + qemu_mutex_unlock(&s->state_lock); + + qemu_bh_schedule(tis->bh); ++ ++ tis->bh_scheduled = true; + } + + /* +@@ -1030,9 +1034,140 @@ static void tpm_tis_reset(DeviceState *dev) + tpm_tis_do_startup_tpm(s); + } + ++ ++/* persistent state handling */ ++ ++static void tpm_tis_pre_save(void *opaque) ++{ ++ TPMState *s = opaque; ++ TPMTISEmuState *tis = &s->s.tis; ++ uint8_t locty = tis->active_locty; ++ ++ DPRINTF("tpm_tis: suspend: locty = %d : r_offset = %d, w_offset = %d\n", ++ locty, tis->loc[0].r_offset, tis->loc[0].w_offset); ++#ifdef DEBUG_TIS ++ tpm_tis_dump_state(opaque, 0); ++#endif ++ ++ qemu_mutex_lock(&s->state_lock); ++ ++ /* wait for outstanding request to complete */ ++ if (TPM_TIS_IS_VALID_LOCTY(locty) && ++ tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { ++ /* ++ * If we get here when the bh is scheduled but did not run, ++ * we won't get notified... ++ */ ++ if (!tis->bh_scheduled) { ++ /* backend thread to notify us */ ++ qemu_cond_wait(&s->cmd_complete, &s->state_lock); ++ } ++ if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { ++ /* bottom half did not run - run its function */ ++ qemu_mutex_unlock(&s->state_lock); ++ tpm_tis_receive_bh(opaque); ++ qemu_mutex_lock(&s->state_lock); ++ } ++ } ++ ++ qemu_mutex_unlock(&s->state_lock); ++ ++ /* copy current active read or write buffer into the buffer ++ written to disk */ ++ if (TPM_TIS_IS_VALID_LOCTY(locty)) { ++ switch (tis->loc[locty].state) { ++ case TPM_TIS_STATE_RECEPTION: ++ memcpy(tis->buf, ++ tis->loc[locty].w_buffer.buffer, ++ MIN(sizeof(tis->buf), ++ tis->loc[locty].w_buffer.size)); ++ tis->offset = tis->loc[locty].w_offset; ++ break; ++ case TPM_TIS_STATE_COMPLETION: ++ memcpy(tis->buf, ++ tis->loc[locty].r_buffer.buffer, ++ MIN(sizeof(tis->buf), ++ tis->loc[locty].r_buffer.size)); ++ tis->offset = tis->loc[locty].r_offset; ++ break; ++ default: ++ /* leak nothing */ ++ memset(tis->buf, 0x0, sizeof(tis->buf)); ++ break; ++ } ++ } ++} ++ ++static int tpm_tis_post_load(void *opaque, ++ int version_id __attribute__((unused))) ++{ ++ TPMState *s = opaque; ++ TPMTISEmuState *tis = &s->s.tis; ++ ++ uint8_t locty = tis->active_locty; ++ ++ if (TPM_TIS_IS_VALID_LOCTY(locty)) { ++ switch (tis->loc[locty].state) { ++ case TPM_TIS_STATE_RECEPTION: ++ memcpy(tis->loc[locty].w_buffer.buffer, ++ tis->buf, ++ MIN(sizeof(tis->buf), ++ tis->loc[locty].w_buffer.size)); ++ tis->loc[locty].w_offset = tis->offset; ++ break; ++ case TPM_TIS_STATE_COMPLETION: ++ memcpy(tis->loc[locty].r_buffer.buffer, ++ tis->buf, ++ MIN(sizeof(tis->buf), ++ tis->loc[locty].r_buffer.size)); ++ tis->loc[locty].r_offset = tis->offset; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ DPRINTF("tpm_tis: resume : locty = %d : r_offset = %d, w_offset = %d\n", ++ locty, tis->loc[0].r_offset, tis->loc[0].w_offset); ++ ++ return 0; ++} ++ ++static const VMStateDescription vmstate_locty = { ++ .name = "loc", ++ .version_id = 1, ++ .minimum_version_id = 0, ++ .minimum_version_id_old = 0, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32(state, TPMLocality), ++ VMSTATE_UINT32(inte, TPMLocality), ++ VMSTATE_UINT32(ints, TPMLocality), ++ VMSTATE_UINT8(access, TPMLocality), ++ VMSTATE_UINT32(sts, TPMLocality), ++ VMSTATE_UINT32(iface_id, TPMLocality), ++ VMSTATE_END_OF_LIST(), ++ } ++}; ++ + static const VMStateDescription vmstate_tpm_tis = { + .name = "tpm", +- .unmigratable = 1, ++ .version_id = 1, ++ .minimum_version_id = 0, ++ .minimum_version_id_old = 0, ++ .pre_save = tpm_tis_pre_save, ++ .post_load = tpm_tis_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_UINT32(s.tis.offset, TPMState), ++ VMSTATE_BUFFER(s.tis.buf, TPMState), ++ VMSTATE_UINT8(s.tis.active_locty, TPMState), ++ VMSTATE_UINT8(s.tis.aborting_locty, TPMState), ++ VMSTATE_UINT8(s.tis.next_locty, TPMState), ++ ++ VMSTATE_STRUCT_ARRAY(s.tis.loc, TPMState, TPM_TIS_NUM_LOCALITIES, 1, ++ vmstate_locty, TPMLocality), ++ ++ VMSTATE_END_OF_LIST() ++ } + }; + + static Property tpm_tis_properties[] = { +diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h +index a1df41fa21..b7fc0ea1a9 100644 +--- a/hw/tpm/tpm_tis.h ++++ b/hw/tpm/tpm_tis.h +@@ -54,6 +54,8 @@ typedef struct TPMLocality { + + typedef struct TPMTISEmuState { + QEMUBH *bh; ++ bool bh_scheduled; /* bh scheduled but did not run yet */ ++ + uint32_t offset; + uint8_t buf[TPM_TIS_BUFFER_MAX]; + +diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c +index 7b35429725..b6ff74d946 100644 +--- a/hw/tpm/tpm_util.c ++++ b/hw/tpm/tpm_util.c +@@ -22,6 +22,17 @@ + #include "qemu/osdep.h" + #include "tpm_util.h" + #include "tpm_int.h" ++#include "tpm_ioctl.h" ++#include "qemu/error-report.h" ++ ++#define DEBUG_TPM 0 ++ ++#define DPRINTF(fmt, ...) do { \ ++ if (DEBUG_TPM) { \ ++ fprintf(stderr, fmt, ## __VA_ARGS__); \ ++ } \ ++} while (0) ++ + + /* + * A basic test of a TPM device. We expect a well formatted response header +@@ -125,3 +136,215 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) + + return 1; + } ++ ++static void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) ++{ ++ g_free(tsb->buffer); ++ tsb->buffer = NULL; ++ tsb->size = 0; ++} ++ ++/* ++ * Transfer a TPM state blob from the TPM into a provided buffer. ++ * ++ * @fd: file descriptor to talk to the CUSE TPM ++ * @type: the type of blob to transfer ++ * @decrypted_blob: whether we request to receive decrypted blobs ++ * @tsb: the TPMSizeBuffer to fill with the blob ++ * @flags: the flags to return to the caller ++ */ ++static int tpm_util_cuse_get_state_blob(int fd, ++ uint8_t type, ++ bool decrypted_blob, ++ TPMSizedBuffer *tsb, ++ uint32_t *flags) ++{ ++ ptm_getstate pgs; ++ uint16_t offset = 0; ++ ptm_res res; ++ ssize_t n; ++ size_t to_read; ++ ++ tpm_sized_buffer_reset(tsb); ++ ++ pgs.u.req.state_flags = (decrypted_blob) ? PTM_STATE_FLAG_DECRYPTED : 0; ++ pgs.u.req.type = type; ++ pgs.u.req.offset = offset; ++ ++ if (ioctl(fd, PTM_GET_STATEBLOB, &pgs) < 0) { ++ error_report("CUSE TPM PTM_GET_STATEBLOB ioctl failed: %s", ++ strerror(errno)); ++ goto err_exit; ++ } ++ res = pgs.u.resp.tpm_result; ++ if (res != 0 && (res & 0x800) == 0) { ++ error_report("Getting the stateblob (type %d) failed with a TPM " ++ "error 0x%x", type, res); ++ goto err_exit; ++ } ++ ++ *flags = pgs.u.resp.state_flags; ++ ++ tsb->buffer = g_malloc(pgs.u.resp.totlength); ++ memcpy(tsb->buffer, pgs.u.resp.data, pgs.u.resp.length); ++ tsb->size = pgs.u.resp.length; ++ ++ /* if there are bytes left to get use read() interface */ ++ while (tsb->size < pgs.u.resp.totlength) { ++ to_read = pgs.u.resp.totlength - tsb->size; ++ if (unlikely(to_read > SSIZE_MAX)) { ++ to_read = SSIZE_MAX; ++ } ++ ++ n = read(fd, &tsb->buffer[tsb->size], to_read); ++ if (n != to_read) { ++ error_report("Could not read stateblob (type %d) : %s", ++ type, strerror(errno)); ++ goto err_exit; ++ } ++ tsb->size += to_read; ++ } ++ ++ DPRINTF("tpm_util: got state blob type %d, %d bytes, flags 0x%08x, " ++ "decrypted=%d\n", type, tsb->size, *flags, decrypted_blob); ++ ++ return 0; ++ ++err_exit: ++ return 1; ++} ++ ++int tpm_util_cuse_get_state_blobs(int tpm_fd, ++ bool decrypted_blobs, ++ TPMBlobBuffers *tpm_blobs) ++{ ++ if (tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_PERMANENT, ++ decrypted_blobs, ++ &tpm_blobs->permanent, ++ &tpm_blobs->permanent_flags) || ++ tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_VOLATILE, ++ decrypted_blobs, ++ &tpm_blobs->volatil, ++ &tpm_blobs->volatil_flags) || ++ tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_SAVESTATE, ++ decrypted_blobs, ++ &tpm_blobs->savestate, ++ &tpm_blobs->savestate_flags)) { ++ goto err_exit; ++ } ++ ++ return 0; ++ ++ err_exit: ++ tpm_sized_buffer_reset(&tpm_blobs->volatil); ++ tpm_sized_buffer_reset(&tpm_blobs->permanent); ++ tpm_sized_buffer_reset(&tpm_blobs->savestate); ++ ++ return 1; ++} ++ ++static int tpm_util_cuse_do_set_stateblob_ioctl(int fd, ++ uint32_t flags, ++ uint32_t type, ++ uint32_t length) ++{ ++ ptm_setstate pss; ++ ++ pss.u.req.state_flags = flags; ++ pss.u.req.type = type; ++ pss.u.req.length = length; ++ ++ if (ioctl(fd, PTM_SET_STATEBLOB, &pss) < 0) { ++ error_report("CUSE TPM PTM_SET_STATEBLOB ioctl failed: %s", ++ strerror(errno)); ++ return 1; ++ } ++ ++ if (pss.u.resp.tpm_result != 0) { ++ error_report("Setting the stateblob (type %d) failed with a TPM " ++ "error 0x%x", type, pss.u.resp.tpm_result); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * Transfer a TPM state blob to the CUSE TPM. ++ * ++ * @fd: file descriptor to talk to the CUSE TPM ++ * @type: the type of TPM state blob to transfer ++ * @tsb: TPMSizeBuffer containing the TPM state blob ++ * @flags: Flags describing the (encryption) state of the TPM state blob ++ */ ++static int tpm_util_cuse_set_state_blob(int fd, ++ uint32_t type, ++ TPMSizedBuffer *tsb, ++ uint32_t flags) ++{ ++ uint32_t offset = 0; ++ ssize_t n; ++ size_t to_write; ++ ++ /* initiate the transfer to the CUSE TPM */ ++ if (tpm_util_cuse_do_set_stateblob_ioctl(fd, flags, type, 0)) { ++ return 1; ++ } ++ ++ /* use the write() interface for transferring the state blob */ ++ while (offset < tsb->size) { ++ to_write = tsb->size - offset; ++ if (unlikely(to_write > SSIZE_MAX)) { ++ to_write = SSIZE_MAX; ++ } ++ ++ n = write(fd, &tsb->buffer[offset], to_write); ++ if (n != to_write) { ++ error_report("Writing the stateblob (type %d) failed: %s", ++ type, strerror(errno)); ++ goto err_exit; ++ } ++ offset += to_write; ++ } ++ ++ /* inidicate that the transfer is finished */ ++ if (tpm_util_cuse_do_set_stateblob_ioctl(fd, flags, type, 0)) { ++ goto err_exit; ++ } ++ ++ DPRINTF("tpm_util: set the state blob type %d, %d bytes, flags 0x%08x\n", ++ type, tsb->size, flags); ++ ++ return 0; ++ ++err_exit: ++ return 1; ++} ++ ++int tpm_util_cuse_set_state_blobs(int tpm_fd, ++ TPMBlobBuffers *tpm_blobs) ++{ ++ ptm_res res; ++ ++ if (ioctl(tpm_fd, PTM_STOP, &res) < 0) { ++ error_report("tpm_passthrough: Could not stop " ++ "the CUSE TPM: %s (%i)", ++ strerror(errno), errno); ++ return 1; ++ } ++ ++ if (tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_PERMANENT, ++ &tpm_blobs->permanent, ++ tpm_blobs->permanent_flags) || ++ tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_VOLATILE, ++ &tpm_blobs->volatil, ++ tpm_blobs->volatil_flags) || ++ tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_SAVESTATE, ++ &tpm_blobs->savestate, ++ tpm_blobs->savestate_flags)) { ++ return 1; ++ } ++ ++ return 0; ++} +diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h +index df76245e6e..c24071d812 100644 +--- a/hw/tpm/tpm_util.h ++++ b/hw/tpm/tpm_util.h +@@ -26,4 +26,11 @@ + + int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); + ++int tpm_util_cuse_get_state_blobs(int tpm_fd, ++ bool decrypted_blobs, ++ TPMBlobBuffers *tpm_blobs); ++ ++int tpm_util_cuse_set_state_blobs(int tpm_fd, ++ TPMBlobBuffers *tpm_blobs); ++ + #endif /* TPM_TPM_UTIL_H */ +diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h +index b58f52d39f..3403821b9d 100644 +--- a/include/sysemu/tpm_backend.h ++++ b/include/sysemu/tpm_backend.h +@@ -62,6 +62,18 @@ typedef struct TPMSizedBuffer { + uint8_t *buffer; + } TPMSizedBuffer; + ++/* blobs from the TPM; part of VM state when migrating */ ++typedef struct TPMBlobBuffers { ++ uint32_t permanent_flags; ++ TPMSizedBuffer permanent; ++ ++ uint32_t volatil_flags; ++ TPMSizedBuffer volatil; ++ ++ uint32_t savestate_flags; ++ TPMSizedBuffer savestate; ++} TPMBlobBuffers; ++ + struct TPMDriverOps { + enum TpmType type; + const QemuOptDesc *opts; +-- +2.11.0 + diff --git a/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb b/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb new file mode 100644 index 0000000000..04d656ba39 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb @@ -0,0 +1,55 @@ +require qemu.inc + +inherit ptest + +RDEPENDS_${PN}-ptest = "bash make" + +LIC_FILES_CHKSUM = "file://COPYING;md5=441c28d2cf86e15a37fa47e15a72fbac \ + file://COPYING.LIB;endline=24;md5=c04def7ae38850e7d3ef548588159913" + +SRC_URI = "http://wiki.qemu-project.org/download/${BP}.tar.bz2 \ + file://powerpc_rom.bin \ + file://disable-grabs.patch \ + file://exclude-some-arm-EABI-obsolete-syscalls.patch \ + file://wacom.patch \ + file://add-ptest-in-makefile.patch \ + file://run-ptest \ + file://qemu-enlarge-env-entry-size.patch \ + file://no-valgrind.patch \ + file://pathlimit.patch \ + file://qemu-2.5.0-cflags.patch \ + file://glibc-2.25.patch \ + file://0001-Provide-support-for-the-CUSE-TPM.patch \ + file://0002-Introduce-condition-to-notify-waiters-of-completed-c.patch \ + file://0003-Introduce-condition-in-TPM-backend-for-notification.patch \ + file://0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch \ + file://apic-fixup-fallthrough-to-PIC.patch \ + " + +SRC_URI_append_class-native = " \ + file://fix-libcap-header-issue-on-some-distro.patch \ + file://cpus.c-qemu_cpu_kick_thread_debugging.patch \ + " +SRC_URI[md5sum] = "634c498476e4b5643cf7a89e7416d0ae" +SRC_URI[sha256sum] = "9f8aaa2839634a2226a49d0f305ef922a7b0bc850d343aaee41948fd6f45700a" + +COMPATIBLE_HOST_mipsarchn32 = "null" +COMPATIBLE_HOST_mipsarchn64 = "null" + +do_install_append() { + # Prevent QA warnings about installed ${localstatedir}/run + if [ -d ${D}${localstatedir}/run ]; then rmdir ${D}${localstatedir}/run; fi + install -Dm 0755 ${WORKDIR}/powerpc_rom.bin ${D}${datadir}/qemu +} + +do_compile_ptest() { + make buildtest-TESTS +} + +do_install_ptest() { + cp -rL ${B}/tests ${D}${PTEST_PATH} + find ${D}${PTEST_PATH}/tests -type f -name "*.[Sshcod]" | xargs -i rm -rf {} + + cp ${S}/tests/Makefile.include ${D}${PTEST_PATH}/tests +} + -- cgit v1.2.3-54-g00ecf