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