bios-tables-test.c 26.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Boot order test cases.
 *
 * Copyright (c) 2013 Red Hat Inc.
 *
 * Authors:
 *  Michael S. Tsirkin <mst@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.
 */

P
Peter Maydell 已提交
13
#include "qemu/osdep.h"
14
#include <glib.h>
15
#include <glib/gstdio.h>
16
#include "qemu-common.h"
17
#include "libqtest.h"
18
#include "hw/acpi/acpi-defs.h"
19
#include "hw/smbios/smbios.h"
G
Gabriel L. Somlo 已提交
20
#include "qemu/bitmap.h"
21

22 23 24
#define MACHINE_PC "pc"
#define MACHINE_Q35 "q35"

25 26
#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML"

27
/* DSDT and SSDTs format */
28
typedef struct {
29
    AcpiTableHeader header;
30 31 32 33 34 35
    gchar *aml;            /* aml bytecode from guest */
    gsize aml_len;
    gchar *aml_file;
    gchar *asl;            /* asl code generated from aml */
    gsize asl_len;
    gchar *asl_file;
36
    bool tmp_files_retain;   /* do not delete the temp asl/aml */
37
} QEMU_PACKED AcpiSdtTable;
38 39

typedef struct {
40
    const char *machine;
41
    const char *variant;
42 43 44 45
    uint32_t rsdp_addr;
    AcpiRsdpDescriptor rsdp_table;
    AcpiRsdtDescriptorRev1 rsdt_table;
    AcpiFadtDescriptorRev1 fadt_table;
46
    AcpiFacsDescriptorRev1 facs_table;
47 48
    uint32_t *rsdt_tables_addr;
    int rsdt_tables_nr;
49
    GArray *tables;
G
Gabriel L. Somlo 已提交
50
    uint32_t smbios_ep_addr;
W
Wei Huang 已提交
51
    struct smbios_21_entry_point smbios_ep_table;
52
} test_data;
53 54 55 56 57 58 59 60

#define LOW(x) ((x) & 0xff)
#define HIGH(x) ((x) >> 8)

#define SIGNATURE 0xdead
#define SIGNATURE_OFFSET 0x10
#define BOOT_SECTOR_ADDRESS 0x7c00

61 62 63 64 65 66 67
#define ACPI_READ_FIELD(field, addr)           \
    do {                                       \
        switch (sizeof(field)) {               \
        case 1:                                \
            field = readb(addr);               \
            break;                             \
        case 2:                                \
68
            field = readw(addr);               \
69 70
            break;                             \
        case 4:                                \
71
            field = readl(addr);               \
72 73
            break;                             \
        case 8:                                \
74
            field = readq(addr);               \
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
            break;                             \
        default:                               \
            g_assert(false);                   \
        }                                      \
        addr += sizeof(field);                  \
    } while (0);

#define ACPI_READ_ARRAY_PTR(arr, length, addr)  \
    do {                                        \
        int idx;                                \
        for (idx = 0; idx < length; ++idx) {    \
            ACPI_READ_FIELD(arr[idx], addr);    \
        }                                       \
    } while (0);

#define ACPI_READ_ARRAY(arr, addr)                               \
    ACPI_READ_ARRAY_PTR(arr, sizeof(arr)/sizeof(arr[0]), addr)

#define ACPI_READ_TABLE_HEADER(table, addr)                      \
    do {                                                         \
        ACPI_READ_FIELD((table)->signature, addr);               \
        ACPI_READ_FIELD((table)->length, addr);                  \
        ACPI_READ_FIELD((table)->revision, addr);                \
        ACPI_READ_FIELD((table)->checksum, addr);                \
        ACPI_READ_ARRAY((table)->oem_id, addr);                  \
        ACPI_READ_ARRAY((table)->oem_table_id, addr);            \
        ACPI_READ_FIELD((table)->oem_revision, addr);            \
        ACPI_READ_ARRAY((table)->asl_compiler_id, addr);         \
        ACPI_READ_FIELD((table)->asl_compiler_revision, addr);   \
    } while (0);

