1acbe4801SKevin Wolf /* 2acbe4801SKevin Wolf * IDE test cases 3acbe4801SKevin Wolf * 4acbe4801SKevin Wolf * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> 5acbe4801SKevin Wolf * 6acbe4801SKevin Wolf * Permission is hereby granted, free of charge, to any person obtaining a copy 7acbe4801SKevin Wolf * of this software and associated documentation files (the "Software"), to deal 8acbe4801SKevin Wolf * in the Software without restriction, including without limitation the rights 9acbe4801SKevin Wolf * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10acbe4801SKevin Wolf * copies of the Software, and to permit persons to whom the Software is 11acbe4801SKevin Wolf * furnished to do so, subject to the following conditions: 12acbe4801SKevin Wolf * 13acbe4801SKevin Wolf * The above copyright notice and this permission notice shall be included in 14acbe4801SKevin Wolf * all copies or substantial portions of the Software. 15acbe4801SKevin Wolf * 16acbe4801SKevin Wolf * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17acbe4801SKevin Wolf * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18acbe4801SKevin Wolf * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19acbe4801SKevin Wolf * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20acbe4801SKevin Wolf * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21acbe4801SKevin Wolf * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22acbe4801SKevin Wolf * THE SOFTWARE. 23acbe4801SKevin Wolf */ 24acbe4801SKevin Wolf 25acbe4801SKevin Wolf #include <stdint.h> 26acbe4801SKevin Wolf #include <string.h> 27acbe4801SKevin Wolf #include <stdio.h> 28acbe4801SKevin Wolf 29acbe4801SKevin Wolf #include <glib.h> 30acbe4801SKevin Wolf 31acbe4801SKevin Wolf #include "libqtest.h" 32b95739dcSKevin Wolf #include "libqos/pci-pc.h" 33b95739dcSKevin Wolf #include "libqos/malloc-pc.h" 34acbe4801SKevin Wolf 35acbe4801SKevin Wolf #include "qemu-common.h" 36b95739dcSKevin Wolf #include "hw/pci/pci_ids.h" 37b95739dcSKevin Wolf #include "hw/pci/pci_regs.h" 38acbe4801SKevin Wolf 39acbe4801SKevin Wolf #define TEST_IMAGE_SIZE 64 * 1024 * 1024 40acbe4801SKevin Wolf 41acbe4801SKevin Wolf #define IDE_PCI_DEV 1 42acbe4801SKevin Wolf #define IDE_PCI_FUNC 1 43acbe4801SKevin Wolf 44acbe4801SKevin Wolf #define IDE_BASE 0x1f0 45acbe4801SKevin Wolf #define IDE_PRIMARY_IRQ 14 46acbe4801SKevin Wolf 47acbe4801SKevin Wolf enum { 48acbe4801SKevin Wolf reg_data = 0x0, 49acbe4801SKevin Wolf reg_nsectors = 0x2, 50acbe4801SKevin Wolf reg_lba_low = 0x3, 51acbe4801SKevin Wolf reg_lba_middle = 0x4, 52acbe4801SKevin Wolf reg_lba_high = 0x5, 53acbe4801SKevin Wolf reg_device = 0x6, 54acbe4801SKevin Wolf reg_status = 0x7, 55acbe4801SKevin Wolf reg_command = 0x7, 56acbe4801SKevin Wolf }; 57acbe4801SKevin Wolf 58acbe4801SKevin Wolf enum { 59acbe4801SKevin Wolf BSY = 0x80, 60acbe4801SKevin Wolf DRDY = 0x40, 61acbe4801SKevin Wolf DF = 0x20, 62acbe4801SKevin Wolf DRQ = 0x08, 63acbe4801SKevin Wolf ERR = 0x01, 64acbe4801SKevin Wolf }; 65acbe4801SKevin Wolf 66acbe4801SKevin Wolf enum { 67c27d5656SKevin Wolf DEV = 0x10, 68b95739dcSKevin Wolf LBA = 0x40, 69b95739dcSKevin Wolf }; 70b95739dcSKevin Wolf 71b95739dcSKevin Wolf enum { 72b95739dcSKevin Wolf bmreg_cmd = 0x0, 73b95739dcSKevin Wolf bmreg_status = 0x2, 74b95739dcSKevin Wolf bmreg_prdt = 0x4, 75b95739dcSKevin Wolf }; 76b95739dcSKevin Wolf 77b95739dcSKevin Wolf enum { 78b95739dcSKevin Wolf CMD_READ_DMA = 0xc8, 79b95739dcSKevin Wolf CMD_WRITE_DMA = 0xca, 80bd07684aSKevin Wolf CMD_FLUSH_CACHE = 0xe7, 81acbe4801SKevin Wolf CMD_IDENTIFY = 0xec, 82948eaed1SKevin Wolf 83948eaed1SKevin Wolf CMDF_ABORT = 0x100, 84d7b7e580SKevin Wolf CMDF_NO_BM = 0x200, 85acbe4801SKevin Wolf }; 86acbe4801SKevin Wolf 87b95739dcSKevin Wolf enum { 88b95739dcSKevin Wolf BM_CMD_START = 0x1, 89b95739dcSKevin Wolf BM_CMD_WRITE = 0x8, /* write = from device to memory */ 90b95739dcSKevin Wolf }; 91b95739dcSKevin Wolf 92b95739dcSKevin Wolf enum { 93b95739dcSKevin Wolf BM_STS_ACTIVE = 0x1, 94b95739dcSKevin Wolf BM_STS_ERROR = 0x2, 95b95739dcSKevin Wolf BM_STS_INTR = 0x4, 96b95739dcSKevin Wolf }; 97b95739dcSKevin Wolf 98b95739dcSKevin Wolf enum { 99b95739dcSKevin Wolf PRDT_EOT = 0x80000000, 100b95739dcSKevin Wolf }; 101b95739dcSKevin Wolf 102acbe4801SKevin Wolf #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) 103acbe4801SKevin Wolf #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) 104acbe4801SKevin Wolf 105b95739dcSKevin Wolf static QPCIBus *pcibus = NULL; 106b95739dcSKevin Wolf static QGuestAllocator *guest_malloc; 107b95739dcSKevin Wolf 108acbe4801SKevin Wolf static char tmp_path[] = "/tmp/qtest.XXXXXX"; 10914a92e5fSPaolo Bonzini static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; 110acbe4801SKevin Wolf 111acbe4801SKevin Wolf static void ide_test_start(const char *cmdline_fmt, ...) 112acbe4801SKevin Wolf { 113acbe4801SKevin Wolf va_list ap; 114acbe4801SKevin Wolf char *cmdline; 115acbe4801SKevin Wolf 116acbe4801SKevin Wolf va_start(ap, cmdline_fmt); 117acbe4801SKevin Wolf cmdline = g_strdup_vprintf(cmdline_fmt, ap); 118acbe4801SKevin Wolf va_end(ap); 119acbe4801SKevin Wolf 120acbe4801SKevin Wolf qtest_start(cmdline); 121acbe4801SKevin Wolf qtest_irq_intercept_in(global_qtest, "ioapic"); 122b95739dcSKevin Wolf guest_malloc = pc_alloc_init(); 123*e42de189SJohn Snow 124*e42de189SJohn Snow g_free(cmdline); 125acbe4801SKevin Wolf } 126acbe4801SKevin Wolf 127acbe4801SKevin Wolf static void ide_test_quit(void) 128acbe4801SKevin Wolf { 1291d9358e6SMarkus Armbruster qtest_end(); 130acbe4801SKevin Wolf } 131acbe4801SKevin Wolf 132b95739dcSKevin Wolf static QPCIDevice *get_pci_device(uint16_t *bmdma_base) 133b95739dcSKevin Wolf { 134b95739dcSKevin Wolf QPCIDevice *dev; 135b95739dcSKevin Wolf uint16_t vendor_id, device_id; 136b95739dcSKevin Wolf 137b95739dcSKevin Wolf if (!pcibus) { 138b95739dcSKevin Wolf pcibus = qpci_init_pc(); 139b95739dcSKevin Wolf } 140b95739dcSKevin Wolf 141b95739dcSKevin Wolf /* Find PCI device and verify it's the right one */ 142b95739dcSKevin Wolf dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC)); 143b95739dcSKevin Wolf g_assert(dev != NULL); 144b95739dcSKevin Wolf 145b95739dcSKevin Wolf vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); 146b95739dcSKevin Wolf device_id = qpci_config_readw(dev, PCI_DEVICE_ID); 147b95739dcSKevin Wolf g_assert(vendor_id == PCI_VENDOR_ID_INTEL); 148b95739dcSKevin Wolf g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1); 149b95739dcSKevin Wolf 150b95739dcSKevin Wolf /* Map bmdma BAR */ 1516ce7100eSJohn Snow *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL); 152b95739dcSKevin Wolf 153b95739dcSKevin Wolf qpci_device_enable(dev); 154b95739dcSKevin Wolf 155b95739dcSKevin Wolf return dev; 156b95739dcSKevin Wolf } 157b95739dcSKevin Wolf 158b95739dcSKevin Wolf static void free_pci_device(QPCIDevice *dev) 159b95739dcSKevin Wolf { 160b95739dcSKevin Wolf /* libqos doesn't have a function for this, so free it manually */ 161b95739dcSKevin Wolf g_free(dev); 162b95739dcSKevin Wolf } 163b95739dcSKevin Wolf 164b95739dcSKevin Wolf typedef struct PrdtEntry { 165b95739dcSKevin Wolf uint32_t addr; 166b95739dcSKevin Wolf uint32_t size; 167b95739dcSKevin Wolf } QEMU_PACKED PrdtEntry; 168b95739dcSKevin Wolf 169b95739dcSKevin Wolf #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) 170b95739dcSKevin Wolf #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) 171b95739dcSKevin Wolf 172b95739dcSKevin Wolf static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, 173b95739dcSKevin Wolf PrdtEntry *prdt, int prdt_entries) 174b95739dcSKevin Wolf { 175b95739dcSKevin Wolf QPCIDevice *dev; 176b95739dcSKevin Wolf uint16_t bmdma_base; 177b95739dcSKevin Wolf uintptr_t guest_prdt; 178b95739dcSKevin Wolf size_t len; 179b95739dcSKevin Wolf bool from_dev; 180b95739dcSKevin Wolf uint8_t status; 181948eaed1SKevin Wolf int flags; 182b95739dcSKevin Wolf 183b95739dcSKevin Wolf dev = get_pci_device(&bmdma_base); 184b95739dcSKevin Wolf 185948eaed1SKevin Wolf flags = cmd & ~0xff; 186948eaed1SKevin Wolf cmd &= 0xff; 187948eaed1SKevin Wolf 188b95739dcSKevin Wolf switch (cmd) { 189b95739dcSKevin Wolf case CMD_READ_DMA: 190b95739dcSKevin Wolf from_dev = true; 191b95739dcSKevin Wolf break; 192b95739dcSKevin Wolf case CMD_WRITE_DMA: 193b95739dcSKevin Wolf from_dev = false; 194b95739dcSKevin Wolf break; 195b95739dcSKevin Wolf default: 196b95739dcSKevin Wolf g_assert_not_reached(); 197b95739dcSKevin Wolf } 198b95739dcSKevin Wolf 199d7b7e580SKevin Wolf if (flags & CMDF_NO_BM) { 200d7b7e580SKevin Wolf qpci_config_writew(dev, PCI_COMMAND, 201d7b7e580SKevin Wolf PCI_COMMAND_IO | PCI_COMMAND_MEMORY); 202d7b7e580SKevin Wolf } 203d7b7e580SKevin Wolf 204b95739dcSKevin Wolf /* Select device 0 */ 205b95739dcSKevin Wolf outb(IDE_BASE + reg_device, 0 | LBA); 206b95739dcSKevin Wolf 207b95739dcSKevin Wolf /* Stop any running transfer, clear any pending interrupt */ 208b95739dcSKevin Wolf outb(bmdma_base + bmreg_cmd, 0); 209b95739dcSKevin Wolf outb(bmdma_base + bmreg_status, BM_STS_INTR); 210b95739dcSKevin Wolf 211b95739dcSKevin Wolf /* Setup PRDT */ 212b95739dcSKevin Wolf len = sizeof(*prdt) * prdt_entries; 213b95739dcSKevin Wolf guest_prdt = guest_alloc(guest_malloc, len); 214b95739dcSKevin Wolf memwrite(guest_prdt, prdt, len); 215b95739dcSKevin Wolf outl(bmdma_base + bmreg_prdt, guest_prdt); 216b95739dcSKevin Wolf 217b95739dcSKevin Wolf /* ATA DMA command */ 218b95739dcSKevin Wolf outb(IDE_BASE + reg_nsectors, nb_sectors); 219b95739dcSKevin Wolf 220b95739dcSKevin Wolf outb(IDE_BASE + reg_lba_low, sector & 0xff); 221b95739dcSKevin Wolf outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff); 222b95739dcSKevin Wolf outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff); 223b95739dcSKevin Wolf 224b95739dcSKevin Wolf outb(IDE_BASE + reg_command, cmd); 225b95739dcSKevin Wolf 226b95739dcSKevin Wolf /* Start DMA transfer */ 227b95739dcSKevin Wolf outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); 228b95739dcSKevin Wolf 229948eaed1SKevin Wolf if (flags & CMDF_ABORT) { 230948eaed1SKevin Wolf outb(bmdma_base + bmreg_cmd, 0); 231948eaed1SKevin Wolf } 232948eaed1SKevin Wolf 233b95739dcSKevin Wolf /* Wait for the DMA transfer to complete */ 234b95739dcSKevin Wolf do { 235b95739dcSKevin Wolf status = inb(bmdma_base + bmreg_status); 236b95739dcSKevin Wolf } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); 237b95739dcSKevin Wolf 238b95739dcSKevin Wolf g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR)); 239b95739dcSKevin Wolf 240b95739dcSKevin Wolf /* Check IDE status code */ 241b95739dcSKevin Wolf assert_bit_set(inb(IDE_BASE + reg_status), DRDY); 242b95739dcSKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ); 243b95739dcSKevin Wolf 244b95739dcSKevin Wolf /* Reading the status register clears the IRQ */ 245b95739dcSKevin Wolf g_assert(!get_irq(IDE_PRIMARY_IRQ)); 246b95739dcSKevin Wolf 247b95739dcSKevin Wolf /* Stop DMA transfer if still active */ 248b95739dcSKevin Wolf if (status & BM_STS_ACTIVE) { 249b95739dcSKevin Wolf outb(bmdma_base + bmreg_cmd, 0); 250b95739dcSKevin Wolf } 251b95739dcSKevin Wolf 252b95739dcSKevin Wolf free_pci_device(dev); 253b95739dcSKevin Wolf 254b95739dcSKevin Wolf return status; 255b95739dcSKevin Wolf } 256b95739dcSKevin Wolf 257b95739dcSKevin Wolf static void test_bmdma_simple_rw(void) 258b95739dcSKevin Wolf { 259b95739dcSKevin Wolf uint8_t status; 260b95739dcSKevin Wolf uint8_t *buf; 261b95739dcSKevin Wolf uint8_t *cmpbuf; 262b95739dcSKevin Wolf size_t len = 512; 263b95739dcSKevin Wolf uintptr_t guest_buf = guest_alloc(guest_malloc, len); 264b95739dcSKevin Wolf 265b95739dcSKevin Wolf PrdtEntry prdt[] = { 266262f27b9SKevin Wolf { 267262f27b9SKevin Wolf .addr = cpu_to_le32(guest_buf), 268262f27b9SKevin Wolf .size = cpu_to_le32(len | PRDT_EOT), 269262f27b9SKevin Wolf }, 270b95739dcSKevin Wolf }; 271b95739dcSKevin Wolf 272b95739dcSKevin Wolf buf = g_malloc(len); 273b95739dcSKevin Wolf cmpbuf = g_malloc(len); 274b95739dcSKevin Wolf 275b95739dcSKevin Wolf /* Write 0x55 pattern to sector 0 */ 276b95739dcSKevin Wolf memset(buf, 0x55, len); 277b95739dcSKevin Wolf memwrite(guest_buf, buf, len); 278b95739dcSKevin Wolf 279b95739dcSKevin Wolf status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt)); 280b95739dcSKevin Wolf g_assert_cmphex(status, ==, BM_STS_INTR); 281b95739dcSKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 282b95739dcSKevin Wolf 283b95739dcSKevin Wolf /* Write 0xaa pattern to sector 1 */ 284b95739dcSKevin Wolf memset(buf, 0xaa, len); 285b95739dcSKevin Wolf memwrite(guest_buf, buf, len); 286b95739dcSKevin Wolf 287b95739dcSKevin Wolf status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt)); 288b95739dcSKevin Wolf g_assert_cmphex(status, ==, BM_STS_INTR); 289b95739dcSKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 290b95739dcSKevin Wolf 291b95739dcSKevin Wolf /* Read and verify 0x55 pattern in sector 0 */ 292b95739dcSKevin Wolf memset(cmpbuf, 0x55, len); 293b95739dcSKevin Wolf 294b95739dcSKevin Wolf status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt)); 295b95739dcSKevin Wolf g_assert_cmphex(status, ==, BM_STS_INTR); 296b95739dcSKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 297b95739dcSKevin Wolf 298b95739dcSKevin Wolf memread(guest_buf, buf, len); 299b95739dcSKevin Wolf g_assert(memcmp(buf, cmpbuf, len) == 0); 300b95739dcSKevin Wolf 301b95739dcSKevin Wolf /* Read and verify 0xaa pattern in sector 1 */ 302b95739dcSKevin Wolf memset(cmpbuf, 0xaa, len); 303b95739dcSKevin Wolf 304b95739dcSKevin Wolf status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt)); 305b95739dcSKevin Wolf g_assert_cmphex(status, ==, BM_STS_INTR); 306b95739dcSKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 307b95739dcSKevin Wolf 308b95739dcSKevin Wolf memread(guest_buf, buf, len); 309b95739dcSKevin Wolf g_assert(memcmp(buf, cmpbuf, len) == 0); 310b95739dcSKevin Wolf 311b95739dcSKevin Wolf 312b95739dcSKevin Wolf g_free(buf); 313b95739dcSKevin Wolf g_free(cmpbuf); 314b95739dcSKevin Wolf } 315b95739dcSKevin Wolf 316948eaed1SKevin Wolf static void test_bmdma_short_prdt(void) 317948eaed1SKevin Wolf { 318948eaed1SKevin Wolf uint8_t status; 319948eaed1SKevin Wolf 320948eaed1SKevin Wolf PrdtEntry prdt[] = { 321262f27b9SKevin Wolf { 322262f27b9SKevin Wolf .addr = 0, 323262f27b9SKevin Wolf .size = cpu_to_le32(0x10 | PRDT_EOT), 324262f27b9SKevin Wolf }, 325948eaed1SKevin Wolf }; 326948eaed1SKevin Wolf 327948eaed1SKevin Wolf /* Normal request */ 328948eaed1SKevin Wolf status = send_dma_request(CMD_READ_DMA, 0, 1, 329948eaed1SKevin Wolf prdt, ARRAY_SIZE(prdt)); 330948eaed1SKevin Wolf g_assert_cmphex(status, ==, 0); 331948eaed1SKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 332948eaed1SKevin Wolf 333948eaed1SKevin Wolf /* Abort the request before it completes */ 334948eaed1SKevin Wolf status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, 335948eaed1SKevin Wolf prdt, ARRAY_SIZE(prdt)); 336948eaed1SKevin Wolf g_assert_cmphex(status, ==, 0); 337948eaed1SKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 338948eaed1SKevin Wolf } 339948eaed1SKevin Wolf 340948eaed1SKevin Wolf static void test_bmdma_long_prdt(void) 341948eaed1SKevin Wolf { 342948eaed1SKevin Wolf uint8_t status; 343948eaed1SKevin Wolf 344948eaed1SKevin Wolf PrdtEntry prdt[] = { 345262f27b9SKevin Wolf { 346262f27b9SKevin Wolf .addr = 0, 347262f27b9SKevin Wolf .size = cpu_to_le32(0x1000 | PRDT_EOT), 348262f27b9SKevin Wolf }, 349948eaed1SKevin Wolf }; 350948eaed1SKevin Wolf 351948eaed1SKevin Wolf /* Normal request */ 352948eaed1SKevin Wolf status = send_dma_request(CMD_READ_DMA, 0, 1, 353948eaed1SKevin Wolf prdt, ARRAY_SIZE(prdt)); 354948eaed1SKevin Wolf g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); 355948eaed1SKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 356948eaed1SKevin Wolf 357948eaed1SKevin Wolf /* Abort the request before it completes */ 358948eaed1SKevin Wolf status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, 359948eaed1SKevin Wolf prdt, ARRAY_SIZE(prdt)); 360948eaed1SKevin Wolf g_assert_cmphex(status, ==, BM_STS_INTR); 361948eaed1SKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 362948eaed1SKevin Wolf } 363948eaed1SKevin Wolf 364d7b7e580SKevin Wolf static void test_bmdma_no_busmaster(void) 365d7b7e580SKevin Wolf { 366d7b7e580SKevin Wolf uint8_t status; 367d7b7e580SKevin Wolf 368d7b7e580SKevin Wolf /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be 369d7b7e580SKevin Wolf * able to access it anyway because the Bus Master bit in the PCI command 370d7b7e580SKevin Wolf * register isn't set. This is complete nonsense, but it used to be pretty 371d7b7e580SKevin Wolf * good at confusing and occasionally crashing qemu. */ 372d7b7e580SKevin Wolf PrdtEntry prdt[4096] = { }; 373d7b7e580SKevin Wolf 374d7b7e580SKevin Wolf status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512, 375d7b7e580SKevin Wolf prdt, ARRAY_SIZE(prdt)); 376d7b7e580SKevin Wolf 377d7b7e580SKevin Wolf /* Not entirely clear what the expected result is, but this is what we get 378d7b7e580SKevin Wolf * in practice. At least we want to be aware of any changes. */ 379d7b7e580SKevin Wolf g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); 380d7b7e580SKevin Wolf assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); 381d7b7e580SKevin Wolf } 382d7b7e580SKevin Wolf 383b95739dcSKevin Wolf static void test_bmdma_setup(void) 384b95739dcSKevin Wolf { 385b95739dcSKevin Wolf ide_test_start( 386b95739dcSKevin Wolf "-drive file=%s,if=ide,serial=%s,cache=writeback " 387b95739dcSKevin Wolf "-global ide-hd.ver=%s", 388b95739dcSKevin Wolf tmp_path, "testdisk", "version"); 389b95739dcSKevin Wolf } 390b95739dcSKevin Wolf 391b95739dcSKevin Wolf static void test_bmdma_teardown(void) 392b95739dcSKevin Wolf { 393b95739dcSKevin Wolf ide_test_quit(); 394b95739dcSKevin Wolf } 395b95739dcSKevin Wolf 396262f27b9SKevin Wolf static void string_cpu_to_be16(uint16_t *s, size_t bytes) 397262f27b9SKevin Wolf { 398262f27b9SKevin Wolf g_assert((bytes & 1) == 0); 399262f27b9SKevin Wolf bytes /= 2; 400262f27b9SKevin Wolf 401262f27b9SKevin Wolf while (bytes--) { 402262f27b9SKevin Wolf *s = cpu_to_be16(*s); 403262f27b9SKevin Wolf s++; 404262f27b9SKevin Wolf } 405262f27b9SKevin Wolf } 406262f27b9SKevin Wolf 407acbe4801SKevin Wolf static void test_identify(void) 408acbe4801SKevin Wolf { 409acbe4801SKevin Wolf uint8_t data; 410acbe4801SKevin Wolf uint16_t buf[256]; 411acbe4801SKevin Wolf int i; 412acbe4801SKevin Wolf int ret; 413acbe4801SKevin Wolf 414acbe4801SKevin Wolf ide_test_start( 415acbe4801SKevin Wolf "-drive file=%s,if=ide,serial=%s,cache=writeback " 416acbe4801SKevin Wolf "-global ide-hd.ver=%s", 417acbe4801SKevin Wolf tmp_path, "testdisk", "version"); 418acbe4801SKevin Wolf 419acbe4801SKevin Wolf /* IDENTIFY command on device 0*/ 420acbe4801SKevin Wolf outb(IDE_BASE + reg_device, 0); 421acbe4801SKevin Wolf outb(IDE_BASE + reg_command, CMD_IDENTIFY); 422acbe4801SKevin Wolf 423acbe4801SKevin Wolf /* Read in the IDENTIFY buffer and check registers */ 424acbe4801SKevin Wolf data = inb(IDE_BASE + reg_device); 425c27d5656SKevin Wolf g_assert_cmpint(data & DEV, ==, 0); 426acbe4801SKevin Wolf 427acbe4801SKevin Wolf for (i = 0; i < 256; i++) { 428acbe4801SKevin Wolf data = inb(IDE_BASE + reg_status); 429acbe4801SKevin Wolf assert_bit_set(data, DRDY | DRQ); 430acbe4801SKevin Wolf assert_bit_clear(data, BSY | DF | ERR); 431acbe4801SKevin Wolf 432acbe4801SKevin Wolf ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data); 433acbe4801SKevin Wolf } 434acbe4801SKevin Wolf 435acbe4801SKevin Wolf data = inb(IDE_BASE + reg_status); 436acbe4801SKevin Wolf assert_bit_set(data, DRDY); 437acbe4801SKevin Wolf assert_bit_clear(data, BSY | DF | ERR | DRQ); 438acbe4801SKevin Wolf 439acbe4801SKevin Wolf /* Check serial number/version in the buffer */ 440262f27b9SKevin Wolf string_cpu_to_be16(&buf[10], 20); 441262f27b9SKevin Wolf ret = memcmp(&buf[10], "testdisk ", 20); 442acbe4801SKevin Wolf g_assert(ret == 0); 443acbe4801SKevin Wolf 444262f27b9SKevin Wolf string_cpu_to_be16(&buf[23], 8); 445262f27b9SKevin Wolf ret = memcmp(&buf[23], "version ", 8); 446acbe4801SKevin Wolf g_assert(ret == 0); 447acbe4801SKevin Wolf 448acbe4801SKevin Wolf /* Write cache enabled bit */ 449acbe4801SKevin Wolf assert_bit_set(buf[85], 0x20); 450acbe4801SKevin Wolf 451acbe4801SKevin Wolf ide_test_quit(); 452acbe4801SKevin Wolf } 453acbe4801SKevin Wolf 454bd07684aSKevin Wolf static void test_flush(void) 455bd07684aSKevin Wolf { 456bd07684aSKevin Wolf uint8_t data; 457bd07684aSKevin Wolf 458bd07684aSKevin Wolf ide_test_start( 459bd07684aSKevin Wolf "-drive file=blkdebug::%s,if=ide,cache=writeback", 460bd07684aSKevin Wolf tmp_path); 461bd07684aSKevin Wolf 462bd07684aSKevin Wolf /* Delay the completion of the flush request until we explicitly do it */ 4630d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" 4640d1aa05eSStefan Hajnoczi " 'command-line':" 4650d1aa05eSStefan Hajnoczi " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); 466bd07684aSKevin Wolf 467bd07684aSKevin Wolf /* FLUSH CACHE command on device 0*/ 468bd07684aSKevin Wolf outb(IDE_BASE + reg_device, 0); 469bd07684aSKevin Wolf outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); 470bd07684aSKevin Wolf 471bd07684aSKevin Wolf /* Check status while request is in flight*/ 472bd07684aSKevin Wolf data = inb(IDE_BASE + reg_status); 473bd07684aSKevin Wolf assert_bit_set(data, BSY | DRDY); 474bd07684aSKevin Wolf assert_bit_clear(data, DF | ERR | DRQ); 475bd07684aSKevin Wolf 476bd07684aSKevin Wolf /* Complete the command */ 4770d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" 4780d1aa05eSStefan Hajnoczi " 'command-line':" 4790d1aa05eSStefan Hajnoczi " 'qemu-io ide0-hd0 \"resume A\"'} }"); 480bd07684aSKevin Wolf 481bd07684aSKevin Wolf /* Check registers */ 482bd07684aSKevin Wolf data = inb(IDE_BASE + reg_device); 483bd07684aSKevin Wolf g_assert_cmpint(data & DEV, ==, 0); 484bd07684aSKevin Wolf 48522bfa16eSMichael Roth do { 486bd07684aSKevin Wolf data = inb(IDE_BASE + reg_status); 48722bfa16eSMichael Roth } while (data & BSY); 48822bfa16eSMichael Roth 489bd07684aSKevin Wolf assert_bit_set(data, DRDY); 490bd07684aSKevin Wolf assert_bit_clear(data, BSY | DF | ERR | DRQ); 491bd07684aSKevin Wolf 492bd07684aSKevin Wolf ide_test_quit(); 493bd07684aSKevin Wolf } 494bd07684aSKevin Wolf 49514a92e5fSPaolo Bonzini static void prepare_blkdebug_script(const char *debug_fn, const char *event) 49614a92e5fSPaolo Bonzini { 49714a92e5fSPaolo Bonzini FILE *debug_file = fopen(debug_fn, "w"); 49814a92e5fSPaolo Bonzini int ret; 49914a92e5fSPaolo Bonzini 50014a92e5fSPaolo Bonzini fprintf(debug_file, "[inject-error]\n"); 50114a92e5fSPaolo Bonzini fprintf(debug_file, "event = \"%s\"\n", event); 50214a92e5fSPaolo Bonzini fprintf(debug_file, "errno = \"5\"\n"); 50314a92e5fSPaolo Bonzini fprintf(debug_file, "state = \"1\"\n"); 50414a92e5fSPaolo Bonzini fprintf(debug_file, "immediately = \"off\"\n"); 50514a92e5fSPaolo Bonzini fprintf(debug_file, "once = \"on\"\n"); 50614a92e5fSPaolo Bonzini 50714a92e5fSPaolo Bonzini fprintf(debug_file, "[set-state]\n"); 50814a92e5fSPaolo Bonzini fprintf(debug_file, "event = \"%s\"\n", event); 50914a92e5fSPaolo Bonzini fprintf(debug_file, "new_state = \"2\"\n"); 51014a92e5fSPaolo Bonzini fflush(debug_file); 51114a92e5fSPaolo Bonzini g_assert(!ferror(debug_file)); 51214a92e5fSPaolo Bonzini 51314a92e5fSPaolo Bonzini ret = fclose(debug_file); 51414a92e5fSPaolo Bonzini g_assert(ret == 0); 51514a92e5fSPaolo Bonzini } 51614a92e5fSPaolo Bonzini 51714a92e5fSPaolo Bonzini static void test_retry_flush(void) 51814a92e5fSPaolo Bonzini { 51914a92e5fSPaolo Bonzini uint8_t data; 52014a92e5fSPaolo Bonzini const char *s; 52114a92e5fSPaolo Bonzini QDict *response; 52214a92e5fSPaolo Bonzini 52314a92e5fSPaolo Bonzini prepare_blkdebug_script(debug_path, "flush_to_disk"); 52414a92e5fSPaolo Bonzini 52514a92e5fSPaolo Bonzini ide_test_start( 52614a92e5fSPaolo Bonzini "-vnc none " 52714a92e5fSPaolo Bonzini "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop", 52814a92e5fSPaolo Bonzini debug_path, tmp_path); 52914a92e5fSPaolo Bonzini 53014a92e5fSPaolo Bonzini /* FLUSH CACHE command on device 0*/ 53114a92e5fSPaolo Bonzini outb(IDE_BASE + reg_device, 0); 53214a92e5fSPaolo Bonzini outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); 53314a92e5fSPaolo Bonzini 53414a92e5fSPaolo Bonzini /* Check status while request is in flight*/ 53514a92e5fSPaolo Bonzini data = inb(IDE_BASE + reg_status); 53614a92e5fSPaolo Bonzini assert_bit_set(data, BSY | DRDY); 53714a92e5fSPaolo Bonzini assert_bit_clear(data, DF | ERR | DRQ); 53814a92e5fSPaolo Bonzini 53914a92e5fSPaolo Bonzini for (;; response = NULL) { 54014a92e5fSPaolo Bonzini response = qmp_receive(); 54114a92e5fSPaolo Bonzini if ((qdict_haskey(response, "event")) && 54214a92e5fSPaolo Bonzini (strcmp(qdict_get_str(response, "event"), "STOP") == 0)) { 54314a92e5fSPaolo Bonzini QDECREF(response); 54414a92e5fSPaolo Bonzini break; 54514a92e5fSPaolo Bonzini } 54614a92e5fSPaolo Bonzini QDECREF(response); 54714a92e5fSPaolo Bonzini } 54814a92e5fSPaolo Bonzini 54914a92e5fSPaolo Bonzini /* Complete the command */ 55014a92e5fSPaolo Bonzini s = "{'execute':'cont' }"; 55114a92e5fSPaolo Bonzini qmp_discard_response(s); 55214a92e5fSPaolo Bonzini 55314a92e5fSPaolo Bonzini /* Check registers */ 55414a92e5fSPaolo Bonzini data = inb(IDE_BASE + reg_device); 55514a92e5fSPaolo Bonzini g_assert_cmpint(data & DEV, ==, 0); 55614a92e5fSPaolo Bonzini 55714a92e5fSPaolo Bonzini do { 55814a92e5fSPaolo Bonzini data = inb(IDE_BASE + reg_status); 55914a92e5fSPaolo Bonzini } while (data & BSY); 56014a92e5fSPaolo Bonzini 56114a92e5fSPaolo Bonzini assert_bit_set(data, DRDY); 56214a92e5fSPaolo Bonzini assert_bit_clear(data, BSY | DF | ERR | DRQ); 56314a92e5fSPaolo Bonzini 56414a92e5fSPaolo Bonzini ide_test_quit(); 56514a92e5fSPaolo Bonzini } 56614a92e5fSPaolo Bonzini 567acbe4801SKevin Wolf int main(int argc, char **argv) 568acbe4801SKevin Wolf { 569acbe4801SKevin Wolf const char *arch = qtest_get_arch(); 570acbe4801SKevin Wolf int fd; 571acbe4801SKevin Wolf int ret; 572acbe4801SKevin Wolf 573acbe4801SKevin Wolf /* Check architecture */ 574acbe4801SKevin Wolf if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { 575acbe4801SKevin Wolf g_test_message("Skipping test for non-x86\n"); 576acbe4801SKevin Wolf return 0; 577acbe4801SKevin Wolf } 578acbe4801SKevin Wolf 57914a92e5fSPaolo Bonzini /* Create temporary blkdebug instructions */ 58014a92e5fSPaolo Bonzini fd = mkstemp(debug_path); 58114a92e5fSPaolo Bonzini g_assert(fd >= 0); 58214a92e5fSPaolo Bonzini close(fd); 58314a92e5fSPaolo Bonzini 584acbe4801SKevin Wolf /* Create a temporary raw image */ 585acbe4801SKevin Wolf fd = mkstemp(tmp_path); 586acbe4801SKevin Wolf g_assert(fd >= 0); 587acbe4801SKevin Wolf ret = ftruncate(fd, TEST_IMAGE_SIZE); 588acbe4801SKevin Wolf g_assert(ret == 0); 589acbe4801SKevin Wolf close(fd); 590acbe4801SKevin Wolf 591acbe4801SKevin Wolf /* Run the tests */ 592acbe4801SKevin Wolf g_test_init(&argc, &argv, NULL); 593acbe4801SKevin Wolf 594acbe4801SKevin Wolf qtest_add_func("/ide/identify", test_identify); 595acbe4801SKevin Wolf 596b95739dcSKevin Wolf qtest_add_func("/ide/bmdma/setup", test_bmdma_setup); 597b95739dcSKevin Wolf qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); 598948eaed1SKevin Wolf qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); 599948eaed1SKevin Wolf qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); 600d7b7e580SKevin Wolf qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); 601b95739dcSKevin Wolf qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); 602b95739dcSKevin Wolf 603bd07684aSKevin Wolf qtest_add_func("/ide/flush", test_flush); 604bd07684aSKevin Wolf 60514a92e5fSPaolo Bonzini qtest_add_func("/ide/retry/flush", test_retry_flush); 60614a92e5fSPaolo Bonzini 607acbe4801SKevin Wolf ret = g_test_run(); 608acbe4801SKevin Wolf 609acbe4801SKevin Wolf /* Cleanup */ 610acbe4801SKevin Wolf unlink(tmp_path); 61114a92e5fSPaolo Bonzini unlink(debug_path); 612acbe4801SKevin Wolf 613acbe4801SKevin Wolf return ret; 614acbe4801SKevin Wolf } 615