xref: /qemu/tests/qtest/ide-test.c (revision e42de189e8eaf3dc93f22e88beca4f5b62ef336c)
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