106 107 108 109 110 111 112 113 114 115 116 117 118 119
#define ACPI_ASSERT_CMP(actual, expected) do { \
    uint32_t ACPI_ASSERT_CMP_le = cpu_to_le32(actual); \
    char ACPI_ASSERT_CMP_str[5] = {}; \
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 4); \
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
} while (0)

#define ACPI_ASSERT_CMP64(actual, expected) do { \
    uint64_t ACPI_ASSERT_CMP_le = cpu_to_le64(actual); \
    char ACPI_ASSERT_CMP_str[9] = {}; \
    memcpy(ACPI_ASSERT_CMP_str, &ACPI_ASSERT_CMP_le, 8); \
    g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \
} while (0)

120 121
/* Boot sector code: write SIGNATURE into memory,
 * then halt.
122 123
 * Q35 machine requires a minimum 0x7e000 bytes disk.
 * (bug or feature?)
124
 */
125
static uint8_t boot_sector[0x7e000] = {
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    /* 7c00: mov $0xdead,%ax */
    [0x00] = 0xb8,
    [0x01] = LOW(SIGNATURE),
    [0x02] = HIGH(SIGNATURE),
    /* 7c03:  mov %ax,0x7c10 */
    [0x03] = 0xa3,
    [0x04] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
    [0x05] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET),
    /* 7c06: cli */
    [0x06] = 0xfa,
    /* 7c07: hlt */
    [0x07] = 0xf4,
    /* 7c08: jmp 0x7c07=0x7c0a-3 */
    [0x08] = 0xeb,
    [0x09] = LOW(-3),
    /* We mov 0xdead here: set value to make debugging easier */
    [SIGNATURE_OFFSET] = LOW(0xface),
    [SIGNATURE_OFFSET + 1] = HIGH(0xface),
    /* End of boot sector marker */
    [0x1FE] = 0x55,
    [0x1FF] = 0xAA,
};

static const char *disk = "tests/acpi-test-disk.raw";
150
static const char *data_dir = "tests/acpi-test-data";
151 152 153 154 155
#ifdef CONFIG_IASL
static const char *iasl = stringify(CONFIG_IASL);
#else
static const char *iasl;
#endif
156

157 158
static void free_test_data(test_data *data)
{
159
    AcpiSdtTable *temp;
160 161
    int i;

162
    g_free(data->rsdt_tables_addr);
163

164 165
    for (i = 0; i < data->tables->len; ++i) {
        temp = &g_array_index(data->tables, AcpiSdtTable, i);
166 167 168 169 170
        g_free(temp->aml);
        if (temp->aml_file &&
            !temp->tmp_files_retain &&
            g_strstr_len(temp->aml_file, -1, "aml-")) {
            unlink(temp->aml_file);
171
        }
172 173 174 175 176
        g_free(temp->aml_file);
        g_free(temp->asl);
        if (temp->asl_file &&
            !temp->tmp_files_retain) {
            unlink(temp->asl_file);
177
        }
178
        g_free(temp->asl_file);
179
    }
180

181
    g_array_free(data->tables, false);
182 183
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
static uint8_t acpi_checksum(const uint8_t *data, int len)
{
    int i;
    uint8_t sum = 0;

    for (i = 0; i < len; i++) {
        sum += data[i];
    }

    return sum;
}

static void test_acpi_rsdp_address(test_data *data)
{
    uint32_t off;

    /* OK, now find RSDP */
    for (off = 0xf0000; off < 0x100000; off += 0x10) {
        uint8_t sig[] = "RSD PTR ";
        int i;

        for (i = 0; i < sizeof sig - 1; ++i) {
            sig[i] = readb(off + i);
        }

        if (!memcmp(sig, "RSD PTR ", sizeof sig)) {
            break;
        }
    }

    g_assert_cmphex(off, <, 0x100000);
    data->rsdp_addr = off;
}

static void test_acpi_rsdp_table(test_data *data)
{
    AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table;
    uint32_t addr = data->rsdp_addr;

    ACPI_READ_FIELD(rsdp_table->signature, addr);
224
    ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR ");
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

    ACPI_READ_FIELD(rsdp_table->checksum, addr);
    ACPI_READ_ARRAY(rsdp_table->oem_id, addr);
    ACPI_READ_FIELD(rsdp_table->revision, addr);
    ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr);
    ACPI_READ_FIELD(rsdp_table->length, addr);

    /* rsdp checksum is not for the whole table, but for the first 20 bytes */
    g_assert(!acpi_checksum((uint8_t *)rsdp_table, 20));
}

