Statistics
| Branch: | Revision:

root / tests / ide-test.c @ f487b677

History | View | Annotate | Download (12.9 kB)

1
/*
2
 * IDE test cases
3
 *
4
 * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24

    
25
#include <stdint.h>
26
#include <string.h>
27
#include <stdio.h>
28

    
29
#include <glib.h>
30

    
31
#include "libqtest.h"
32
#include "libqos/pci-pc.h"
33
#include "libqos/malloc-pc.h"
34

    
35
#include "qemu-common.h"
36
#include "hw/pci/pci_ids.h"
37
#include "hw/pci/pci_regs.h"
38

    
39
#define TEST_IMAGE_SIZE 64 * 1024 * 1024
40

    
41
#define IDE_PCI_DEV     1
42
#define IDE_PCI_FUNC    1
43

    
44
#define IDE_BASE 0x1f0
45
#define IDE_PRIMARY_IRQ 14
46

    
47
enum {
48
    reg_data        = 0x0,
49
    reg_nsectors    = 0x2,
50
    reg_lba_low     = 0x3,
51
    reg_lba_middle  = 0x4,
52
    reg_lba_high    = 0x5,
53
    reg_device      = 0x6,
54
    reg_status      = 0x7,
55
    reg_command     = 0x7,
56
};
57

    
58
enum {
59
    BSY     = 0x80,
60
    DRDY    = 0x40,
61
    DF      = 0x20,
62
    DRQ     = 0x08,
63
    ERR     = 0x01,
64
};
65

    
66
enum {
67
    DEV     = 0x10,
68
    LBA     = 0x40,
69
};
70

    
71
enum {
72
    bmreg_cmd       = 0x0,
73
    bmreg_status    = 0x2,
74
    bmreg_prdt      = 0x4,
75
};
76

    
77
enum {
78
    CMD_READ_DMA    = 0xc8,
79
    CMD_WRITE_DMA   = 0xca,
80
    CMD_FLUSH_CACHE = 0xe7,
81
    CMD_IDENTIFY    = 0xec,
82

    
83
    CMDF_ABORT      = 0x100,
84
};
85

    
86
enum {
87
    BM_CMD_START    =  0x1,
88
    BM_CMD_WRITE    =  0x8, /* write = from device to memory */
89
};
90

    
91
enum {
92
    BM_STS_ACTIVE   =  0x1,
93
    BM_STS_ERROR    =  0x2,
94
    BM_STS_INTR     =  0x4,
95
};
96

    
97
enum {
98
    PRDT_EOT        = 0x80000000,
99
};
100

    
101
#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
102
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
103

    
104
static QPCIBus *pcibus = NULL;
105
static QGuestAllocator *guest_malloc;
106

    
107
static char tmp_path[] = "/tmp/qtest.XXXXXX";
108

    
109
static void ide_test_start(const char *cmdline_fmt, ...)
110
{
111
    va_list ap;
112
    char *cmdline;
113

    
114
    va_start(ap, cmdline_fmt);
115
    cmdline = g_strdup_vprintf(cmdline_fmt, ap);
116
    va_end(ap);
117

    
118
    qtest_start(cmdline);
119
    qtest_irq_intercept_in(global_qtest, "ioapic");
120
    guest_malloc = pc_alloc_init();
121
}
122

    
123
static void ide_test_quit(void)
124
{
125
    qtest_quit(global_qtest);
126
}
127

    
128
static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
129
{
130
    QPCIDevice *dev;
131
    uint16_t vendor_id, device_id;
132

    
133
    if (!pcibus) {
134
        pcibus = qpci_init_pc();
135
    }
136

    
137
    /* Find PCI device and verify it's the right one */
138
    dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
139
    g_assert(dev != NULL);
140

    
141
    vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
142
    device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
143
    g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
144
    g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
145

    
146
    /* Map bmdma BAR */
147
    *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
148

    
149
    qpci_device_enable(dev);
150

    
151
    return dev;
152
}
153

    
154
static void free_pci_device(QPCIDevice *dev)
155
{
156
    /* libqos doesn't have a function for this, so free it manually */
157
    g_free(dev);
158
}
159

    
160
typedef struct PrdtEntry {
161
    uint32_t addr;
162
    uint32_t size;
163
} QEMU_PACKED PrdtEntry;
164

    
165
#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
166
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
167

    
168
static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
169
                            PrdtEntry *prdt, int prdt_entries)
