xref: /qemu/tests/qtest/ide-test.c (revision b95739dcf5ae1dfa8cd5b2a5be27d427835e693b)
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     LBA     = 0x40,
68 };
69 
70 enum {
71     bmreg_cmd       = 0x0,
72     bmreg_status    = 0x2,
73     bmreg_prdt      = 0x4,
74 };
75 
76 enum {
77     CMD_READ_DMA    = 0xc8,
78     CMD_WRITE_DMA   = 0xca,
79     CMD_IDENTIFY    = 0xec,
80 };
81 
82 enum {
83     BM_CMD_START    =  0x1,
84     BM_CMD_WRITE    =  0x8, /* write = from device to memory */
85 };
86 
87 enum {
88     BM_STS_ACTIVE   =  0x1,
89     BM_STS_ERROR    =  0x2,
90     BM_STS_INTR     =  0x4,
91 };
92 
93 enum {
94     PRDT_EOT        = 0x80000000,
95 };
96 
97 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
98 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
99 
100 static QPCIBus *pcibus = NULL;
101 static QGuestAllocator *guest_malloc;
102 
103 static char tmp_path[] = "/tmp/qtest.XXXXXX";
104 
105 static void ide_test_start(const char *cmdline_fmt, ...)
106 {
107     va_list ap;
108     char *cmdline;
109 
110     va_start(ap, cmdline_fmt);
111     cmdline = g_strdup_vprintf(cmdline_fmt, ap);
112     va_end(ap);
113 
114     qtest_start(cmdline);
115     qtest_irq_intercept_in(global_qtest, "ioapic");
116     guest_malloc = pc_alloc_init();
117 }
118 
119 static void ide_test_quit(void)
120 {
121     qtest_quit(global_qtest);
122 }
123 
124 static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
125 {
126     QPCIDevice *dev;
127     uint16_t vendor_id, device_id;
128 
129     if (!pcibus) {
130         pcibus = qpci_init_pc();
131     }
132 
133     /* Find PCI device and verify it's the right one */
134     dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
135     g_assert(dev != NULL);
136 
137     vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
138     device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
139     g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
140     g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
141 
142     /* Map bmdma BAR */
143     *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
144 
145     qpci_device_enable(dev);
146 
147     return dev;
148 }
149 
150 static void free_pci_device(QPCIDevice *dev)
151 {
152     /* libqos doesn't have a function for this, so free it manually */
153     g_free(dev);
154 }
155 
156 typedef struct PrdtEntry {
157     uint32_t addr;
158     uint32_t size;
159 } QEMU_PACKED PrdtEntry;
160 
161 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
162 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
163 
164 static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
165                             PrdtEntry *prdt, int prdt_entries)
166 {
167     QPCIDevice *dev;
168     uint16_t bmdma_base;
169     uintptr_t guest_prdt;
170     size_t len;
171     bool from_dev;
172     uint8_t status;
173 
174     dev = get_pci_device(&bmdma_base);
175 
176     switch (cmd) {
177     case CMD_READ_DMA:
178         from_dev = true;
179         break;
180     case CMD_WRITE_DMA:
181         from_dev = false;
182         break;
183     default:
184         g_assert_not_reached();
185     }
186 
187     /* Select device 0 */
188     outb(IDE_BASE + reg_device, 0 | LBA);
189 
190     /* Stop any running transfer, clear any pending interrupt */
191     outb(bmdma_base + bmreg_cmd, 0);
192     outb(bmdma_base + bmreg_status, BM_STS_INTR);
193 
194     /* Setup PRDT */
195     len = sizeof(*prdt) * prdt_entries;
196     guest_prdt = guest_alloc(guest_malloc, len);
197     memwrite(guest_prdt, prdt, len);
198     outl(bmdma_base + bmreg_prdt, guest_prdt);
199 
200     /* ATA DMA command */
201     outb(IDE_BASE + reg_nsectors, nb_sectors);
202 
203     outb(IDE_BASE + reg_lba_low,    sector & 0xff);
204     outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
205     outb(IDE_BASE + reg_lba_high,   (sector >> 16) & 0xff);
206 
207     outb(IDE_BASE + reg_command, cmd);
208 
209     /* Start DMA transfer */
210     outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
211 
212     /* Wait for the DMA transfer to complete */
213     do {
214         status = inb(bmdma_base + bmreg_status);
215     } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
216 
217     g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
218 
219     /* Check IDE status code */
220     assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
221     assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
222 
223     /* Reading the status register clears the IRQ */
224     g_assert(!get_irq(IDE_PRIMARY_IRQ));
225 
226     /* Stop DMA transfer if still active */
227     if (status & BM_STS_ACTIVE) {
228         outb(bmdma_base + bmreg_cmd, 0);
229     }
230 
231     free_pci_device(dev);
232 
233     return status;
234 }
235 
236 static void test_bmdma_simple_rw(void)
237 {
238     uint8_t status;
239     uint8_t *buf;
240     uint8_t *cmpbuf;
241     size_t len = 512;
242     uintptr_t guest_buf = guest_alloc(guest_malloc, len);
243 
244     PrdtEntry prdt[] = {
245         { .addr = guest_buf, .size = len | PRDT_EOT },
246     };
247 
248     buf = g_malloc(len);
249     cmpbuf = g_malloc(len);
250 
251     /* Write 0x55 pattern to sector 0 */
252     memset(buf, 0x55, len);
253     memwrite(guest_buf, buf, len);
254 
255     status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
256     g_assert_cmphex(status, ==, BM_STS_INTR);
257     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
258 
259     /* Write 0xaa pattern to sector 1 */
260     memset(buf, 0xaa, len);
261     memwrite(guest_buf, buf, len);
262 
263     status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
264     g_assert_cmphex(status, ==, BM_STS_INTR);
265     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
266 
267     /* Read and verify 0x55 pattern in sector 0 */
268     memset(cmpbuf, 0x55, len);
269 
270     status = send_dma_request(CMD_READ_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     memread(guest_buf, buf, len);
275     g_assert(memcmp(buf, cmpbuf, len) == 0);
276 
277     /* Read and verify 0xaa pattern in sector 1 */
278     memset(cmpbuf, 0xaa, len);
279 
280     status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
281     g_assert_cmphex(status, ==, BM_STS_INTR);
282     assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
283 
284     memread(guest_buf, buf, len);
285     g_assert(memcmp(buf, cmpbuf, len) == 0);
286 
287 
288     g_free(buf);
289     g_free(cmpbuf);
290 }
291 
292 static void test_bmdma_setup(void)
293 {
294     ide_test_start(
295         "-vnc none "
296         "-drive file=%s,if=ide,serial=%s,cache=writeback "
297         "-global ide-hd.ver=%s",
298         tmp_path, "testdisk", "version");
299 }
300 
301 static void test_bmdma_teardown(void)
302 {
303     ide_test_quit();
304 }
305 
306 static void test_identify(void)
307 {
308     uint8_t data;
309     uint16_t buf[256];
310     int i;
311     int ret;
312 
313     ide_test_start(
314         "-vnc none "
315         "-drive file=%s,if=ide,serial=%s,cache=writeback "
316         "-global ide-hd.ver=%s",
317         tmp_path, "testdisk", "version");
318 
319     /* IDENTIFY command on device 0*/
320     outb(IDE_BASE + reg_device, 0);
321     outb(IDE_BASE + reg_command, CMD_IDENTIFY);
322 
323     /* Read in the IDENTIFY buffer and check registers */
324     data = inb(IDE_BASE + reg_device);
325     g_assert_cmpint(data & 0x10, ==, 0);
326 
327     for (i = 0; i < 256; i++) {
328         data = inb(IDE_BASE + reg_status);
329         assert_bit_set(data, DRDY | DRQ);
330         assert_bit_clear(data, BSY | DF | ERR);
331 
332         ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
333     }
334 
335     data = inb(IDE_BASE + reg_status);
336     assert_bit_set(data, DRDY);
337     assert_bit_clear(data, BSY | DF | ERR | DRQ);
338 
339     /* Check serial number/version in the buffer */
340     ret = memcmp(&buf[10], "ettsidks            ", 20);
341     g_assert(ret == 0);
342 
343     ret = memcmp(&buf[23], "evsroi n", 8);
344     g_assert(ret == 0);
345 
346     /* Write cache enabled bit */
347     assert_bit_set(buf[85], 0x20);
348 
349     ide_test_quit();
350 }
351 
352 int main(int argc, char **argv)
353 {
354     const char *arch = qtest_get_arch();
355     int fd;
356     int ret;
357 
358     /* Check architecture */
359     if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
360         g_test_message("Skipping test for non-x86\n");
361         return 0;
362     }
363 
364     /* Create a temporary raw image */
365     fd = mkstemp(tmp_path);
366     g_assert(fd >= 0);
367     ret = ftruncate(fd, TEST_IMAGE_SIZE);
368     g_assert(ret == 0);
369     close(fd);
370 
371     /* Run the tests */
372     g_test_init(&argc, &argv, NULL);
373 
374     qtest_add_func("/ide/identify", test_identify);
375 
376     qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
377     qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
378     qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
379 
380     ret = g_test_run();
381 
382     /* Cleanup */
383     unlink(tmp_path);
384 
385     return ret;
386 }
387