]> git.proxmox.com Git - qemu.git/blob - tests/fdc-test.c
fdc-test: add tests for non-DMA READ command
[qemu.git] / tests / fdc-test.c
1 /*
2 * Floppy test cases.
3 *
4 * Copyright (c) 2012 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 "qemu-common.h"
33
34 #define TEST_IMAGE_SIZE 1440 * 1024
35
36 #define FLOPPY_BASE 0x3f0
37 #define FLOPPY_IRQ 6
38
39 enum {
40 reg_sra = 0x0,
41 reg_srb = 0x1,
42 reg_dor = 0x2,
43 reg_msr = 0x4,
44 reg_dsr = 0x4,
45 reg_fifo = 0x5,
46 reg_dir = 0x7,
47 };
48
49 enum {
50 CMD_SENSE_INT = 0x08,
51 CMD_SEEK = 0x0f,
52 CMD_READ = 0xe6,
53 CMD_RELATIVE_SEEK_OUT = 0x8f,
54 CMD_RELATIVE_SEEK_IN = 0xcf,
55 };
56
57 enum {
58 BUSY = 0x10,
59 NONDMA = 0x20,
60 RQM = 0x80,
61 DIO = 0x40,
62
63 DSKCHG = 0x80,
64 };
65
66 char test_image[] = "/tmp/qtest.XXXXXX";
67
68 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
69 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
70
71 static uint8_t base = 0x70;
72
73 enum {
74 CMOS_FLOPPY = 0x10,
75 };
76
77 static void floppy_send(uint8_t byte)
78 {
79 uint8_t msr;
80
81 msr = inb(FLOPPY_BASE + reg_msr);
82 assert_bit_set(msr, RQM);
83 assert_bit_clear(msr, DIO);
84
85 outb(FLOPPY_BASE + reg_fifo, byte);
86 }
87
88 static uint8_t floppy_recv(void)
89 {
90 uint8_t msr;
91
92 msr = inb(FLOPPY_BASE + reg_msr);
93 assert_bit_set(msr, RQM | DIO);
94
95 return inb(FLOPPY_BASE + reg_fifo);
96 }
97
98 /* pcn: Present Cylinder Number */
99 static void ack_irq(uint8_t *pcn)
100 {
101 uint8_t ret;
102
103 g_assert(get_irq(FLOPPY_IRQ));
104 floppy_send(CMD_SENSE_INT);
105 floppy_recv();
106
107 ret = floppy_recv();
108 if (pcn != NULL) {
109 *pcn = ret;
110 }
111
112 g_assert(!get_irq(FLOPPY_IRQ));
113 }
114
115 static uint8_t send_read_command(void)
116 {
117 uint8_t drive = 0;
118 uint8_t head = 0;
119 uint8_t cyl = 0;
120 uint8_t sect_addr = 1;
121 uint8_t sect_size = 2;
122 uint8_t eot = 1;
123 uint8_t gap = 0x1b;
124 uint8_t gpl = 0xff;
125
126 uint8_t msr = 0;
127 uint8_t st0;
128
129 uint8_t ret = 0;
130
131 floppy_send(CMD_READ);
132 floppy_send(head << 2 | drive);
133 g_assert(!get_irq(FLOPPY_IRQ));
134 floppy_send(cyl);
135 floppy_send(head);
136 floppy_send(sect_addr);
137 floppy_send(sect_size);
138 floppy_send(eot);
139 floppy_send(gap);
140 floppy_send(gpl);
141
142 uint8_t i = 0;
143 uint8_t n = 2;
144 for (; i < n; i++) {
145 msr = inb(FLOPPY_BASE + reg_msr);
146 if (msr == 0xd0) {
147 break;
148 }
149 sleep(1);
150 }
151
152 if (i >= n) {
153 return 1;
154 }
155
156 st0 = floppy_recv();
157 if (st0 != 0x60) {
158 ret = 1;
159 }
160
161 floppy_recv();
162 floppy_recv();
163 floppy_recv();
164 floppy_recv();
165 floppy_recv();
166 floppy_recv();
167
168 return ret;
169 }
170
171 static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
172 {
173 uint8_t drive = 0;
174 uint8_t head = 0;
175 uint8_t cyl = 0;
176 uint8_t sect_addr = 1;
177 uint8_t sect_size = 2;
178 uint8_t eot = nb_sect;
179 uint8_t gap = 0x1b;
180 uint8_t gpl = 0xff;
181
182 uint8_t msr = 0;
183 uint8_t st0;
184
185 uint8_t ret = 0;
186
187 floppy_send(CMD_READ);
188 floppy_send(head << 2 | drive);
189 g_assert(!get_irq(FLOPPY_IRQ));
190 floppy_send(cyl);
191 floppy_send(head);
192 floppy_send(sect_addr);
193 floppy_send(sect_size);
194 floppy_send(eot);
195 floppy_send(gap);
196 floppy_send(gpl);
197
198 uint16_t i = 0;
199 uint8_t n = 2;
200 for (; i < n; i++) {
201 msr = inb(FLOPPY_BASE + reg_msr);
202 if (msr == (BUSY | NONDMA | DIO | RQM)) {
203 break;
204 }
205 sleep(1);
206 }
207
208 if (i >= n) {
209 return 1;
210 }
211
212 /* Non-DMA mode */
213 for (i = 0; i < 512 * 2 * nb_sect; i++) {
214 msr = inb(FLOPPY_BASE + reg_msr);
215 assert_bit_set(msr, BUSY | RQM | DIO);
216 inb(FLOPPY_BASE + reg_fifo);
217 }
218
219 st0 = floppy_recv();
220 if (st0 != expected_st0) {
221 ret = 1;
222 }
223
224 floppy_recv();
225 floppy_recv();
226 floppy_recv();
227 floppy_recv();
228 floppy_recv();
229 floppy_recv();
230
231 return ret;
232 }
233
234 static void send_seek(int cyl)
235 {
236 int drive = 0;
237 int head = 0;
238
239 floppy_send(CMD_SEEK);
240 floppy_send(head << 2 | drive);
241 g_assert(!get_irq(FLOPPY_IRQ));
242 floppy_send(cyl);
243 ack_irq(NULL);
244 }
245
246 static uint8_t cmos_read(uint8_t reg)
247 {
248 outb(base + 0, reg);
249 return inb(base + 1);
250 }
251
252 static void test_cmos(void)
253 {
254 uint8_t cmos;
255
256 cmos = cmos_read(CMOS_FLOPPY);
257 g_assert(cmos == 0x40);
258 }
259
260 static void test_no_media_on_start(void)
261 {
262 uint8_t dir;
263
264 /* Media changed bit must be set all time after start if there is
265 * no media in drive. */
266 dir = inb(FLOPPY_BASE + reg_dir);
267 assert_bit_set(dir, DSKCHG);
268 dir = inb(FLOPPY_BASE + reg_dir);
269 assert_bit_set(dir, DSKCHG);
270 send_seek(1);
271 dir = inb(FLOPPY_BASE + reg_dir);
272 assert_bit_set(dir, DSKCHG);
273 dir = inb(FLOPPY_BASE + reg_dir);
274 assert_bit_set(dir, DSKCHG);
275 }
276
277 static void test_read_without_media(void)
278 {
279 uint8_t ret;
280
281 ret = send_read_command();
282 g_assert(ret == 0);
283 }
284
285 static void test_media_insert(void)
286 {
287 uint8_t dir;
288
289 /* Insert media in drive. DSKCHK should not be reset until a step pulse
290 * is sent. */
291 qmp("{'execute':'change', 'arguments':{ 'device':'floppy0', "
292 "'target': '%s' }}", test_image);
293 qmp(""); /* ignore event (FIXME open -> open transition?!) */
294 qmp(""); /* ignore event */
295
296 dir = inb(FLOPPY_BASE + reg_dir);
297 assert_bit_set(dir, DSKCHG);
298 dir = inb(FLOPPY_BASE + reg_dir);
299 assert_bit_set(dir, DSKCHG);
300
301 send_seek(0);
302 dir = inb(FLOPPY_BASE + reg_dir);
303 assert_bit_set(dir, DSKCHG);
304 dir = inb(FLOPPY_BASE + reg_dir);
305 assert_bit_set(dir, DSKCHG);
306
307 /* Step to next track should clear DSKCHG bit. */
308 send_seek(1);
309 dir = inb(FLOPPY_BASE + reg_dir);
310 assert_bit_clear(dir, DSKCHG);
311 dir = inb(FLOPPY_BASE + reg_dir);
312 assert_bit_clear(dir, DSKCHG);
313 }
314
315 static void test_media_change(void)
316 {
317 uint8_t dir;
318
319 test_media_insert();
320
321 /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
322 * reset the bit. */
323 qmp("{'execute':'eject', 'arguments':{ 'device':'floppy0' }}");
324 qmp(""); /* ignore event */
325
326 dir = inb(FLOPPY_BASE + reg_dir);
327 assert_bit_set(dir, DSKCHG);
328 dir = inb(FLOPPY_BASE + reg_dir);
329 assert_bit_set(dir, DSKCHG);
330
331 send_seek(0);
332 dir = inb(FLOPPY_BASE + reg_dir);
333 assert_bit_set(dir, DSKCHG);
334 dir = inb(FLOPPY_BASE + reg_dir);
335 assert_bit_set(dir, DSKCHG);
336
337 send_seek(1);
338 dir = inb(FLOPPY_BASE + reg_dir);
339 assert_bit_set(dir, DSKCHG);
340 dir = inb(FLOPPY_BASE + reg_dir);
341 assert_bit_set(dir, DSKCHG);
342 }
343
344 static void test_sense_interrupt(void)
345 {
346 int drive = 0;
347 int head = 0;
348 int cyl = 0;
349 int ret = 0;
350
351 floppy_send(CMD_SENSE_INT);
352 ret = floppy_recv();
353 g_assert(ret == 0x80);
354
355 floppy_send(CMD_SEEK);
356 floppy_send(head << 2 | drive);
357 g_assert(!get_irq(FLOPPY_IRQ));
358 floppy_send(cyl);
359
360 floppy_send(CMD_SENSE_INT);
361 ret = floppy_recv();
362 g_assert(ret == 0x20);
363 floppy_recv();
364 }
365
366 static void test_relative_seek(void)
367 {
368 uint8_t drive = 0;
369 uint8_t head = 0;
370 uint8_t cyl = 1;
371 uint8_t pcn;
372
373 /* Send seek to track 0 */
374 send_seek(0);
375
376 /* Send relative seek to increase track by 1 */
377 floppy_send(CMD_RELATIVE_SEEK_IN);
378 floppy_send(head << 2 | drive);
379 g_assert(!get_irq(FLOPPY_IRQ));
380 floppy_send(cyl);
381
382 ack_irq(&pcn);
383 g_assert(pcn == 1);
384
385 /* Send relative seek to decrease track by 1 */
386 floppy_send(CMD_RELATIVE_SEEK_OUT);
387 floppy_send(head << 2 | drive);
388 g_assert(!get_irq(FLOPPY_IRQ));
389 floppy_send(cyl);
390
391 ack_irq(&pcn);
392 g_assert(pcn == 0);
393 }
394
395 static void test_read_no_dma_1(void)
396 {
397 uint8_t ret;
398
399 outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
400 send_seek(0);
401 ret = send_read_no_dma_command(1, 0x24); /* FIXME: should be 0x04 */
402 g_assert(ret == 0);
403 }
404
405 static void test_read_no_dma_18(void)
406 {
407 uint8_t ret;
408
409 outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
410 send_seek(0);
411 ret = send_read_no_dma_command(18, 0x24); /* FIXME: should be 0x04 */
412 g_assert(ret == 0);
413 }
414
415 static void test_read_no_dma_19(void)
416 {
417 uint8_t ret;
418
419 outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
420 send_seek(0);
421 ret = send_read_no_dma_command(19, 0x20);
422 g_assert(ret == 0);
423 }
424
425 /* success if no crash or abort */
426 static void fuzz_registers(void)
427 {
428 unsigned int i;
429
430 for (i = 0; i < 1000; i++) {
431 uint8_t reg, val;
432
433 reg = (uint8_t)g_test_rand_int_range(0, 8);
434 val = (uint8_t)g_test_rand_int_range(0, 256);
435
436 outb(FLOPPY_BASE + reg, val);
437 inb(FLOPPY_BASE + reg);
438 }
439 }
440
441 int main(int argc, char **argv)
442 {
443 const char *arch = qtest_get_arch();
444 char *cmdline;
445 int fd;
446 int ret;
447
448 /* Check architecture */
449 if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
450 g_test_message("Skipping test for non-x86\n");
451 return 0;
452 }
453
454 /* Create a temporary raw image */
455 fd = mkstemp(test_image);
456 g_assert(fd >= 0);
457 ret = ftruncate(fd, TEST_IMAGE_SIZE);
458 g_assert(ret == 0);
459 close(fd);
460
461 /* Run the tests */
462 g_test_init(&argc, &argv, NULL);
463
464 cmdline = g_strdup_printf("-vnc none ");
465
466 qtest_start(cmdline);
467 qtest_irq_intercept_in(global_qtest, "ioapic");
468 qtest_add_func("/fdc/cmos", test_cmos);
469 qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
470 qtest_add_func("/fdc/read_without_media", test_read_without_media);
471 qtest_add_func("/fdc/media_change", test_media_change);
472 qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
473 qtest_add_func("/fdc/relative_seek", test_relative_seek);
474 qtest_add_func("/fdc/media_insert", test_media_insert);
475 qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
476 qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
477 qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
478 qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
479
480 ret = g_test_run();
481
482 /* Cleanup */
483 qtest_quit(global_qtest);
484 unlink(test_image);
485
486 return ret;
487 }