static void test_acpi_rsdt_table(test_data *data)
{
    AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table;
    uint32_t addr = data->rsdp_table.rsdt_physical_address;
    uint32_t *tables;
    int tables_nr;
    uint8_t checksum;

    /* read the header */
    ACPI_READ_TABLE_HEADER(rsdt_table, addr);
246
    ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT");
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

    /* compute the table entries in rsdt */
    tables_nr = (rsdt_table->length - sizeof(AcpiRsdtDescriptorRev1)) /
                sizeof(uint32_t);
    g_assert_cmpint(tables_nr, >, 0);

    /* get the addresses of the tables pointed by rsdt */
    tables = g_new0(uint32_t, tables_nr);
    ACPI_READ_ARRAY_PTR(tables, tables_nr, addr);

    checksum = acpi_checksum((uint8_t *)rsdt_table, rsdt_table->length) +
               acpi_checksum((uint8_t *)tables, tables_nr * sizeof(uint32_t));
    g_assert(!checksum);

   /* SSDT tables after FADT */
    data->rsdt_tables_addr = tables;
    data->rsdt_tables_nr = tables_nr;
}

static void test_acpi_fadt_table(test_data *data)
{
    AcpiFadtDescriptorRev1 *fadt_table = &data->fadt_table;
    uint32_t addr;

    /* FADT table comes first */
    addr = data->rsdt_tables_addr[0];
    ACPI_READ_TABLE_HEADER(fadt_table, addr);

    ACPI_READ_FIELD(fadt_table->firmware_ctrl, addr);
    ACPI_READ_FIELD(fadt_table->dsdt, addr);
    ACPI_READ_FIELD(fadt_table->model, addr);
    ACPI_READ_FIELD(fadt_table->reserved1, addr);
    ACPI_READ_FIELD(fadt_table->sci_int, addr);
    ACPI_READ_FIELD(fadt_table->smi_cmd, addr);
    ACPI_READ_FIELD(fadt_table->acpi_enable, addr);
    ACPI_READ_FIELD(fadt_table->acpi_disable, addr);
    ACPI_READ_FIELD(fadt_table->S4bios_req, addr);
    ACPI_READ_FIELD(fadt_table->reserved2, addr);
    ACPI_READ_FIELD(fadt_table->pm1a_evt_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm1b_evt_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm1a_cnt_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm1b_cnt_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm2_cnt_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm_tmr_blk, addr);
    ACPI_READ_FIELD(fadt_table->gpe0_blk, addr);
    ACPI_READ_FIELD(fadt_table->gpe1_blk, addr);
    ACPI_READ_FIELD(fadt_table->pm1_evt_len, addr);
    ACPI_READ_FIELD(fadt_table->pm1_cnt_len, addr);
    ACPI_READ_FIELD(fadt_table->pm2_cnt_len, addr);
    ACPI_READ_FIELD(fadt_table->pm_tmr_len, addr);
    ACPI_READ_FIELD(fadt_table->gpe0_blk_len, addr);
    ACPI_READ_FIELD(fadt_table->gpe1_blk_len, addr);
    ACPI_READ_FIELD(fadt_table->gpe1_base, addr);
    ACPI_READ_FIELD(fadt_table->reserved3, addr);
    ACPI_READ_FIELD(fadt_table->plvl2_lat, addr);
    ACPI_READ_FIELD(fadt_table->plvl3_lat, addr);
    ACPI_READ_FIELD(fadt_table->flush_size, addr);
    ACPI_READ_FIELD(fadt_table->flush_stride, addr);
    ACPI_READ_FIELD(fadt_table->duty_offset, addr);
    ACPI_READ_FIELD(fadt_table->duty_width, addr);
    ACPI_READ_FIELD(fadt_table->day_alrm, addr);
    ACPI_READ_FIELD(fadt_table->mon_alrm, addr);
    ACPI_READ_FIELD(fadt_table->century, addr);
    ACPI_READ_FIELD(fadt_table->reserved4, addr);
    ACPI_READ_FIELD(fadt_table->reserved4a, addr);
    ACPI_READ_FIELD(fadt_table->reserved4b, addr);
    ACPI_READ_FIELD(fadt_table->flags, addr);

315
    ACPI_ASSERT_CMP(fadt_table->signature, "FACP");
316 317 318
    g_assert(!acpi_checksum((uint8_t *)fadt_table, fadt_table->length));
}

