qemu/tests/tpm-util.c
<<
>>
Prefs
   1/*
   2 * QTest TPM utilities
   3 *
   4 * Copyright (c) 2018 IBM Corporation
   5 * Copyright (c) 2018 Red Hat, Inc.
   6 *
   7 * Authors:
   8 *   Stefan Berger <stefanb@linux.vnet.ibm.com>
   9 *   Marc-André Lureau <marcandre.lureau@redhat.com>
  10 *
  11 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  12 * See the COPYING file in the top-level directory.
  13 */
  14
  15#include "qemu/osdep.h"
  16
  17#include "hw/acpi/tpm.h"
  18#include "libqtest.h"
  19#include "tpm-util.h"
  20#include "qapi/qmp/qdict.h"
  21
  22#define TIS_REG(LOCTY, REG) \
  23    (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG)
  24
  25static bool got_stop;
  26
  27void tpm_util_crb_transfer(QTestState *s,
  28                           const unsigned char *req, size_t req_size,
  29                           unsigned char *rsp, size_t rsp_size)
  30{
  31    uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
  32    uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
  33
  34    qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
  35
  36    qtest_memwrite(s, caddr, req, req_size);
  37
  38    uint32_t sts, start = 1;
  39    uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
  40    qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
  41    while (true) {
  42        start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
  43        if ((start & 1) == 0) {
  44            break;
  45        }
  46        if (g_get_monotonic_time() >= end_time) {
  47            break;
  48        }
  49    };
  50    start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
  51    g_assert_cmpint(start & 1, ==, 0);
  52    sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
  53    g_assert_cmpint(sts & 1, ==, 0);
  54
  55    qtest_memread(s, raddr, rsp, rsp_size);
  56}
  57
  58void tpm_util_tis_transfer(QTestState *s,
  59                           const unsigned char *req, size_t req_size,
  60                           unsigned char *rsp, size_t rsp_size)
  61{
  62    uint32_t sts;
  63    uint16_t bcount;
  64    size_t i;
  65
  66    /* request use of locality 0 */
  67    qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE);
  68    qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY);
  69
  70    sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
  71    bcount = (sts >> 8) & 0xffff;
  72    g_assert_cmpint(bcount, >=, req_size);
  73
  74    /* transmit command */
  75    for (i = 0; i < req_size; i++) {
  76        qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]);
  77    }
  78
  79    /* start processing */
  80    qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO);
  81
  82    uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
  83    do {
  84        sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
  85        if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
  86            break;
  87        }
  88    } while (g_get_monotonic_time() < end_time);
  89
  90    sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
  91    bcount = (sts >> 8) & 0xffff;
  92
  93    memset(rsp, 0, rsp_size);
  94    for (i = 0; i < bcount; i++) {
  95        rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO));
  96    }
  97
  98    /* relinquish use of locality 0 */
  99    qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS),
 100                 TPM_TIS_ACCESS_ACTIVE_LOCALITY);
 101}
 102
 103void tpm_util_startup(QTestState *s, tx_func *tx)
 104{
 105    unsigned char buffer[1024];
 106    unsigned char tpm_startup[] =
 107        "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
 108    unsigned char tpm_startup_resp[] =
 109        "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00";
 110
 111    tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer));
 112
 113    g_assert_cmpmem(buffer, sizeof(tpm_startup_resp),
 114                    tpm_startup_resp, sizeof(tpm_startup_resp));
 115}
 116
 117void tpm_util_pcrextend(QTestState *s, tx_func *tx)
 118{
 119    unsigned char buffer[1024];
 120    unsigned char tpm_pcrextend[] =
 121        "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00"
 122        "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"
 123        "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 124        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 125        "\x00";
 126
 127    unsigned char tpm_pcrextend_resp[] =
 128        "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 129        "\x01\x00\x00";
 130
 131    tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer));
 132
 133    g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp),
 134                    tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp));
 135}
 136
 137void tpm_util_pcrread(QTestState *s, tx_func *tx,
 138                      const unsigned char *exp_resp, size_t exp_resp_size)
 139{
 140    unsigned char buffer[1024];
 141    unsigned char tpm_pcrread[] =
 142        "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b"
 143        "\x03\x00\x04\x00";
 144
 145    tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
 146
 147    g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size);
 148}
 149
 150static gboolean tpm_util_swtpm_has_tpm2(void)
 151{
 152    gint mystdout;
 153    gboolean succ;
 154    unsigned i;
 155    char buffer[10240];
 156    ssize_t n;
 157    gchar *swtpm_argv[] = {
 158        g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL
 159    };
 160
 161    succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL,
 162                                    G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
 163                                    NULL, &mystdout, NULL, NULL);
 164    if (!succ) {
 165        goto cleanup;
 166    }
 167
 168    n = read(mystdout, buffer, sizeof(buffer) - 1);
 169    if (n < 0) {
 170        goto cleanup;
 171    }
 172    buffer[n] = 0;
 173    if (!strstr(buffer, "--tpm2")) {
 174        succ = false;
 175    }
 176
 177 cleanup:
 178    for (i = 0; swtpm_argv[i]; i++) {
 179        g_free(swtpm_argv[i]);
 180    }
 181
 182    return succ;
 183}
 184
 185gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
 186                              SocketAddress **addr, GError **error)
 187{
 188    char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path);
 189    char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock",
 190                                            path);
 191    gchar *swtpm_argv[] = {
 192        g_strdup("swtpm"), g_strdup("socket"),
 193        g_strdup("--tpmstate"), swtpm_argv_tpmstate,
 194        g_strdup("--ctrl"), swtpm_argv_ctrl,
 195        g_strdup("--tpm2"),
 196        NULL
 197    };
 198    gboolean succ;
 199    unsigned i;
 200
 201    succ = tpm_util_swtpm_has_tpm2();
 202    if (!succ) {
 203        goto cleanup;
 204    }
 205
 206    *addr = g_new0(SocketAddress, 1);
 207    (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX;
 208    (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL);
 209
 210    succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH,
 211                         NULL, NULL, pid, error);
 212
 213cleanup:
 214    for (i = 0; swtpm_argv[i]; i++) {
 215        g_free(swtpm_argv[i]);
 216    }
 217
 218    return succ;
 219}
 220
 221void tpm_util_swtpm_kill(GPid pid)
 222{
 223    int n;
 224
 225    if (!pid) {
 226        return;
 227    }
 228
 229    g_spawn_close_pid(pid);
 230
 231    n = kill(pid, 0);
 232    if (n < 0) {
 233        return;
 234    }
 235
 236    kill(pid, SIGKILL);
 237}
 238
 239void tpm_util_migrate(QTestState *who, const char *uri)
 240{
 241    QDict *rsp;
 242    gchar *cmd;
 243
 244    cmd = g_strdup_printf("{ 'execute': 'migrate',"
 245                          "'arguments': { 'uri': '%s' } }",
 246                          uri);
 247    rsp = qtest_qmp(who, cmd);
 248    g_free(cmd);
 249    g_assert(qdict_haskey(rsp, "return"));
 250    qobject_unref(rsp);
 251}
 252
 253/*
 254 * Events can get in the way of responses we are actually waiting for.
 255 */
 256static QDict *tpm_util_wait_command(QTestState *who, const char *command)
 257{
 258    const char *event_string;
 259    QDict *response;
 260
 261    response = qtest_qmp(who, command);
 262
 263    while (qdict_haskey(response, "event")) {
 264        /* OK, it was an event */
 265        event_string = qdict_get_str(response, "event");
 266        if (!strcmp(event_string, "STOP")) {
 267            got_stop = true;
 268        }
 269        qobject_unref(response);
 270        response = qtest_qmp_receive(who);
 271    }
 272    return response;
 273}
 274
 275void tpm_util_wait_for_migration_complete(QTestState *who)
 276{
 277    while (true) {
 278        QDict *rsp, *rsp_return;
 279        bool completed;
 280        const char *status;
 281
 282        rsp = tpm_util_wait_command(who, "{ 'execute': 'query-migrate' }");
 283        rsp_return = qdict_get_qdict(rsp, "return");
 284        status = qdict_get_str(rsp_return, "status");
 285        completed = strcmp(status, "completed") == 0;
 286        g_assert_cmpstr(status, !=,  "failed");
 287        qobject_unref(rsp);
 288        if (completed) {
 289            return;
 290        }
 291        usleep(1000);
 292    }
 293}
 294
 295void tpm_util_migration_start_qemu(QTestState **src_qemu,
 296                                   QTestState **dst_qemu,
 297                                   SocketAddress *src_tpm_addr,
 298                                   SocketAddress *dst_tpm_addr,
 299                                   const char *miguri,
 300                                   const char *ifmodel)
 301{
 302    char *src_qemu_args, *dst_qemu_args;
 303
 304    src_qemu_args = g_strdup_printf(
 305        "-chardev socket,id=chr,path=%s "
 306        "-tpmdev emulator,id=dev,chardev=chr "
 307        "-device %s,tpmdev=dev ",
 308        src_tpm_addr->u.q_unix.path, ifmodel);
 309
 310    *src_qemu = qtest_init(src_qemu_args);
 311
 312    dst_qemu_args = g_strdup_printf(
 313        "-chardev socket,id=chr,path=%s "
 314        "-tpmdev emulator,id=dev,chardev=chr "
 315        "-device %s,tpmdev=dev "
 316        "-incoming %s",
 317        dst_tpm_addr->u.q_unix.path,
 318        ifmodel, miguri);
 319
 320    *dst_qemu = qtest_init(dst_qemu_args);
 321
 322    free(src_qemu_args);
 323    free(dst_qemu_args);
 324}
 325