xref: /qemu/tests/qtest/ide-test.c (revision e42de189e8eaf3dc93f22e88beca4f5b62ef336c)
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     CMDF_NO_BM      = 0x200,
85 };
86 
87 enum {
88     BM_CMD_START    =  0x1,
89     BM_CMD_WRITE    =  0x8, /* write = from device to memory */
90 };
91 
92 enum {
93     BM_STS_ACTIVE   =  0x1,
94     BM_STS_ERROR    =  0x2,
95     BM_STS_INTR     =  0x4,
96 };
97 
98 enum {
99     PRDT_EOT        = 0x80000000,
100 };
101 
102 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
103 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
104 
105 static QPCIBus *pcibus = NULL;
106 static QGuestAllocator *guest_malloc;
107 
108 static char tmp_path[] = "/tmp/qtest.XXXXXX";
109 static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
110 
111 static void ide_test_start(const char *cmdline_fmt, ...)
112 {
113     va_list ap;
114     char *cmdline;
115 
116     va_start(ap, cmdline_fmt);
117     cmdline = g_strdup_vprintf(cmdline_fmt, ap);
118     va_end(ap);
119 
120     qtest_start(cmdline);
121     qtest_irq_intercept_in(global_qtest, "ioapic");
122     guest_malloc = pc_alloc_init();
123 
124     g_free(cmdline);
125 }
126 
127 static void ide_test_quit(void)
128 {
129     qtest_end();
130 }
131 
132 static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
133 {
134     QPCIDevice *dev;
135     uint16_t vendor_id, device_id;
136 
137     if (!pcibus) {
138         pcibus = qpci_init_pc();
139     }
140 
141     /* Find PCI device and verify it's the right one */
142     dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
143     g_assert(dev != NULL);
144 
145     vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
146     device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
147     g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
148     g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
149 
150     /* Map bmdma BAR */
151     *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL);
152 
153     qpci_device_enable(dev);
154 
155     return dev;
156 }
157 
158 static void free_pci_device(QPCIDevice *dev)
159 {
160     /* libqos doesn't have a function for this, so free it manually */
161     g_free(dev);
162 }
163 
164 typedef struct PrdtEntry {
165     uint32_t addr;
166     uint32_t size;
167 } QEMU_PACKED PrdtEntry;
168 
169 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
170 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
171 
172 static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
173                             PrdtEntry *prdt, int prdt_entries)
174 {
175     QPCIDevice *dev;
176     uint16_t bmdma_base;
177     uintptr_t guest_prdt;
178     size_t len;
179     bool from_dev;
180     uint8_t status;
181     int flags;
182 
183     dev = get_pci_device(&bmdma_base);
184 
185     flags = cmd & ~0xff;
186     cmd &= 0xff;
187 
188     switch (cmd) {
189     case CMD_READ_DMA:
190         from_dev = true;
191         break;
192     case CMD_WRITE_DMA:
193         from_dev = false;
194         break;
195     default:
196         g_assert_not_reached();
197     }
198 
199     if (flags & CMDF_NO_BM) {
200         qpci_config_writew(dev, PCI_COMMAND,
201                            PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
202     }
203 
204     /* Select device 0 */
205     outb(IDE_BASE + reg_device, 0 | LBA);
206 
207     /* Stop any running transfer, clear any pending interrupt */
208     outb(bmdma_base + bmreg_cmd, 0);
209     outb(bmdma_base + bmreg_status, BM_STS_INTR);
210 
211     /* Setup PRDT */
212     len = sizeof(*prdt) * prdt_entries;
213     guest_prdt = guest_alloc(guest_malloc, len);
214     memwrite(guest_prdt, prdt, len);
215     outl(bmdma_base + bmreg_prdt, guest_prdt);
216 
217     /* ATA DMA command */
218     outb(IDE_BASE + reg_nsectors, nb_sectors);
219 
220     outb(IDE_BASE + reg_lba_low,    sector & 0xff);
221     outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
222     outb(IDE_BASE + reg_lba_high,   (sector >> 16) & 0xff);
223 
224     outb(IDE_BASE + reg_command, cmd);
225 
226     /* Start DMA transfer */
227     outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
228 
229     if (flags & CMDF_ABORT) {
230         outb(bmdma_base + bmreg_cmd, 0);
231     }
232 
233     /* Wait for the DMA transfer to complete */
234     do {
235         status = inb(bmdma_base + bmreg_status);
236     } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
237 
238     g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
239 
240     /* Check IDE status code */
241     assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
242     assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
243 
244     /* Reading the status register clears the IRQ */
245     g_assert(!get_irq(IDE_PRIMARY_IRQ));
246 
247     /* Stop DMA transfer if still active */
248     if (status & BM_STS_ACTIVE) {
249         outb(bmdma_base + bmreg_cmd, 0);
250     }
251 
252     free_pci_device(dev);
253 
254     return status;
255 }
256 
257 static void test_bmdma_simple_rw(void)
258 {
259     uint8_t status;
260     uint8_t *buf;
261     uint8_t *cmpbuf;
262     size_t len = 512;
263     uintptr_t guest_buf = guest_alloc(guest_malloc, len);
264 
265     PrdtEntry prdt[] = {
266         {
267             .addr = cpu_to_le32(guest_buf),
268             .size = cpu_to_le32(len | PRDT_EOT),
269         },
270     };
271 
272     buf = g_malloc(len);
273     cmpbuf = g_malloc(len);
274 
275     /* Write 0x55 pattern to sector 0 */
276     memset(buf, 0x55, len);
277     memwrite(guest_buf, buf, len);
278 
279     status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
280     g_assert_cmphex(status, ==, BM_STS_INTR);
281     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
282 
283     /* Write 0xaa pattern to sector 1 */
284     memset(buf, 0xaa, len);
285     memwrite(guest_buf, buf, len);
286 
287     status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
288     g_assert_cmphex(status, ==, BM_STS_INTR);
289     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
290 
291     /* Read and verify 0x55 pattern in sector 0 */
292     memset(cmpbuf, 0x55, len);
293 
294     status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
295     g_assert_cmphex(status, ==, BM_STS_INTR);
296     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
297 
298     memread(guest_buf, buf, len);
299     g_assert(memcmp(buf, cmpbuf, len) == 0);
300 
301     /* Read and verify 0xaa pattern in sector 1 */
302     memset(cmpbuf, 0xaa, len);
303 
304     status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
305     g_assert_cmphex(status, ==, BM_STS_INTR);
306     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
307 
308     memread(guest_buf, buf, len);
309     g_assert(memcmp(buf, cmpbuf, len) == 0);
310 
311 
312     g_free(buf);
313     g_free(cmpbuf);
314 }
315 
316 static void test_bmdma_short_prdt(void)
317 {
318     uint8_t status;
319 
320     PrdtEntry prdt[] = {
321         {
322             .addr = 0,
323             .size = cpu_to_le32(0x10 | PRDT_EOT),
324         },
325     };
326 
327     /* Normal request */
328     status = send_dma_request(CMD_READ_DMA, 0, 1,
329                               prdt, ARRAY_SIZE(prdt));
330     g_assert_cmphex(status, ==, 0);
331     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
332 
333     /* Abort the request before it completes */
334     status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
335                               prdt, ARRAY_SIZE(prdt));
336     g_assert_cmphex(status, ==, 0);
337     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
338 }
339 
340 static void test_bmdma_long_prdt(void)
341 {
342     uint8_t status;
343 
344     PrdtEntry prdt[] = {
345         {
346             .addr = 0,
347             .size = cpu_to_le32(0x1000 | PRDT_EOT),
348         },
349     };
350 
351     /* Normal request */
352     status = send_dma_request(CMD_READ_DMA, 0, 1,
353                               prdt, ARRAY_SIZE(prdt));
354     g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
355     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
356 
357     /* Abort the request before it completes */
358     status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
359                               prdt, ARRAY_SIZE(prdt));
360     g_assert_cmphex(status, ==, BM_STS_INTR);
361     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
362 }
363 
364 static void test_bmdma_no_busmaster(void)
365 {
366     uint8_t status;
367 
368     /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be
369      * able to access it anyway because the Bus Master bit in the PCI command
370      * register isn't set. This is complete nonsense, but it used to be pretty
371      * good at confusing and occasionally crashing qemu. */
372     PrdtEntry prdt[4096] = { };
373 
374     status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512,
375                               prdt, ARRAY_SIZE(prdt));
376 
377     /* Not entirely clear what the expected result is, but this is what we get
378      * in practice. At least we want to be aware of any changes. */
379     g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
380     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
381 }
382 
383 static void test_bmdma_setup(void)
384 {
385     ide_test_start(
386         "-drive file=%s,if=ide,serial=%s,cache=writeback "
387         "-global ide-hd.ver=%s",
388         tmp_path, "testdisk", "version");
389 }
390 
391 static void test_bmdma_teardown(void)
392 {
393     ide_test_quit();
394 }
395 
396 static void string_cpu_to_be16(uint16_t *s, size_t bytes)
397 {
398     g_assert((bytes & 1) == 0);
399     bytes /= 2;
400 
401     while (bytes--) {
402         *s = cpu_to_be16(*s);
403         s++;
404     }
405 }
406 
407 static void test_identify(void)
408 {
409     uint8_t data;
410     uint16_t buf[256];
411     int i;
412     int ret;
413 
414     ide_test_start(
415         "-drive file=%s,if=ide,serial=%s,cache=writeback "
416         "-global ide-hd.ver=%s",
417         tmp_path, "testdisk", "version");
418 
419     /* IDENTIFY command on device 0*/
420     outb(IDE_BASE + reg_device, 0);
421     outb(IDE_BASE + reg_command, CMD_IDENTIFY);
422 
423     /* Read in the IDENTIFY buffer and check registers */
424     data = inb(IDE_BASE + reg_device);
425     g_assert_cmpint(data & DEV, ==, 0);
426 
427     for (i = 0; i < 256; i++) {
428         data = inb(IDE_BASE + reg_status);
429         assert_bit_set(data, DRDY | DRQ);
430         assert_bit_clear(data, BSY | DF | ERR);
431 
432         ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
433     }
434 
435     data = inb(IDE_BASE + reg_status);
436     assert_bit_set(data, DRDY);
437     assert_bit_clear(data, BSY | DF | ERR | DRQ);
438 
439     /* Check serial number/version in the buffer */
440     string_cpu_to_be16(&buf[10], 20);
441     ret = memcmp(&buf[10], "testdisk            ", 20);
442     g_assert(ret == 0);
443 
444     string_cpu_to_be16(&buf[23], 8);
445     ret = memcmp(&buf[23], "version ", 8);
446     g_assert(ret == 0);
447 
448     /* Write cache enabled bit */
449     assert_bit_set(buf[85], 0x20);
450 
451     ide_test_quit();
452 }
453 
454 static void test_flush(void)
455 {
456     uint8_t data;
457 
458     ide_test_start(
459         "-drive file=blkdebug::%s,if=ide,cache=writeback",
460         tmp_path);
461 
462     /* Delay the completion of the flush request until we explicitly do it */
463     qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
464                          " 'command-line':"
465                          " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
466 
467     /* FLUSH CACHE command on device 0*/
468     outb(IDE_BASE + reg_device, 0);
469     outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
470 
471     /* Check status while request is in flight*/
472     data = inb(IDE_BASE + reg_status);
473     assert_bit_set(data, BSY | DRDY);
474     assert_bit_clear(data, DF | ERR | DRQ);
475 
476     /* Complete the command */
477     qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
478                          " 'command-line':"
479                          " 'qemu-io ide0-hd0 \"resume A\"'} }");
480 
481     /* Check registers */
482     data = inb(IDE_BASE + reg_device);
483     g_assert_cmpint(data & DEV, ==, 0);
484 
485     do {
486         data = inb(IDE_BASE + reg_status);
487     } while (data & BSY);
488 
489     assert_bit_set(data, DRDY);
490     assert_bit_clear(data, BSY | DF | ERR | DRQ);
491 
492     ide_test_quit();
493 }
494 
495 static void prepare_blkdebug_script(const char *debug_fn, const char *event)
496 {
497     FILE *debug_file = fopen(debug_fn, "w");
498     int ret;
499 
500     fprintf(debug_file, "[inject-error]\n");
501     fprintf(debug_file, "event = \"%s\"\n", event);
502     fprintf(debug_file, "errno = \"5\"\n");
503     fprintf(debug_file, "state = \"1\"\n");
504     fprintf(debug_file, "immediately = \"off\"\n");
505     fprintf(debug_file, "once = \"on\"\n");
506 
507     fprintf(debug_file, "[set-state]\n");
508     fprintf(debug_file, "event = \"%s\"\n", event);
509     fprintf(debug_file, "new_state = \"2\"\n");
510     fflush(debug_file);
511     g_assert(!ferror(debug_file));
512 
513     ret = fclose(debug_file);
514     g_assert(ret == 0);
515 }
516 
517 static void test_retry_flush(void)
518 {
519     uint8_t data;
520     const char *s;
521     QDict *response;
522 
523     prepare_blkdebug_script(debug_path, "flush_to_disk");
524 
525     ide_test_start(
526         "-vnc none "
527         "-drive file=blkdebug:%s:%s,if=ide,cache=writeback,rerror=stop,werror=stop",
528         debug_path, tmp_path);
529 
530     /* FLUSH CACHE command on device 0*/
531     outb(IDE_BASE + reg_device, 0);
532     outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
533 
534     /* Check status while request is in flight*/
535     data = inb(IDE_BASE + reg_status);
536     assert_bit_set(data, BSY | DRDY);
537     assert_bit_clear(data, DF | ERR | DRQ);
538 
539     for (;; response = NULL) {
540         response = qmp_receive();
541         if ((qdict_haskey(response, "event")) &&
542             (strcmp(qdict_get_str(response, "event"), "STOP") == 0)) {
543             QDECREF(response);
544             break;
545         }
546         QDECREF(response);
547     }
548 
549     /* Complete the command */
550     s = "{'execute':'cont' }";
551     qmp_discard_response(s);
552 
553     /* Check registers */
554     data = inb(IDE_BASE + reg_device);
555     g_assert_cmpint(data & DEV, ==, 0);
556 
557     do {
558         data = inb(IDE_BASE + reg_status);
559     } while (data & BSY);
560 
561     assert_bit_set(data, DRDY);
562     assert_bit_clear(data, BSY | DF | ERR | DRQ);
563 
564     ide_test_quit();
565 }
566 
567 int main(int argc, char **argv)
568 {
569     const char *arch = qtest_get_arch();
570     int fd;
571     int ret;
572 
573     /* Check architecture */
574     if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
575         g_test_message("Skipping test for non-x86\n");
576         return 0;
577     }
578 
579     /* Create temporary blkdebug instructions */
580     fd = mkstemp(debug_path);
581     g_assert(fd >= 0);
582     close(fd);
583 
584     /* Create a temporary raw image */
585     fd = mkstemp(tmp_path);
586     g_assert(fd >= 0);
587     ret = ftruncate(fd, TEST_IMAGE_SIZE);
588     g_assert(ret == 0);
589     close(fd);
590 
591     /* Run the tests */
592     g_test_init(&argc, &argv, NULL);
593 
594     qtest_add_func("/ide/identify", test_identify);
595 
596     qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
597     qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
598     qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
599     qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
600     qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster);
601     qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
602 
603     qtest_add_func("/ide/flush", test_flush);
604 
605     qtest_add_func("/ide/retry/flush", test_retry_flush);
606 
607     ret = g_test_run();
608 
609     /* Cleanup */
610     unlink(tmp_path);
611     unlink(debug_path);
612 
613     return ret;
614 }
615