248 lines
7.0 KiB
C
248 lines
7.0 KiB
C
|
/*
|
||
|
* 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;
|
||
|
}
|