319 320 321 322 323 324 325 326 327 328 329 330 331
static void test_acpi_facs_table(test_data *data)
{
    AcpiFacsDescriptorRev1 *facs_table = &data->facs_table;
    uint32_t addr = data->fadt_table.firmware_ctrl;

    ACPI_READ_FIELD(facs_table->signature, addr);
    ACPI_READ_FIELD(facs_table->length, addr);
    ACPI_READ_FIELD(facs_table->hardware_signature, addr);
    ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr);
    ACPI_READ_FIELD(facs_table->global_lock, addr);
    ACPI_READ_FIELD(facs_table->flags, addr);
    ACPI_READ_ARRAY(facs_table->resverved3, addr);

332
    ACPI_ASSERT_CMP(facs_table->signature, "FACS");
333 334
}

335 336 337 338 339 340 341 342 343 344 345
static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr)
{
    uint8_t checksum;

    ACPI_READ_TABLE_HEADER(&sdt_table->header, addr);

    sdt_table->aml_len = sdt_table->header.length - sizeof(AcpiTableHeader);
    sdt_table->aml = g_malloc0(sdt_table->aml_len);
    ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr);

    checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) +
346
               acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len);
347 348 349 350 351
    g_assert(!checksum);
}

static void test_acpi_dsdt_table(test_data *data)
{
352
    AcpiSdtTable dsdt_table;
353 354
    uint32_t addr = data->fadt_table.dsdt;

355
    memset(&dsdt_table, 0, sizeof(dsdt_table));
356
    data->tables = g_array_new(false, true, sizeof(AcpiSdtTable));
357 358

    test_dst_table(&dsdt_table, addr);
359
    ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT");
360 361

    /* Place DSDT first */
362
    g_array_append_val(data->tables, dsdt_table);
363 364
}

365
static void test_acpi_tables(test_data *data)
366
{
367
    int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
368 369
    int i;

370
    for (i = 0; i < tables_nr; i++) {
371
        AcpiSdtTable ssdt_table;
372 373

        memset(&ssdt_table, 0 , sizeof(ssdt_table));
374
        uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */
375
        test_dst_table(&ssdt_table, addr);
376
        g_array_append_val(data->tables, ssdt_table);
377
    }
378 379
}

380
static void dump_aml_files(test_data *data, bool rebuild)
381 382 383
{
    AcpiSdtTable *sdt;
    GError *error = NULL;
384
    gchar *aml_file = NULL;
385 386 387 388
    gint fd;
    ssize_t ret;
    int i;

389
    for (i = 0; i < data->tables->len; ++i) {
390
        const char *ext = data->variant ? data->variant : "";
391
        sdt = &g_array_index(data->tables, AcpiSdtTable, i);
392 393
        g_assert(sdt->aml);

394
        if (rebuild) {
395
            uint32_t signature = cpu_to_le32(sdt->header.signature);
396 397
            aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
                                       (gchar *)&signature, ext);
398 399 400 401 402 403 404
            fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT,
                        S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
        } else {
            fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error);
            g_assert_no_error(error);
        }
        g_assert(fd >= 0);
