qemu/tests/hd-geo-test.c
<<
>>
Prefs
   1/*
   2 * Hard disk geometry test cases.
   3 *
   4 * Copyright (c) 2012 Red Hat Inc.
   5 *
   6 * Authors:
   7 *  Markus Armbruster <armbru@redhat.com>,
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13/*
  14 * Covers only IDE and tests only CMOS contents.  Better than nothing.
  15 * Improvements welcome.
  16 */
  17
  18#include "qemu/osdep.h"
  19#include <glib.h>
  20#include "qemu-common.h"
  21#include "libqtest.h"
  22
  23static char *create_test_img(int secs)
  24{
  25    char *template = strdup("/tmp/qtest.XXXXXX");
  26    int fd, ret;
  27
  28    fd = mkstemp(template);
  29    g_assert(fd >= 0);
  30    ret = ftruncate(fd, (off_t)secs * 512);
  31    g_assert(ret == 0);
  32    close(fd);
  33    return template;
  34}
  35
  36typedef struct {
  37    int cyls, heads, secs, trans;
  38} CHST;
  39
  40typedef enum {
  41    mbr_blank, mbr_lba, mbr_chs,
  42    mbr_last
  43} MBRcontents;
  44
  45typedef enum {
  46    /* order is relevant */
  47    backend_small, backend_large, backend_empty,
  48    backend_last
  49} Backend;
  50
  51static const int img_secs[backend_last] = {
  52    [backend_small] = 61440,
  53    [backend_large] = 8388608,
  54    [backend_empty] = -1,
  55};
  56
  57static const CHST hd_chst[backend_last][mbr_last] = {
  58    [backend_small] = {
  59        [mbr_blank] = { 60, 16, 63, 0 },
  60        [mbr_lba]   = { 60, 16, 63, 2 },
  61        [mbr_chs]   = { 60, 16, 63, 0 }
  62    },
  63    [backend_large] = {
  64        [mbr_blank] = { 8322, 16, 63, 1 },
  65        [mbr_lba]   = { 8322, 16, 63, 1 },
  66        [mbr_chs]   = { 8322, 16, 63, 0 }
  67    },
  68};
  69
  70static const char *img_file_name[backend_last];
  71
  72static const CHST *cur_ide[4];
  73
  74static bool is_hd(const CHST *expected_chst)
  75{
  76    return expected_chst && expected_chst->cyls;
  77}
  78
  79static void test_cmos_byte(int reg, int expected)
  80{
  81    enum { cmos_base = 0x70 };
  82    int actual;
  83
  84    outb(cmos_base + 0, reg);
  85    actual = inb(cmos_base + 1);
  86    g_assert(actual == expected);
  87}
  88
  89static void test_cmos_bytes(int reg0, int n, uint8_t expected[])
  90{
  91    int i;
  92
  93    for (i = 0; i < 9; i++) {
  94        test_cmos_byte(reg0 + i, expected[i]);
  95    }
  96}
  97
  98static void test_cmos_disk_data(void)
  99{
 100    test_cmos_byte(0x12,
 101                   (is_hd(cur_ide[0]) ? 0xf0 : 0) |
 102                   (is_hd(cur_ide[1]) ? 0x0f : 0));
 103}
 104
 105static void test_cmos_drive_cyl(int reg0, const CHST *expected_chst)
 106{
 107    if (is_hd(expected_chst)) {
 108        int c = expected_chst->cyls;
 109        int h = expected_chst->heads;
 110        int s = expected_chst->secs;
 111        uint8_t expected_bytes[9] = {
 112            c & 0xff, c >> 8, h, 0xff, 0xff, 0xc0 | ((h > 8) << 3),
 113            c & 0xff, c >> 8, s
 114        };
 115        test_cmos_bytes(reg0, 9, expected_bytes);
 116    } else {
 117        int i;
 118
 119        for (i = 0; i < 9; i++) {
 120            test_cmos_byte(reg0 + i, 0);
 121        }
 122    }
 123}
 124
 125static void test_cmos_drive1(void)
 126{
 127    test_cmos_byte(0x19, is_hd(cur_ide[0]) ? 47 : 0);
 128    test_cmos_drive_cyl(0x1b, cur_ide[0]);
 129}
 130
 131static void test_cmos_drive2(void)
 132{
 133    test_cmos_byte(0x1a, is_hd(cur_ide[1]) ? 47 : 0);
 134    test_cmos_drive_cyl(0x24, cur_ide[1]);
 135}
 136
 137static void test_cmos_disktransflag(void)
 138{
 139    int val, i;
 140
 141    val = 0;
 142    for (i = 0; i < ARRAY_SIZE(cur_ide); i++) {
 143        if (is_hd(cur_ide[i])) {
 144            val |= cur_ide[i]->trans << (2 * i);
 145        }
 146    }
 147    test_cmos_byte(0x39, val);
 148}
 149
 150static void test_cmos(void)
 151{
 152    test_cmos_disk_data();
 153    test_cmos_drive1();
 154    test_cmos_drive2();
 155    test_cmos_disktransflag();
 156}
 157
 158static int append_arg(int argc, char *argv[], int argv_sz, char *arg)
 159{
 160    g_assert(argc + 1 < argv_sz);
 161    argv[argc++] = arg;
 162    argv[argc] = NULL;
 163    return argc;
 164}
 165
 166static int setup_common(char *argv[], int argv_sz)
 167{
 168    memset(cur_ide, 0, sizeof(cur_ide));
 169    return append_arg(0, argv, argv_sz,
 170                      g_strdup("-nodefaults"));
 171}
 172
 173static void setup_mbr(int img_idx, MBRcontents mbr)
 174{
 175    static const uint8_t part_lba[16] = {
 176        /* chs 0,1,1 (lba 63) to chs 0,127,63 (8001 sectors) */
 177        0x80, 1, 1, 0, 6, 127, 63, 0, 63, 0, 0, 0, 0x41, 0x1F, 0, 0,
 178    };
 179    static const uint8_t part_chs[16] = {
 180        /* chs 0,1,1 (lba 63) to chs 7,15,63 (8001 sectors) */
 181        0x80, 1, 1, 0, 6,  15, 63, 7, 63, 0, 0, 0, 0x41, 0x1F, 0, 0,
 182    };
 183    uint8_t buf[512];
 184    int fd, ret;
 185
 186    memset(buf, 0, sizeof(buf));
 187
 188    if (mbr != mbr_blank) {
 189        buf[0x1fe] = 0x55;
 190        buf[0x1ff] = 0xAA;
 191        memcpy(buf + 0x1BE, mbr == mbr_lba ? part_lba : part_chs, 16);
 192    }
 193
 194    fd = open(img_file_name[img_idx], O_WRONLY);
 195    g_assert(fd >= 0);
 196    ret = write(fd, buf, sizeof(buf));
 197    g_assert(ret == sizeof(buf));
 198    close(fd);
 199}
 200
 201static int setup_ide(int argc, char *argv[], int argv_sz,
 202                     int ide_idx, const char *dev, int img_idx,
 203                     MBRcontents mbr, const char *opts)
 204{
 205    char *s1, *s2, *s3;
 206
 207    s1 = g_strdup_printf("-drive id=drive%d,if=%s",
 208                         ide_idx, dev ? "none" : "ide");
 209    s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx);
 210
 211    if (img_secs[img_idx] >= 0) {
 212        setup_mbr(img_idx, mbr);
 213        s3 = g_strdup_printf(",format=raw,file=%s", img_file_name[img_idx]);
 214    } else {
 215        s3 = g_strdup(",media=cdrom");
 216    }
 217    argc = append_arg(argc, argv, argv_sz,
 218                      g_strdup_printf("%s%s%s%s", s1, s2, s3, opts));
 219    g_free(s1);
 220    g_free(s2);
 221    g_free(s3);
 222
 223    if (dev) {
 224        argc = append_arg(argc, argv, argv_sz,
 225                          g_strdup_printf("-device %s,drive=drive%d,"
 226                                          "bus=ide.%d,unit=%d",
 227                                          dev, ide_idx,
 228                                          ide_idx / 2, ide_idx % 2));
 229    }
 230    return argc;
 231}
 232
 233/*
 234 * Test case: no IDE devices
 235 */
 236static void test_ide_none(void)
 237{
 238    char *argv[256];
 239
 240    setup_common(argv, ARRAY_SIZE(argv));
 241    qtest_start(g_strjoinv(" ", argv));
 242    test_cmos();
 243    qtest_end();
 244}
 245
 246static void test_ide_mbr(bool use_device, MBRcontents mbr)
 247{
 248    char *argv[256];
 249    int argc;
 250    Backend i;
 251    const char *dev;
 252
 253    argc = setup_common(argv, ARRAY_SIZE(argv));
 254    for (i = 0; i < backend_last; i++) {
 255        cur_ide[i] = &hd_chst[i][mbr];
 256        dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL;
 257        argc = setup_ide(argc, argv, ARRAY_SIZE(argv), i, dev, i, mbr, "");
 258    }
 259    qtest_start(g_strjoinv(" ", argv));
 260    test_cmos();
 261    qtest_end();
 262}
 263
 264/*
 265 * Test case: IDE devices (if=ide) with blank MBRs
 266 */
 267static void test_ide_drive_mbr_blank(void)
 268{
 269    test_ide_mbr(false, mbr_blank);
 270}
 271
 272/*
 273 * Test case: IDE devices (if=ide) with MBRs indicating LBA is in use
 274 */
 275static void test_ide_drive_mbr_lba(void)
 276{
 277    test_ide_mbr(false, mbr_lba);
 278}
 279
 280/*
 281 * Test case: IDE devices (if=ide) with MBRs indicating CHS is in use
 282 */
 283static void test_ide_drive_mbr_chs(void)
 284{
 285    test_ide_mbr(false, mbr_chs);
 286}
 287
 288/*
 289 * Test case: IDE devices (if=none) with blank MBRs
 290 */
 291static void test_ide_device_mbr_blank(void)
 292{
 293    test_ide_mbr(true, mbr_blank);
 294}
 295
 296/*
 297 * Test case: IDE devices (if=none) with MBRs indicating LBA is in use
 298 */
 299static void test_ide_device_mbr_lba(void)
 300{
 301    test_ide_mbr(true, mbr_lba);
 302}
 303
 304/*
 305 * Test case: IDE devices (if=none) with MBRs indicating CHS is in use
 306 */
 307static void test_ide_device_mbr_chs(void)
 308{
 309    test_ide_mbr(true, mbr_chs);
 310}
 311
 312static void test_ide_drive_user(const char *dev, bool trans)
 313{
 314    char *argv[256], *opts;
 315    int argc;
 316    int secs = img_secs[backend_small];
 317    const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans };
 318
 319    argc = setup_common(argv, ARRAY_SIZE(argv));
 320    opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d",
 321                           dev ?: "",
 322                           trans && dev ? "bios-chs-" : "",
 323                           trans ? "trans=lba," : "",
 324                           expected_chst.cyls, expected_chst.heads,
 325                           expected_chst.secs);
 326    cur_ide[0] = &expected_chst;
 327    argc = setup_ide(argc, argv, ARRAY_SIZE(argv),
 328                     0, dev ? opts : NULL, backend_small, mbr_chs,
 329                     dev ? "" : opts);
 330    g_free(opts);
 331    qtest_start(g_strjoinv(" ", argv));
 332    test_cmos();
 333    qtest_end();
 334}
 335
 336/*
 337 * Test case: IDE device (if=ide) with explicit CHS
 338 */
 339static void test_ide_drive_user_chs(void)
 340{
 341    test_ide_drive_user(NULL, false);
 342}
 343
 344/*
 345 * Test case: IDE device (if=ide) with explicit CHS and translation
 346 */
 347static void test_ide_drive_user_chst(void)
 348{
 349    test_ide_drive_user(NULL, true);
 350}
 351
 352/*
 353 * Test case: IDE device (if=none) with explicit CHS
 354 */
 355static void test_ide_device_user_chs(void)
 356{
 357    test_ide_drive_user("ide-hd", false);
 358}
 359
 360/*
 361 * Test case: IDE device (if=none) with explicit CHS and translation
 362 */
 363static void test_ide_device_user_chst(void)
 364{
 365    test_ide_drive_user("ide-hd", true);
 366}
 367
 368/*
 369 * Test case: IDE devices (if=ide), but use index=0 for CD-ROM
 370 */
 371static void test_ide_drive_cd_0(void)
 372{
 373    char *argv[256];
 374    int argc, ide_idx;
 375    Backend i;
 376
 377    argc = setup_common(argv, ARRAY_SIZE(argv));
 378    for (i = 0; i <= backend_empty; i++) {
 379        ide_idx = backend_empty - i;
 380        cur_ide[ide_idx] = &hd_chst[i][mbr_blank];
 381        argc = setup_ide(argc, argv, ARRAY_SIZE(argv),
 382                         ide_idx, NULL, i, mbr_blank, "");
 383    }
 384    qtest_start(g_strjoinv(" ", argv));
 385    test_cmos();
 386    qtest_end();
 387}
 388
 389int main(int argc, char **argv)
 390{
 391    Backend i;
 392    int ret;
 393
 394    g_test_init(&argc, &argv, NULL);
 395
 396    for (i = 0; i < backend_last; i++) {
 397        if (img_secs[i] >= 0) {
 398            img_file_name[i] = create_test_img(img_secs[i]);
 399        } else {
 400            img_file_name[i] = NULL;
 401        }
 402    }
 403
 404    qtest_add_func("hd-geo/ide/none", test_ide_none);
 405    qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank);
 406    qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba);
 407    qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs);
 408    qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs);
 409    qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst);
 410    qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0);
 411    qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank);
 412    qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba);
 413    qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs);
 414    qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs);
 415    qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst);
 416
 417    ret = g_test_run();
 418
 419    for (i = 0; i < backend_last; i++) {
 420        unlink(img_file_name[i]);
 421    }
 422
 423    return ret;
 424}
 425