mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 03:59:52 +00:00
Merge tpm 2018/05/23 v4
-----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJbCMvcAAoJEHWtZYAqC0IRaZkIAKYYlLTjuiSXjOi3pwAIK9MS kBXqEqQcAXeKXFVYfyrzxQGJK8vVm+I/P+L+Q3jOLanRWM+gZsxGpct7fDHAQQMY iY+o5RAZUJH9rpr9mgvim332A52B/TmPl3viU6SFVM8y7PSOm3Tm7NsC17ak7XDF rEY2q5Cou08SU5rTz6Dif2adoz9/YkITMIamz2nVmnjxS5zDCfe+DEh+TrQJXGt5 3n+Ywva7AzYCvuunKIscYD1QBOu7pMMOTduGkABduKyWr1oLeipG1QU/VTqz9S7O xDvySSZMYUohj2D71J7brc05gmMQb668geDIxuKmnXrSYgSoraJatGvfV0UTxOA= =BJKH -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2018-05-23-4' into staging Merge tpm 2018/05/23 v4 # gpg: Signature made Sat 26 May 2018 03:52:12 BST # gpg: using RSA key 75AD65802A0B4211 # gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: B818 B9CA DF90 89C2 D5CE C66B 75AD 6580 2A0B 4211 * remotes/stefanberger/tags/pull-tpm-2018-05-23-4: test: Add test cases that use the external swtpm with CRB interface docs: tpm: add VM save/restore example and troubleshooting guide tpm: extend TPM TIS with state migration support tpm: extend TPM emulator with state migration support Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b706ec24f1
@ -200,3 +200,109 @@ crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
||||
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
||||
...
|
||||
PCR-23: 00 00 00 00 00 00 00 00 ...
|
||||
|
||||
|
||||
=== Migration with the TPM emulator ===
|
||||
|
||||
The TPM emulator supports the following types of virtual machine migration:
|
||||
|
||||
- VM save / restore (migration into a file)
|
||||
- Network migration
|
||||
- Snapshotting (migration into storage like QoW2 or QED)
|
||||
|
||||
The following command sequences can be used to test VM save / restore.
|
||||
|
||||
|
||||
In a 1st terminal start an instance of a swtpm using the following command:
|
||||
|
||||
mkdir /tmp/mytpm1
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In a 2nd terminal start the VM:
|
||||
|
||||
qemu-system-x86_64 -display sdl -enable-kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-monitor stdio \
|
||||
test.img
|
||||
|
||||
Verify that the attached TPM is working as expected using applications inside
|
||||
the VM.
|
||||
|
||||
To store the state of the VM use the following command in the QEMU monitor in
|
||||
the 2nd terminal:
|
||||
|
||||
(qemu) migrate "exec:cat > testvm.bin"
|
||||
(qemu) quit
|
||||
|
||||
At this point a file called 'testvm.bin' should exists and the swtpm and QEMU
|
||||
processes should have ended.
|
||||
|
||||
To test 'VM restore' you have to start the swtpm with the same parameters
|
||||
as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be
|
||||
passed again on the command line.
|
||||
|
||||
In the 1st terminal restart the swtpm with the same command line as before:
|
||||
|
||||
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
||||
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
||||
--log level=20 --tpm2
|
||||
|
||||
In the 2nd terminal restore the state of the VM using the additonal
|
||||
'-incoming' option.
|
||||
|
||||
qemu-system-x86_64 -display sdl -enable-kvm \
|
||||
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
||||
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
|
||||
-tpmdev emulator,id=tpm0,chardev=chrtpm \
|
||||
-device tpm-tis,tpmdev=tpm0 \
|
||||
-incoming "exec:cat < testvm.bin" \
|
||||
test.img
|
||||
|
||||
|
||||
Troubleshooting migration:
|
||||
|
||||
There are several reasons why migration may fail. In case of problems,
|
||||
please ensure that the command lines adhere to the following rules and,
|
||||
if possible, that identical versions of QEMU and swtpm are used at all
|
||||
times.
|
||||
|
||||
VM save and restore:
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on VM restore
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM migration to 'localhost':
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
- swtpm command line parameters should point to two different
|
||||
directories on the source and destination swtpm (--tpmstate dir=...)
|
||||
(especially if different versions of libtpms were to be used on the
|
||||
same machine).
|
||||
|
||||
VM migration across the network:
|
||||
- QEMU command line parameters should be identical apart from the
|
||||
'-incoming' option on the destination side
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
VM Snapshotting:
|
||||
- QEMU command line parameters should be identical
|
||||
- swtpm command line parameters should be identical
|
||||
|
||||
|
||||
Besides that, migration failure reasons on the swtpm level may include
|
||||
the following:
|
||||
|
||||
- the versions of the swtpm on the source and destination sides are
|
||||
incompatible
|
||||
- downgrading of TPM state may not be supported
|
||||
- the source and destination libtpms were compiled with different
|
||||
compile-time options and the destination side refuses to accept the
|
||||
state
|
||||
- different migration keys are used on the source and destination side
|
||||
and the destination side cannot decrypt the migrated state
|
||||
(swtpm ... --migration-key ... )
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
* Author: Amarnath Valluri <amarnath.valluri@intel.com>
|
||||
*
|
||||
* Copyright (c) 2010 - 2013 IBM Corporation
|
||||
* Copyright (c) 2010 - 2013, 2018 IBM Corporation
|
||||
* Authors:
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
*
|
||||
@ -49,6 +49,19 @@
|
||||
#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
|
||||
|
||||
/* data structures */
|
||||
|
||||
/* 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;
|
||||
|
||||
typedef struct TPMEmulator {
|
||||
TPMBackend parent;
|
||||
|
||||
@ -64,6 +77,8 @@ typedef struct TPMEmulator {
|
||||
|
||||
unsigned int established_flag:1;
|
||||
unsigned int established_flag_cached:1;
|
||||
|
||||
TPMBlobBuffers state_blobs;
|
||||
} TPMEmulator;
|
||||
|
||||
|
||||
@ -293,7 +308,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||
static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
|
||||
bool is_resume)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||
ptm_init init = {
|
||||
@ -301,12 +317,17 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||
};
|
||||
ptm_res res;
|
||||
|
||||
trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
|
||||
|
||||
if (buffersize != 0 &&
|
||||
tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
trace_tpm_emulator_startup_tpm();
|
||||
if (is_resume) {
|
||||
init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE);
|
||||
}
|
||||
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
|
||||
sizeof(init)) < 0) {
|
||||
error_report("tpm-emulator: could not send INIT: %s",
|
||||
@ -325,6 +346,11 @@ err_exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
|
||||
{
|
||||
return tpm_emulator_startup_tpm_resume(tb, buffersize, false);
|
||||
}
|
||||
|
||||
static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||
@ -423,16 +449,21 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
|
||||
static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
|
||||
{
|
||||
Error *err = NULL;
|
||||
ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB |
|
||||
PTM_CAP_STOP;
|
||||
|
||||
error_setg(&tpm_emu->migration_blocker,
|
||||
"Migration disabled: TPM emulator not yet migratable");
|
||||
migrate_add_blocker(tpm_emu->migration_blocker, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
error_free(tpm_emu->migration_blocker);
|
||||
tpm_emu->migration_blocker = NULL;
|
||||
if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
|
||||
error_setg(&tpm_emu->migration_blocker,
|
||||
"Migration disabled: TPM emulator does not support "
|
||||
"migration");
|
||||
migrate_add_blocker(tpm_emu->migration_blocker, &err);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
error_free(tpm_emu->migration_blocker);
|
||||
tpm_emu->migration_blocker = NULL;
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -570,6 +601,267 @@ static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
/*
|
||||
* Transfer a TPM state blob from the TPM into a provided buffer.
|
||||
*
|
||||
* @tpm_emu: TPMEmulator
|
||||
* @type: the type of blob to transfer
|
||||
* @tsb: the TPMSizeBuffer to fill with the blob
|
||||
* @flags: the flags to return to the caller
|
||||
*/
|
||||
static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu,
|
||||
uint8_t type,
|
||||
TPMSizedBuffer *tsb,
|
||||
uint32_t *flags)
|
||||
{
|
||||
ptm_getstate pgs;
|
||||
ptm_res res;
|
||||
ssize_t n;
|
||||
uint32_t totlength, length;
|
||||
|
||||
tpm_sized_buffer_reset(tsb);
|
||||
|
||||
pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED);
|
||||
pgs.u.req.type = cpu_to_be32(type);
|
||||
pgs.u.req.offset = 0;
|
||||
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB,
|
||||
&pgs, sizeof(pgs.u.req),
|
||||
offsetof(ptm_getstate, u.resp.data)) < 0) {
|
||||
error_report("tpm-emulator: could not get state blob type %d : %s",
|
||||
type, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = be32_to_cpu(pgs.u.resp.tpm_result);
|
||||
if (res != 0 && (res & 0x800) == 0) {
|
||||
error_report("tpm-emulator: Getting the stateblob (type %d) failed "
|
||||
"with a TPM error 0x%x", type, res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
totlength = be32_to_cpu(pgs.u.resp.totlength);
|
||||
length = be32_to_cpu(pgs.u.resp.length);
|
||||
if (totlength != length) {
|
||||
error_report("tpm-emulator: Expecting to read %u bytes "
|
||||
"but would get %u", totlength, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*flags = be32_to_cpu(pgs.u.resp.state_flags);
|
||||
|
||||
if (totlength > 0) {
|
||||
tsb->buffer = g_try_malloc(totlength);
|
||||
if (!tsb->buffer) {
|
||||
error_report("tpm-emulator: Out of memory allocating %u bytes",
|
||||
totlength);
|
||||
return -1;
|
||||
}
|
||||
|
||||
n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength);
|
||||
if (n != totlength) {
|
||||
error_report("tpm-emulator: Could not read stateblob (type %d); "
|
||||
"expected %u bytes, got %zd",
|
||||
type, totlength, n);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
tsb->size = totlength;
|
||||
|
||||
trace_tpm_emulator_get_state_blob(type, tsb->size, *flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
|
||||
{
|
||||
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
|
||||
|
||||
if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
|
||||
&state_blobs->permanent,
|
||||
&state_blobs->permanent_flags) < 0 ||
|
||||
tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
|
||||
&state_blobs->volatil,
|
||||
&state_blobs->volatil_flags) < 0 ||
|
||||
tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
|
||||
&state_blobs->savestate,
|
||||
&state_blobs->savestate_flags) < 0) {
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_exit:
|
||||
tpm_sized_buffer_reset(&state_blobs->volatil);
|
||||
tpm_sized_buffer_reset(&state_blobs->permanent);
|
||||
tpm_sized_buffer_reset(&state_blobs->savestate);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transfer a TPM state blob to the TPM emulator.
|
||||
*
|
||||
* @tpm_emu: TPMEmulator
|
||||
* @type: the type of TPM state blob to transfer
|
||||
* @tsb: TPMSizedBuffer containing the TPM state blob
|
||||
* @flags: Flags describing the (encryption) state of the TPM state blob
|
||||
*/
|
||||
static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
|
||||
uint32_t type,
|
||||
TPMSizedBuffer *tsb,
|
||||
uint32_t flags)
|
||||
{
|
||||
ssize_t n;
|
||||
ptm_setstate pss;
|
||||
ptm_res tpm_result;
|
||||
|
||||
if (tsb->size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pss = (ptm_setstate) {
|
||||
.u.req.state_flags = cpu_to_be32(flags),
|
||||
.u.req.type = cpu_to_be32(type),
|
||||
.u.req.length = cpu_to_be32(tsb->size),
|
||||
};
|
||||
|
||||
/* write the header only */
|
||||
if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
|
||||
offsetof(ptm_setstate, u.req.data), 0) < 0) {
|
||||
error_report("tpm-emulator: could not set state blob type %d : %s",
|
||||
type, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* now the body */
|
||||
n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
|
||||
if (n != tsb->size) {
|
||||
error_report("tpm-emulator: Writing the stateblob (type %d) "
|
||||
"failed; could not write %u bytes, but only %zd",
|
||||
type, tsb->size, n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* now get the result */
|
||||
n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
|
||||
(uint8_t *)&pss, sizeof(pss.u.resp));
|
||||
if (n != sizeof(pss.u.resp)) {
|
||||
error_report("tpm-emulator: Reading response from writing stateblob "
|
||||
"(type %d) failed; expected %zu bytes, got %zd", type,
|
||||
sizeof(pss.u.resp), n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
|
||||
if (tpm_result != 0) {
|
||||
error_report("tpm-emulator: Setting the stateblob (type %d) failed "
|
||||
"with a TPM error 0x%x", type, tpm_result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
trace_tpm_emulator_set_state_blob(type, tsb->size, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set all the TPM state blobs.
|
||||
*
|
||||
* Returns a negative errno code in case of error.
|
||||
*/
|
||||
static int tpm_emulator_set_state_blobs(TPMBackend *tb)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
|
||||
|
||||
trace_tpm_emulator_set_state_blobs();
|
||||
|
||||
if (tpm_emulator_stop_tpm(tb) < 0) {
|
||||
trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
|
||||
&state_blobs->permanent,
|
||||
state_blobs->permanent_flags) < 0 ||
|
||||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
|
||||
&state_blobs->volatil,
|
||||
state_blobs->volatil_flags) < 0 ||
|
||||
tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
|
||||
&state_blobs->savestate,
|
||||
state_blobs->savestate_flags) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
trace_tpm_emulator_set_state_blobs_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_emulator_pre_save(void *opaque)
|
||||
{
|
||||
TPMBackend *tb = opaque;
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
|
||||
|
||||
trace_tpm_emulator_pre_save();
|
||||
|
||||
tpm_backend_finish_sync(tb);
|
||||
|
||||
/* get the state blobs from the TPM */
|
||||
return tpm_emulator_get_state_blobs(tpm_emu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the TPM state blobs into the TPM.
|
||||
*
|
||||
* Returns negative errno codes in case of error.
|
||||
*/
|
||||
static int tpm_emulator_post_load(void *opaque, int version_id)
|
||||
{
|
||||
TPMBackend *tb = opaque;
|
||||
int ret;
|
||||
|
||||
ret = tpm_emulator_set_state_blobs(tb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_tpm_emulator = {
|
||||
.name = "tpm-emulator",
|
||||
.version_id = 0,
|
||||
.pre_save = tpm_emulator_pre_save,
|
||||
.post_load = tpm_emulator_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
|
||||
VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer,
|
||||
TPMEmulator, 0, 0,
|
||||
state_blobs.permanent.size),
|
||||
|
||||
VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator),
|
||||
VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator),
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer,
|
||||
TPMEmulator, 0, 0,
|
||||
state_blobs.volatil.size),
|
||||
|
||||
VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator),
|
||||
VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator),
|
||||
VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer,
|
||||
TPMEmulator, 0, 0,
|
||||
state_blobs.savestate.size),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void tpm_emulator_inst_init(Object *obj)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
|
||||
@ -579,6 +871,8 @@ static void tpm_emulator_inst_init(Object *obj)
|
||||
tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
|
||||
tpm_emu->cur_locty_number = ~0;
|
||||
qemu_mutex_init(&tpm_emu->mutex);
|
||||
|
||||
vmstate_register(NULL, -1, &vmstate_tpm_emulator, obj);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -600,6 +894,7 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
|
||||
static void tpm_emulator_inst_finalize(Object *obj)
|
||||
{
|
||||
TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
|
||||
TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
|
||||
|
||||
tpm_emulator_shutdown(tpm_emu);
|
||||
|
||||
@ -614,7 +909,13 @@ static void tpm_emulator_inst_finalize(Object *obj)
|
||||
error_free(tpm_emu->migration_blocker);
|
||||
}
|
||||
|
||||
tpm_sized_buffer_reset(&state_blobs->volatil);
|
||||
tpm_sized_buffer_reset(&state_blobs->permanent);
|
||||
tpm_sized_buffer_reset(&state_blobs->savestate);
|
||||
|
||||
qemu_mutex_destroy(&tpm_emu->mutex);
|
||||
|
||||
vmstate_unregister(NULL, &vmstate_tpm_emulator, obj);
|
||||
}
|
||||
|
||||
static void tpm_emulator_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -894,9 +894,57 @@ static void tpm_tis_reset(DeviceState *dev)
|
||||
tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size);
|
||||
}
|
||||
|
||||
/* persistent state handling */
|
||||
|
||||
static int tpm_tis_pre_save(void *opaque)
|
||||
{
|
||||
TPMState *s = opaque;
|
||||
uint8_t locty = s->active_locty;
|
||||
|
||||
trace_tpm_tis_pre_save(locty, s->rw_offset);
|
||||
|
||||
if (DEBUG_TIS) {
|
||||
tpm_tis_dump_state(opaque, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Synchronize with backend completion.
|
||||
*/
|
||||
tpm_backend_finish_sync(s->be_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_locty = {
|
||||
.name = "tpm-tis/locty",
|
||||
.version_id = 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,
|
||||
.name = "tpm-tis",
|
||||
.version_id = 0,
|
||||
.pre_save = tpm_tis_pre_save,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BUFFER(buffer, TPMState),
|
||||
VMSTATE_UINT16(rw_offset, TPMState),
|
||||
VMSTATE_UINT8(active_locty, TPMState),
|
||||
VMSTATE_UINT8(aborting_locty, TPMState),
|
||||
VMSTATE_UINT8(next_locty, TPMState),
|
||||
|
||||
VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0,
|
||||
vmstate_locty, TPMLocality),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property tpm_tis_properties[] = {
|
||||
|
@ -20,13 +20,19 @@ tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
|
||||
tpm_emulator_handle_request(void) "processing TPM command"
|
||||
tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
|
||||
tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
|
||||
tpm_emulator_startup_tpm(void) "startup"
|
||||
tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu"
|
||||
tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
|
||||
tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD"
|
||||
tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2"
|
||||
tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2"
|
||||
tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified"
|
||||
tpm_emulator_handle_device_opts_startup_error(void) "Startup error"
|
||||
tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x"
|
||||
tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x"
|
||||
tpm_emulator_set_state_blobs(void) "setting state blobs"
|
||||
tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s"
|
||||
tpm_emulator_set_state_blobs_done(void) "Done setting state blobs"
|
||||
tpm_emulator_pre_save(void) ""
|
||||
tpm_emulator_inst_init(void) ""
|
||||
|
||||
# hw/tpm/tpm_tis.c
|
||||
@ -44,3 +50,4 @@ tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seiz
|
||||
tpm_tis_mmio_write_init_abort(void) "Initiating abort"
|
||||
tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ"
|
||||
tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)"
|
||||
tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u"
|
||||
|
@ -297,6 +297,7 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX
|
||||
ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),)
|
||||
check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
|
||||
endif
|
||||
check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
|
||||
@ -721,6 +722,8 @@ tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
|
||||
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
|
||||
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
||||
tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
|
||||
tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \
|
||||
tests/tpm-util.o $(test-io-obj-y)
|
||||
tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y)
|
||||
tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y)
|
||||
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
|
||||
|
247
tests/tpm-crb-swtpm-test.c
Normal file
247
tests/tpm-crb-swtpm-test.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* QTest testcase for TPM CRB talking to external swtpm and swtpm migration
|
||||
*
|
||||
* Copyright (c) 2018 IBM Corporation
|
||||
* with parts borrowed from migration-test.c that is:
|
||||
* Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "libqtest.h"
|
||||
#include "tpm-util.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
||||
typedef struct TestState {
|
||||
char *src_tpm_path;
|
||||
char *dst_tpm_path;
|
||||
char *uri;
|
||||
} TestState;
|
||||
|
||||
bool got_stop;
|
||||
|
||||
static void migrate(QTestState *who, const char *uri)
|
||||
{
|
||||
QDict *rsp;
|
||||
gchar *cmd;
|
||||
|
||||
cmd = g_strdup_printf("{ 'execute': 'migrate',"
|
||||
"'arguments': { 'uri': '%s' } }",
|
||||
uri);
|
||||
rsp = qtest_qmp(who, cmd);
|
||||
g_free(cmd);
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
qobject_unref(rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Events can get in the way of responses we are actually waiting for.
|
||||
*/
|
||||
static QDict *wait_command(QTestState *who, const char *command)
|
||||
{
|
||||
const char *event_string;
|
||||
QDict *response;
|
||||
|
||||
response = qtest_qmp(who, command);
|
||||
|
||||
while (qdict_haskey(response, "event")) {
|
||||
/* OK, it was an event */
|
||||
event_string = qdict_get_str(response, "event");
|
||||
if (!strcmp(event_string, "STOP")) {
|
||||
got_stop = true;
|
||||
}
|
||||
qobject_unref(response);
|
||||
response = qtest_qmp_receive(who);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
static void wait_for_migration_complete(QTestState *who)
|
||||
{
|
||||
while (true) {
|
||||
QDict *rsp, *rsp_return;
|
||||
bool completed;
|
||||
const char *status;
|
||||
|
||||
rsp = wait_command(who, "{ 'execute': 'query-migrate' }");
|
||||
rsp_return = qdict_get_qdict(rsp, "return");
|
||||
status = qdict_get_str(rsp_return, "status");
|
||||
completed = strcmp(status, "completed") == 0;
|
||||
g_assert_cmpstr(status, !=, "failed");
|
||||
qobject_unref(rsp);
|
||||
if (completed) {
|
||||
return;
|
||||
}
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
static void migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu,
|
||||
SocketAddress *src_tpm_addr,
|
||||
SocketAddress *dst_tpm_addr,
|
||||
const char *miguri)
|
||||
{
|
||||
char *src_qemu_args, *dst_qemu_args;
|
||||
|
||||
src_qemu_args = g_strdup_printf(
|
||||
"-chardev socket,id=chr,path=%s "
|
||||
"-tpmdev emulator,id=dev,chardev=chr "
|
||||
"-device tpm-crb,tpmdev=dev ",
|
||||
src_tpm_addr->u.q_unix.path);
|
||||
|
||||
*src_qemu = qtest_init(src_qemu_args);
|
||||
|
||||
dst_qemu_args = g_strdup_printf(
|
||||
"-chardev socket,id=chr,path=%s "
|
||||
"-tpmdev emulator,id=dev,chardev=chr "
|
||||
"-device tpm-crb,tpmdev=dev "
|
||||
"-incoming %s",
|
||||
dst_tpm_addr->u.q_unix.path,
|
||||
miguri);
|
||||
|
||||
*dst_qemu = qtest_init(dst_qemu_args);
|
||||
|
||||
free(src_qemu_args);
|
||||
free(dst_qemu_args);
|
||||
}
|
||||
|
||||
static void tpm_crb_swtpm_test(const void *data)
|
||||
{
|
||||
char *args = NULL;
|
||||
QTestState *s;
|
||||
SocketAddress *addr = NULL;
|
||||
gboolean succ;
|
||||
GPid swtpm_pid;
|
||||
GError *error = NULL;
|
||||
const TestState *ts = data;
|
||||
|
||||
succ = tpm_util_swtpm_start(ts->src_tpm_path, &swtpm_pid, &addr, &error);
|
||||
/* succ may be false if swtpm is not available */
|
||||
if (!succ) {
|
||||
return;
|
||||
}
|
||||
|
||||
args = g_strdup_printf(
|
||||
"-chardev socket,id=chr,path=%s "
|
||||
"-tpmdev emulator,id=dev,chardev=chr "
|
||||
"-device tpm-crb,tpmdev=dev",
|
||||
addr->u.q_unix.path);
|
||||
|
||||
s = qtest_start(args);
|
||||
g_free(args);
|
||||
|
||||
tpm_util_startup(s, tpm_util_crb_transfer);
|
||||
tpm_util_pcrextend(s, tpm_util_crb_transfer);
|
||||
|
||||
unsigned char tpm_pcrread_resp[] =
|
||||
"\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00"
|
||||
"\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85"
|
||||
"\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89"
|
||||
"\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde";
|
||||
tpm_util_pcrread(s, tpm_util_crb_transfer, tpm_pcrread_resp,
|
||||
sizeof(tpm_pcrread_resp));
|
||||
|
||||
qtest_end();
|
||||
tpm_util_swtpm_kill(swtpm_pid);
|
||||
|
||||
if (addr) {
|
||||
g_unlink(addr->u.q_unix.path);
|
||||
qapi_free_SocketAddress(addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void tpm_crb_swtpm_migration_test(const void *data)
|
||||
{
|
||||
const TestState *ts = data;
|
||||
gboolean succ;
|
||||
GPid src_tpm_pid, dst_tpm_pid;
|
||||
SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL;
|
||||
GError *error = NULL;
|
||||
QTestState *src_qemu, *dst_qemu;
|
||||
|
||||
succ = tpm_util_swtpm_start(ts->src_tpm_path, &src_tpm_pid,
|
||||
&src_tpm_addr, &error);
|
||||
/* succ may be false if swtpm is not available */
|
||||
if (!succ) {
|
||||
return;
|
||||
}
|
||||
|
||||
succ = tpm_util_swtpm_start(ts->dst_tpm_path, &dst_tpm_pid,
|
||||
&dst_tpm_addr, &error);
|
||||
/* succ may be false if swtpm is not available */
|
||||
if (!succ) {
|
||||
goto err_src_tpm_kill;
|
||||
}
|
||||
|
||||
migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr,
|
||||
ts->uri);
|
||||
|
||||
tpm_util_startup(src_qemu, tpm_util_crb_transfer);
|
||||
tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer);
|
||||
|
||||
unsigned char tpm_pcrread_resp[] =
|
||||
"\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00"
|
||||
"\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85"
|
||||
"\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89"
|
||||
"\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde";
|
||||
tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp,
|
||||
sizeof(tpm_pcrread_resp));
|
||||
|
||||
migrate(src_qemu, ts->uri);
|
||||
wait_for_migration_complete(src_qemu);
|
||||
|
||||
tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp,
|
||||
sizeof(tpm_pcrread_resp));
|
||||
|
||||
qtest_quit(dst_qemu);
|
||||
qtest_quit(src_qemu);
|
||||
|
||||
tpm_util_swtpm_kill(dst_tpm_pid);
|
||||
if (dst_tpm_addr) {
|
||||
g_unlink(dst_tpm_addr->u.q_unix.path);
|
||||
qapi_free_SocketAddress(dst_tpm_addr);
|
||||
}
|
||||
|
||||
err_src_tpm_kill:
|
||||
tpm_util_swtpm_kill(src_tpm_pid);
|
||||
if (src_tpm_addr) {
|
||||
g_unlink(src_tpm_addr->u.q_unix.path);
|
||||
qapi_free_SocketAddress(src_tpm_addr);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
TestState ts = { 0 };
|
||||
|
||||
ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL);
|
||||
ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL);
|
||||
ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path);
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test);
|
||||
qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts,
|
||||
tpm_crb_swtpm_migration_test);
|
||||
ret = g_test_run();
|
||||
|
||||
g_rmdir(ts.dst_tpm_path);
|
||||
g_free(ts.dst_tpm_path);
|
||||
g_rmdir(ts.src_tpm_path);
|
||||
g_free(ts.src_tpm_path);
|
||||
g_free(ts.uri);
|
||||
|
||||
return ret;
|
||||
}
|
186
tests/tpm-util.c
Normal file
186
tests/tpm-util.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* QTest TPM utilities
|
||||
*
|
||||
* Copyright (c) 2018 IBM Corporation
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
* Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "libqtest.h"
|
||||
#include "tpm-util.h"
|
||||
|
||||
void tpm_util_crb_transfer(QTestState *s,
|
||||
const unsigned char *req, size_t req_size,
|
||||
unsigned char *rsp, size_t rsp_size)
|
||||
{
|
||||
uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
|
||||
uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
|
||||
|
||||
qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
|
||||
|
||||
qtest_memwrite(s, caddr, req, req_size);
|
||||
|
||||
uint32_t sts, start = 1;
|
||||
uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
|
||||
qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
|
||||
while (true) {
|
||||
start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
|
||||
if ((start & 1) == 0) {
|
||||
break;
|
||||
}
|
||||
if (g_get_monotonic_time() >= end_time) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
|
||||
g_assert_cmpint(start & 1, ==, 0);
|
||||
sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
|
||||
g_assert_cmpint(sts & 1, ==, 0);
|
||||
|
||||
qtest_memread(s, raddr, rsp, rsp_size);
|
||||
}
|
||||
|
||||
void tpm_util_startup(QTestState *s, tx_func *tx)
|
||||
{
|
||||
unsigned char buffer[1024];
|
||||
unsigned char tpm_startup[] =
|
||||
"\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
|
||||
unsigned char tpm_startup_resp[] =
|
||||
"\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00";
|
||||
|
||||
tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer));
|
||||
|
||||
g_assert_cmpmem(buffer, sizeof(tpm_startup_resp),
|
||||
tpm_startup_resp, sizeof(tpm_startup_resp));
|
||||
}
|
||||
|
||||
void tpm_util_pcrextend(QTestState *s, tx_func *tx)
|
||||
{
|
||||
unsigned char buffer[1024];
|
||||
unsigned char tpm_pcrextend[] =
|
||||
"\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00"
|
||||
"\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"
|
||||
"\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00";
|
||||
|
||||
unsigned char tpm_pcrextend_resp[] =
|
||||
"\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x01\x00\x00";
|
||||
|
||||
tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer));
|
||||
|
||||
g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp),
|
||||
tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp));
|
||||
}
|
||||
|
||||
void tpm_util_pcrread(QTestState *s, tx_func *tx,
|
||||
const unsigned char *exp_resp, size_t exp_resp_size)
|
||||
{
|
||||
unsigned char buffer[1024];
|
||||
unsigned char tpm_pcrread[] =
|
||||
"\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b"
|
||||
"\x03\x00\x04\x00";
|
||||
|
||||
tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
|
||||
|
||||
g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size);
|
||||
}
|
||||
|
||||
static gboolean tpm_util_swtpm_has_tpm2(void)
|
||||
{
|
||||
gint mystdout;
|
||||
gboolean succ;
|
||||
unsigned i;
|
||||
char buffer[10240];
|
||||
ssize_t n;
|
||||
gchar *swtpm_argv[] = {
|
||||
g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL
|
||||
};
|
||||
|
||||
succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL,
|
||||
G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
|
||||
NULL, &mystdout, NULL, NULL);
|
||||
if (!succ) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
n = read(mystdout, buffer, sizeof(buffer) - 1);
|
||||
if (n < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
buffer[n] = 0;
|
||||
if (!strstr(buffer, "--tpm2")) {
|
||||
succ = false;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
for (i = 0; swtpm_argv[i]; i++) {
|
||||
g_free(swtpm_argv[i]);
|
||||
}
|
||||
|
||||
return succ;
|
||||
}
|
||||
|
||||
gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
|
||||
SocketAddress **addr, GError **error)
|
||||
{
|
||||
char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path);
|
||||
char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock",
|
||||
path);
|
||||
gchar *swtpm_argv[] = {
|
||||
g_strdup("swtpm"), g_strdup("socket"),
|
||||
g_strdup("--tpmstate"), swtpm_argv_tpmstate,
|
||||
g_strdup("--ctrl"), swtpm_argv_ctrl,
|
||||
g_strdup("--tpm2"),
|
||||
NULL
|
||||
};
|
||||
gboolean succ;
|
||||
unsigned i;
|
||||
|
||||
succ = tpm_util_swtpm_has_tpm2();
|
||||
if (!succ) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*addr = g_new0(SocketAddress, 1);
|
||||
(*addr)->type = SOCKET_ADDRESS_TYPE_UNIX;
|
||||
(*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL);
|
||||
|
||||
succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, pid, error);
|
||||
|
||||
cleanup:
|
||||
for (i = 0; swtpm_argv[i]; i++) {
|
||||
g_free(swtpm_argv[i]);
|
||||
}
|
||||
|
||||
return succ;
|
||||
}
|
||||
|
||||
void tpm_util_swtpm_kill(GPid pid)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!pid) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_spawn_close_pid(pid);
|
||||
|
||||
n = kill(pid, 0);
|
||||
if (n < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
kill(pid, SIGKILL);
|
||||
}
|
36
tests/tpm-util.h
Normal file
36
tests/tpm-util.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* QTest TPM utilities
|
||||
*
|
||||
* Copyright (c) 2018 IBM Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Stefan Berger <stefanb@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef TESTS_TPM_UTIL_H
|
||||
#define TESTS_TPM_UTIL_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "io/channel-socket.h"
|
||||
|
||||
typedef void (tx_func)(QTestState *s,
|
||||
const unsigned char *req, size_t req_size,
|
||||
unsigned char *rsp, size_t rsp_size);
|
||||
|
||||
void tpm_util_crb_transfer(QTestState *s,
|
||||
const unsigned char *req, size_t req_size,
|
||||
unsigned char *rsp, size_t rsp_size);
|
||||
|
||||
void tpm_util_startup(QTestState *s, tx_func *tx);
|
||||
void tpm_util_pcrextend(QTestState *s, tx_func *tx);
|
||||
void tpm_util_pcrread(QTestState *s, tx_func *tx,
|
||||
const unsigned char *exp_resp, size_t exp_resp_size);
|
||||
|
||||
gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
|
||||
SocketAddress **addr, GError **error);
|
||||
void tpm_util_swtpm_kill(GPid pid);
|
||||
|
||||
#endif /* TESTS_TPM_UTIL_H */
|
Loading…
Reference in New Issue
Block a user