405 406 407 408 409 410 411

        ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader));
        g_assert(ret == sizeof(AcpiTableHeader));
        ret = qemu_write_full(fd, sdt->aml, sdt->aml_len);
        g_assert(ret == sdt->aml_len);

        close(fd);
412

413
        g_free(aml_file);
414 415 416
    }
}

417
static bool compare_signature(AcpiSdtTable *sdt, const char *signature)
418
{
419
   return !memcmp(&sdt->header.signature, signature, 4);
420 421
}

422
static bool load_asl(GArray *sdts, AcpiSdtTable *sdt)
423 424 425
{
    AcpiSdtTable *temp;
    GError *error = NULL;
426
    GString *command_line = g_string_new(iasl);
427 428 429 430 431 432 433 434 435 436
    gint fd;
    gchar *out, *out_err;
    gboolean ret;
    int i;

    fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error);
    g_assert_no_error(error);
    close(fd);

    /* build command line */
437
    g_string_append_printf(command_line, " -p %s ", sdt->asl_file);
438 439
    if (compare_signature(sdt, "DSDT") ||
        compare_signature(sdt, "SSDT")) {
440 441
        for (i = 0; i < sdts->len; ++i) {
            temp = &g_array_index(sdts, AcpiSdtTable, i);
442 443
            if (compare_signature(temp, "DSDT") ||
                compare_signature(temp, "SSDT")) {
444 445 446
                g_string_append_printf(command_line, "-e %s ", temp->aml_file);
            }
        }
447 448 449 450
    }
    g_string_append_printf(command_line, "-d %s", sdt->aml_file);

    /* pass 'out' and 'out_err' in order to be redirected */
451
    ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error);
452
    g_assert_no_error(error);
453 454 455 456 457
    if (ret) {
        ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl,
                                  &sdt->asl_len, &error);
        g_assert(ret);
        g_assert_no_error(error);
458
        ret = (sdt->asl_len > 0);
459
    }
460 461 462 463

    g_free(out);
    g_free(out_err);
    g_string_free(command_line, true);
464 465

    return !ret;
466 467 468 469 470 471 472 473 474 475 476 477 478 479
}

#define COMMENT_END "*/"
#define DEF_BLOCK "DefinitionBlock ("
#define BLOCK_NAME_END ".aml"

static GString *normalize_asl(gchar *asl_code)
{
    GString *asl = g_string_new(asl_code);
    gchar *comment, *block_name;

    /* strip comments (different generation days) */
    comment = g_strstr_len(asl->str, asl->len, COMMENT_END);
    if (comment) {
480 481 482 483 484
        comment += strlen(COMMENT_END);
        while (*comment == '\n') {
            comment++;
        }
        asl = g_string_erase(asl, 0, comment - asl->str);
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
    }

    /* strip def block name (it has file path in it) */
    if (g_str_has_prefix(asl->str, DEF_BLOCK)) {
        block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END);
        g_assert(block_name);
        asl = g_string_erase(asl, 0,
                             block_name + sizeof(BLOCK_NAME_END) - asl->str);
    }

    return asl;
}

static GArray *load_expected_aml(test_data *data)
{
    int i;
    AcpiSdtTable *sdt;
502
    gchar *aml_file = NULL;
503 504 505
    GError *error = NULL;
    gboolean ret;

506 507
    GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable));
    for (i = 0; i < data->tables->len; ++i) {
508
        AcpiSdtTable exp_sdt;
509
        uint32_t signature;
510
        const char *ext = data->variant ? data->variant : "";
511

512
        sdt = &g_array_index(data->tables, AcpiSdtTable, i);
513 514 515 516

        memset(&exp_sdt, 0, sizeof(exp_sdt));
        exp_sdt.header.signature = sdt->header.signature;

517
        signature = cpu_to_le32(sdt->header.signature);
518 519 520 521 522 523 524 525 526

try_again:
        aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
                                   (gchar *)&signature, ext);
        if (data->variant && !g_file_test(aml_file, G_FILE_TEST_EXISTS)) {
            g_free(aml_file);
            ext = "";
            goto try_again;
        }