170
{
171
    QPCIDevice *dev;
172
    uint16_t bmdma_base;
173
    uintptr_t guest_prdt;
174
    size_t len;
175
    bool from_dev;
176
    uint8_t status;
177
    int flags;
178

    
179
    dev = get_pci_device(&bmdma_base);
180

    
181
    flags = cmd & ~0xff;
182
    cmd &= 0xff;
183

    
184
    switch (cmd) {
185
    case CMD_READ_DMA:
186
        from_dev = true;
187
        break;
188
    case CMD_WRITE_DMA:
189
        from_dev = false;
190
        break;
191
    default:
192
        g_assert_not_reached();
193
    }
194

    
195
    /* Select device 0 */
196
    outb(IDE_BASE + reg_device, 0 | LBA);
197

    
198
    /* Stop any running transfer, clear any pending interrupt */
199
    outb(bmdma_base + bmreg_cmd, 0);
200
    outb(bmdma_base + bmreg_status, BM_STS_INTR);
201

    
202
    /* Setup PRDT */
203
    len = sizeof(*prdt) * prdt_entries;
204
    guest_prdt = guest_alloc(guest_malloc, len);
205
    memwrite(guest_prdt, prdt, len);
206
    outl(bmdma_base + bmreg_prdt, guest_prdt);
207

    
208
    /* ATA DMA command */
209
    outb(IDE_BASE + reg_nsectors, nb_sectors);
210

    
211
    outb(IDE_BASE + reg_lba_low,    sector & 0xff);
212
    outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
213
    outb(IDE_BASE + reg_lba_high,   (sector >> 16) & 0xff);
214

    
215
    outb(IDE_BASE + reg_command, cmd);
216

    
217
    /* Start DMA transfer */
218
    outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
219

    
220
    if (flags & CMDF_ABORT) {
221
        outb(bmdma_base + bmreg_cmd, 0);
222
    }
223

    
224
    /* Wait for the DMA transfer to complete */
225
    do {
226
        status = inb(bmdma_base + bmreg_status);
227
    } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
228

    
229
    g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
230

    
231
    /* Check IDE status code */
232
    assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
233
    assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
234

    
235
    /* Reading the status register clears the IRQ */
236
    g_assert(!get_irq(IDE_PRIMARY_IRQ));
237

    
238
    /* Stop DMA transfer if still active */
239
    if (status & BM_STS_ACTIVE) {
240
        outb(bmdma_base + bmreg_cmd, 0);
241
    }
242

    
243
    free_pci_device(dev);
244

    
245
    return status;
246
}
247

    
248
static void test_bmdma_simple_rw(void)
249
{
250
    uint8_t status;
251
    uint8_t *buf;
252
    uint8_t *cmpbuf;
253
    size_t len = 512;
254
    uintptr_t guest_buf = guest_alloc(guest_malloc, len);
255

    
256
    PrdtEntry prdt[] = {
257
        {
258
            .addr = cpu_to_le32(guest_buf),
259
            .size = cpu_to_le32(len | PRDT_EOT),
260
        },
261
    };
262

    
263
    buf = g_malloc(len);
264
    cmpbuf = g_malloc(len);
265

    
266
    /* Write 0x55 pattern to sector 0 */
267
    memset(buf, 0x55, len);
268
    memwrite(guest_buf, buf, len);
269

    
270
    status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
271
    g_assert_cmphex(status, ==, BM_STS_INTR);
272
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
273

    
274
    /* Write 0xaa pattern to sector 1 */
275
    memset(buf, 0xaa, len);
276
    memwrite(guest_buf, buf, len);
277

    
278
    status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
279
    g_assert_cmphex(status, ==, BM_STS_INTR);
280
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
281

    
282
    /* Read and verify 0x55 pattern in sector 0 */
283
    memset(cmpbuf, 0x55, len);
284

    
285
    status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
286
    g_assert_cmphex(status, ==, BM_STS_INTR);
287
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
288

    
289
    memread(guest_buf, buf, len);
290
    g_assert(memcmp(buf, cmpbuf, len) == 0);
291

    
292
    /* Read and verify 0xaa pattern in sector 1 */
293
    memset(cmpbuf, 0xaa, len);
294

    
295
    status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
296
    g_assert_cmphex(status, ==, BM_STS_INTR);
297
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
298

    
299
    memread(guest_buf, buf, len);
300
    g_assert(memcmp(buf, cmpbuf, len) == 0);
301

    
302

    
303
    g_free(buf);
304
    g_free(cmpbuf);
305
}
306

    
307
static void test_bmdma_short_prdt(void)
308
{
309
    uint8_t status;
310

    
311
    PrdtEntry prdt[] = {
312
        {
313
            .addr = 0,
314
            .size = cpu_to_le32(0x10 | PRDT_EOT),
315
        },
316
    };
317

    
318
    /* Normal request */
319
    status = send_dma_request(CMD_READ_DMA, 0, 1,
320
                              prdt, ARRAY_SIZE(prdt));
321
    g_assert_cmphex(status, ==, 0);
322
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
323

    
324
    /* Abort the request before it completes */
325
    status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
326
                              prdt, ARRAY_SIZE(prdt));
327
    g_assert_cmphex(status, ==, 0);
328
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
329
}
330

    
331
static void test_bmdma_long_prdt(void)
332
{
333
    uint8_t status;
334

    
335
    PrdtEntry prdt[] = {
336
        {
337
            .addr = 0,
338
            .size = cpu_to_le32(0x1000 | PRDT_EOT),
339
        },
340
    };
341

    
342
    /* Normal request */
343
    status = send_dma_request(CMD_READ_DMA, 0, 1,
344
                              prdt, ARRAY_SIZE(prdt));
345
    g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
346
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
347

    
348
    /* Abort the request before it completes */
349
    status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
350
                              prdt, ARRAY_SIZE(prdt));
351
    g_assert_cmphex(status, ==, BM_STS_INTR);
352
    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
353
}
354

    
355
static void test_bmdma_setup(void)
356
{
357
    ide_test_start(
358
        "-vnc none "
359
        "-drive file=%s,if=ide,serial=%s,cache=writeback "
360
        "-global ide-hd.ver=%s",
361
        tmp_path, "testdisk", "version");
362
}
363

    
364
static void test_bmdma_teardown(void)
365
{
366
    ide_test_quit();
367
}
368

    
369
static void string_cpu_to_be16(uint16_t *s, size_t bytes)
370
{
371
    g_assert((bytes & 1) == 0);
372
    bytes /= 2;
373

    
374
    while (bytes--) {
375
        *s = cpu_to_be16(*s);
376
        s++;
377
    }
378
}
379

    
380
static void test_identify(void)
381
{
382
    uint8_t data;
383
    uint16_t buf[256];
384
    int i;
385
    int ret;
386

    
387
    ide_test_start(
388
        "-vnc none "
389
        "-drive file=%s,if=ide,serial=%s,cache=writeback "
390
        "-global ide-hd.ver=%s",
391
        tmp_path, "testdisk", "version");
392

    
393
    /* IDENTIFY command on device 0*/
394
    outb(IDE_BASE + reg_device, 0);
395
    outb(IDE_BASE + reg_command, CMD_IDENTIFY);
396

    
397
    /* Read in the IDENTIFY buffer and check registers */
398
    data = inb(IDE_BASE + reg_device);
399
    g_assert_cmpint(data & DEV, ==, 0);
400

    
401
    for (i = 0; i < 256; i++) {
402
        data = inb(IDE_BASE + reg_status);
403
        assert_bit_set(data, DRDY | DRQ);
404
        assert_bit_clear(data, BSY | DF | ERR);
405

    
406
        ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
407
    }
408

    
409
    data = inb(IDE_BASE + reg_status);
410
    assert_bit_set(data, DRDY);
411
    assert_bit_clear(data, BSY | DF | ERR | DRQ);
412

    
413
    /* Check serial number/version in the buffer */
414
    string_cpu_to_be16(&buf[10], 20);
415
    ret = memcmp(&buf[10], "testdisk            ", 20);
416
    g_assert(ret == 0);
417

    
418
    string_cpu_to_be16(&buf[23], 8);
419
    ret = memcmp(&buf[23], "version ", 8);
420
    g_assert(ret == 0);
421

    
422
    /* Write cache enabled bit */
423
    assert_bit_set(buf[85], 0x20);
424

    
425
    ide_test_quit();
426
}
427

    
428
static void test_flush(void)
429
{
430
    uint8_t data;
431

    
432
    ide_test_start(
433
        "-vnc none "
434
        "-drive file=blkdebug::%s,if=ide,cache=writeback",
435
        tmp_path);
436

    
437
    /* Delay the completion of the flush request until we explicitly do it */
438
    qmp("{'execute':'human-monitor-command', 'arguments': { "
439
        "'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
440

    
441
    /* FLUSH CACHE command on device 0*/
442
    outb(IDE_BASE + reg_device, 0);
443
    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
444

    
445
    /* Check status while request is in flight*/
446
    data = inb(IDE_BASE + reg_status);
447
    assert_bit_set(data, BSY | DRDY);
448
    assert_bit_clear(data, DF | ERR | DRQ);
449

    
450
    /* Complete the command */
451
    qmp("{'execute':'human-monitor-command', 'arguments': { "
452
        "'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }");
453

    
454
    /* Check registers */
455
    data = inb(IDE_BASE + reg_device);
456
    g_assert_cmpint(data & DEV, ==, 0);
457

    
458
    do {
459
        data = inb(IDE_BASE + reg_status);
460
    } while (data & BSY);
461

    
462
    assert_bit_set(data, DRDY);
463
    assert_bit_clear(data, BSY | DF | ERR | DRQ);
464

    
465
    ide_test_quit();
466
}
467

    
468
int main(int argc, char **argv)
469
{
470
    const char *arch = qtest_get_arch();
471
    int fd;
472
    int ret;
473

    
474
    /* Check architecture */
475
    if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
476
        g_test_message("Skipping test for non-x86\n");
477
        return 0;
478
    }
479

    
480
    /* Create a temporary raw image */
481
    fd = mkstemp(tmp_path);
482
    g_assert(fd >= 0);
483
    ret = ftruncate(fd, TEST_IMAGE_SIZE);
484
    g_assert(ret == 0);
485
    close(fd);
486

    
487
    /* Run the tests */
488
    g_test_init(&argc, &argv, NULL);
489

    
490
    qtest_add_func("/ide/identify", test_identify);
491

    
492
    qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
493
    qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
494
    qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
495
    qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
496
    qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
497

    
498
    qtest_add_func("/ide/flush", test_flush);
499

    
500
    ret = g_test_run();
501

    
502
    /* Cleanup */
503
    unlink(tmp_path);
504

    
505
    return ret;
506
}