527 528 529 530 531 532 533 534 535
        exp_sdt.aml_file = aml_file;
        g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS));
        ret = g_file_get_contents(aml_file, &exp_sdt.aml,
                                  &exp_sdt.aml_len, &error);
        g_assert(ret);
        g_assert_no_error(error);
        g_assert(exp_sdt.aml);
        g_assert(exp_sdt.aml_len);

536
        g_array_append_val(exp_tables, exp_sdt);
537 538
    }

539
    return exp_tables;
540 541 542 543 544 545 546
}

static void test_acpi_asl(test_data *data)
{
    int i;
    AcpiSdtTable *sdt, *exp_sdt;
    test_data exp_data;
547
    gboolean exp_err, err;
548 549

    memset(&exp_data, 0, sizeof(exp_data));
550
    exp_data.tables = load_expected_aml(data);
551
    dump_aml_files(data, false);
552
    for (i = 0; i < data->tables->len; ++i) {
553 554
        GString *asl, *exp_asl;

555 556
        sdt = &g_array_index(data->tables, AcpiSdtTable, i);
        exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i);
557

558
        err = load_asl(data->tables, sdt);
559 560
        asl = normalize_asl(sdt->asl);

561
        exp_err = load_asl(exp_data.tables, exp_sdt);
562 563
        exp_asl = normalize_asl(exp_sdt->asl);

564 565 566
        /* TODO: check for warnings */
        g_assert(!err || exp_err);

567
        if (g_strcmp0(asl->str, exp_asl->str)) {
568 569 570 571 572 573 574 575 576 577 578 579 580
            if (exp_err) {
                fprintf(stderr,
                        "Warning! iasl couldn't parse the expected aml\n");
            } else {
                uint32_t signature = cpu_to_le32(exp_sdt->header.signature);
                sdt->tmp_files_retain = true;
                exp_sdt->tmp_files_retain = true;
                fprintf(stderr,
                        "acpi-test: Warning! %.4s mismatch. "
                        "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n",
                        (gchar *)&signature,
                        sdt->asl_file, sdt->aml_file,
                        exp_sdt->asl_file, exp_sdt->aml_file);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
                if (getenv("V")) {
                    const char *diff_cmd = getenv("DIFF");
                    if (diff_cmd) {
                        int ret G_GNUC_UNUSED;
                        char *diff = g_strdup_printf("%s %s %s", diff_cmd,
                            exp_sdt->asl_file, sdt->asl_file);
                        ret = system(diff) ;
                        g_free(diff);
                    } else {
                        fprintf(stderr, "acpi-test: Warning. not showing "
                            "difference since no diff utility is specified. "
                            "Set 'DIFF' environment variable to a preferred "
                            "diff utility and run 'make V=1 check' again to "
                            "see ASL difference.");
                    }
                }
597
          }
598
        }
599 600 601 602 603
        g_string_free(asl, true);
        g_string_free(exp_asl, true);
    }

    free_test_data(&exp_data);
604 605
}

606
static bool smbios_ep_table_ok(test_data *data)
G
Gabriel L. Somlo 已提交
607
{
W
Wei Huang 已提交
608
    struct smbios_21_entry_point *ep_table = &data->smbios_ep_table;
G
Gabriel L. Somlo 已提交
609 610 611
    uint32_t addr = data->smbios_ep_addr;

    ACPI_READ_ARRAY(ep_table->anchor_string, addr);
612 613 614
    if (memcmp(ep_table->anchor_string, "_SM_", 4)) {
        return false;
    }
G
Gabriel L. Somlo 已提交
615 616 617 618 619 620 621 622
    ACPI_READ_FIELD(ep_table->checksum, addr);
    ACPI_READ_FIELD(ep_table->length, addr);
    ACPI_READ_FIELD(ep_table->smbios_major_version, addr);
    ACPI_READ_FIELD(ep_table->smbios_minor_version, addr);
    ACPI_READ_FIELD(ep_table->max_structure_size, addr);
    ACPI_READ_FIELD(ep_table->entry_point_revision, addr);
    ACPI_READ_ARRAY(ep_table->formatted_area, addr);
    ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr);
623 624 625
    if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) {
        return false;
    }
G
Gabriel L. Somlo 已提交
626 627
    ACPI_READ_FIELD(ep_table->intermediate_checksum, addr);
    ACPI_READ_FIELD(ep_table->structure_table_length, addr);
628 629 630
    if (ep_table->structure_table_length == 0) {
        return false;
    }
G
Gabriel L. Somlo 已提交
631 632
    ACPI_READ_FIELD(ep_table->structure_table_address, addr);
    ACPI_READ_FIELD(ep_table->number_of_structures, addr);
633 634 635
    if (ep_table->number_of_structures == 0) {
        return false;
    }
G
Gabriel L. Somlo 已提交
636
    ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr);
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
    if (acpi_checksum((uint8_t *)ep_table, sizeof *ep_table) ||
        acpi_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) {
        return false;
    }
    return true;
}

static void test_smbios_entry_point(test_data *data)
{
    uint32_t off;

    /* find smbios entry point structure */
    for (off = 0xf0000; off < 0x100000; off += 0x10) {
        uint8_t sig[] = "_SM_";
        int i;

        for (i = 0; i < sizeof sig - 1; ++i) {
            sig[i] = readb(off + i);
        }

        if (!memcmp(sig, "_SM_", sizeof sig)) {
            /* signature match, but is this a valid entry point? */
            data->smbios_ep_addr = off;
            if (smbios_ep_table_ok(data)) {
                break;
            }
        }
    }

    g_assert_cmphex(off, <, 0x100000);
G
Gabriel L. Somlo 已提交
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
}

static inline bool smbios_single_instance(uint8_t type)
{
    switch (type) {
    case 0:
    case 1:
    case 2:
    case 3:
    case 16:
    case 32:
    case 127:
        return true;
    default:
        return false;
    }
}

static void test_smbios_structs(test_data *data)
{
    DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 };
W
Wei Huang 已提交
688
    struct smbios_21_entry_point *ep_table = &data->smbios_ep_table;
G
Gabriel L. Somlo 已提交
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
    uint32_t addr = ep_table->structure_table_address;
    int i, len, max_len = 0;
    uint8_t type, prv, crt;
    uint8_t required_struct_types[] = {0, 1, 3, 4, 16, 17, 19, 32, 127};

    /* walk the smbios tables */
    for (i = 0; i < ep_table->number_of_structures; i++) {

        /* grab type and formatted area length from struct header */
        type = readb(addr);
        g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE);
        len = readb(addr + 1);

        /* single-instance structs must not have been encountered before */
        if (smbios_single_instance(type)) {
            g_assert(!test_bit(type, struct_bitmap));
        }
        set_bit(type, struct_bitmap);

        /* seek to end of unformatted string area of this struct ("\0\0") */
        prv = crt = 1;
        while (prv || crt) {
            prv = crt;
            crt = readb(addr + len);
            len++;
        }

        /* keep track of max. struct size */
        if (max_len < len) {
            max_len = len;
            g_assert_cmpuint(max_len, <=, ep_table->max_structure_size);
        }

        /* start of next structure */
        addr += len;
    }

    /* total table length and max struct size must match entry point values */
    g_assert_cmpuint(ep_table->structure_table_length, ==,
                     addr - ep_table->structure_table_address);
    g_assert_cmpuint(ep_table->max_structure_size, ==, max_len);

    /* required struct types must all be present */
    for (i = 0; i < ARRAY_SIZE(required_struct_types); i++) {
        g_assert(test_bit(required_struct_types[i], struct_bitmap));
    }
}

737
static void test_acpi_one(const char *params, test_data *data)
738 739 740 741 742 743 744
{
    char *args;
    uint8_t signature_low;
    uint8_t signature_high;
    uint16_t signature;
    int i;

745
    args = g_strdup_printf("-net none -display none %s "
746
                           "-drive id=hd0,if=none,file=%s,format=raw "
747 748
                           "-device ide-hd,drive=hd0 ",
                           params ? params : "", disk);
749

750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
    qtest_start(args);

   /* Wait at most 1 minute */
#define TEST_DELAY (1 * G_USEC_PER_SEC / 10)
#define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1)

    /* Poll until code has run and modified memory.  Once it has we know BIOS
     * initialization is done.  TODO: check that IP reached the halt
     * instruction.
     */
    for (i = 0; i < TEST_CYCLES; ++i) {
        signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET);
        signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1);
        signature = (signature_high << 8) | signature_low;
        if (signature == SIGNATURE) {
            break;
        }
        g_usleep(TEST_DELAY);
    }
    g_assert_cmphex(signature, ==, SIGNATURE);

771 772 773 774
    test_acpi_rsdp_address(data);
    test_acpi_rsdp_table(data);
    test_acpi_rsdt_table(data);
    test_acpi_fadt_table(data);
775
    test_acpi_facs_table(data);
776
    test_acpi_dsdt_table(data);
777
    test_acpi_tables(data);
778

779
    if (iasl) {
780 781 782 783 784
        if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
            dump_aml_files(data, true);
        } else {
            test_acpi_asl(data);
        }
785 786
    }

787
    test_smbios_entry_point(data);
G
Gabriel L. Somlo 已提交
788 789
    test_smbios_structs(data);

790 791 792 793
    qtest_quit(global_qtest);
    g_free(args);
}

794
static void test_acpi_piix4_tcg(void)
795
{
796 797
    test_data data;

798 799 800
    /* Supplying -machine accel argument overrides the default (qtest).
     * This is to make guest actually run.
     */
801 802
    memset(&data, 0, sizeof(data));
    data.machine = MACHINE_PC;
803
    test_acpi_one("-machine accel=tcg", &data);
804
    free_test_data(&data);
805 806
}

807 808 809 810 811 812 813 814 815 816 817
static void test_acpi_piix4_tcg_bridge(void)
{
    test_data data;

    memset(&data, 0, sizeof(data));
    data.machine = MACHINE_PC;
    data.variant = ".bridge";
    test_acpi_one("-machine accel=tcg -device pci-bridge,chassis_nr=1", &data);
    free_test_data(&data);
}

818 819 820
static void test_acpi_q35_tcg(void)
{
    test_data data;
821

822 823 824
    memset(&data, 0, sizeof(data));
    data.machine = MACHINE_Q35;
    test_acpi_one("-machine q35,accel=tcg", &data);
825
    free_test_data(&data);
826 827
}

828 829 830 831 832 833 834 835 836 837 838 839
static void test_acpi_q35_tcg_bridge(void)
{
    test_data data;

    memset(&data, 0, sizeof(data));
    data.machine = MACHINE_Q35;
    data.variant = ".bridge";
    test_acpi_one("-machine q35,accel=tcg -device pci-bridge,chassis_nr=1",
                  &data);
    free_test_data(&data);
}

840 841 842 843
int main(int argc, char *argv[])
{
    const char *arch = qtest_get_arch();
    FILE *f = fopen(disk, "w");
844
    int ret;
845 846 847 848 849

    if (!f) {
        fprintf(stderr, "Couldn't open \"%s\": %s", disk, strerror(errno));
        return 1;
    }
850 851 852 853 854 855
    fwrite(boot_sector, 1, sizeof boot_sector, f);
    fclose(f);

    g_test_init(&argc, &argv, NULL);

    if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
856
        qtest_add_func("acpi/piix4/tcg", test_acpi_piix4_tcg);
857
        qtest_add_func("acpi/piix4/tcg/bridge", test_acpi_piix4_tcg_bridge);
858
        qtest_add_func("acpi/q35/tcg", test_acpi_q35_tcg);
859
        qtest_add_func("acpi/q35/tcg/bridge", test_acpi_q35_tcg_bridge);
860
    }
861 862 863
    ret = g_test_run();
    unlink(disk);
    return ret;
864
}