11e8a1faeSThomas Huth /*
21e8a1faeSThomas Huth * Floppy test cases.
31e8a1faeSThomas Huth *
41e8a1faeSThomas Huth * Copyright (c) 2012 Kevin Wolf <kwolf@redhat.com>
51e8a1faeSThomas Huth *
61e8a1faeSThomas Huth * Permission is hereby granted, free of charge, to any person obtaining a copy
71e8a1faeSThomas Huth * of this software and associated documentation files (the "Software"), to deal
81e8a1faeSThomas Huth * in the Software without restriction, including without limitation the rights
91e8a1faeSThomas Huth * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
101e8a1faeSThomas Huth * copies of the Software, and to permit persons to whom the Software is
111e8a1faeSThomas Huth * furnished to do so, subject to the following conditions:
121e8a1faeSThomas Huth *
131e8a1faeSThomas Huth * The above copyright notice and this permission notice shall be included in
141e8a1faeSThomas Huth * all copies or substantial portions of the Software.
151e8a1faeSThomas Huth *
161e8a1faeSThomas Huth * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171e8a1faeSThomas Huth * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181e8a1faeSThomas Huth * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
191e8a1faeSThomas Huth * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201e8a1faeSThomas Huth * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211e8a1faeSThomas Huth * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
221e8a1faeSThomas Huth * THE SOFTWARE.
231e8a1faeSThomas Huth */
241e8a1faeSThomas Huth
251e8a1faeSThomas Huth #include "qemu/osdep.h"
261e8a1faeSThomas Huth
271e8a1faeSThomas Huth
281e8a1faeSThomas Huth #include "libqtest-single.h"
291e8a1faeSThomas Huth #include "qapi/qmp/qdict.h"
301e8a1faeSThomas Huth
31cc20926eSPhilippe Mathieu-Daudé #define DRIVE_FLOPPY_BLANK \
32cc20926eSPhilippe Mathieu-Daudé "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k"
33cc20926eSPhilippe Mathieu-Daudé
341e8a1faeSThomas Huth #define TEST_IMAGE_SIZE 1440 * 1024
351e8a1faeSThomas Huth
361e8a1faeSThomas Huth #define FLOPPY_BASE 0x3f0
371e8a1faeSThomas Huth #define FLOPPY_IRQ 6
381e8a1faeSThomas Huth
391e8a1faeSThomas Huth enum {
401e8a1faeSThomas Huth reg_sra = 0x0,
411e8a1faeSThomas Huth reg_srb = 0x1,
421e8a1faeSThomas Huth reg_dor = 0x2,
431e8a1faeSThomas Huth reg_msr = 0x4,
441e8a1faeSThomas Huth reg_dsr = 0x4,
451e8a1faeSThomas Huth reg_fifo = 0x5,
461e8a1faeSThomas Huth reg_dir = 0x7,
471e8a1faeSThomas Huth };
481e8a1faeSThomas Huth
491e8a1faeSThomas Huth enum {
501e8a1faeSThomas Huth CMD_SENSE_INT = 0x08,
511e8a1faeSThomas Huth CMD_READ_ID = 0x0a,
521e8a1faeSThomas Huth CMD_SEEK = 0x0f,
531e8a1faeSThomas Huth CMD_VERIFY = 0x16,
541e8a1faeSThomas Huth CMD_READ = 0xe6,
551e8a1faeSThomas Huth CMD_RELATIVE_SEEK_OUT = 0x8f,
561e8a1faeSThomas Huth CMD_RELATIVE_SEEK_IN = 0xcf,
571e8a1faeSThomas Huth };
581e8a1faeSThomas Huth
591e8a1faeSThomas Huth enum {
601e8a1faeSThomas Huth BUSY = 0x10,
611e8a1faeSThomas Huth NONDMA = 0x20,
621e8a1faeSThomas Huth RQM = 0x80,
631e8a1faeSThomas Huth DIO = 0x40,
641e8a1faeSThomas Huth
651e8a1faeSThomas Huth DSKCHG = 0x80,
661e8a1faeSThomas Huth };
671e8a1faeSThomas Huth
68394bcc5bSBin Meng static char *test_image;
691e8a1faeSThomas Huth
701e8a1faeSThomas Huth #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
711e8a1faeSThomas Huth #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
721e8a1faeSThomas Huth
731e8a1faeSThomas Huth static uint8_t base = 0x70;
741e8a1faeSThomas Huth
751e8a1faeSThomas Huth enum {
761e8a1faeSThomas Huth CMOS_FLOPPY = 0x10,
771e8a1faeSThomas Huth };
781e8a1faeSThomas Huth
floppy_send(uint8_t byte)791e8a1faeSThomas Huth static void floppy_send(uint8_t byte)
801e8a1faeSThomas Huth {
811e8a1faeSThomas Huth uint8_t msr;
821e8a1faeSThomas Huth
831e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
841e8a1faeSThomas Huth assert_bit_set(msr, RQM);
851e8a1faeSThomas Huth assert_bit_clear(msr, DIO);
861e8a1faeSThomas Huth
871e8a1faeSThomas Huth outb(FLOPPY_BASE + reg_fifo, byte);
881e8a1faeSThomas Huth }
891e8a1faeSThomas Huth
floppy_recv(void)901e8a1faeSThomas Huth static uint8_t floppy_recv(void)
911e8a1faeSThomas Huth {
921e8a1faeSThomas Huth uint8_t msr;
931e8a1faeSThomas Huth
941e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
951e8a1faeSThomas Huth assert_bit_set(msr, RQM | DIO);
961e8a1faeSThomas Huth
971e8a1faeSThomas Huth return inb(FLOPPY_BASE + reg_fifo);
981e8a1faeSThomas Huth }
991e8a1faeSThomas Huth
1001e8a1faeSThomas Huth /* pcn: Present Cylinder Number */
ack_irq(uint8_t * pcn)1011e8a1faeSThomas Huth static void ack_irq(uint8_t *pcn)
1021e8a1faeSThomas Huth {
1031e8a1faeSThomas Huth uint8_t ret;
1041e8a1faeSThomas Huth
1051e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
1061e8a1faeSThomas Huth floppy_send(CMD_SENSE_INT);
1071e8a1faeSThomas Huth floppy_recv();
1081e8a1faeSThomas Huth
1091e8a1faeSThomas Huth ret = floppy_recv();
1101e8a1faeSThomas Huth if (pcn != NULL) {
1111e8a1faeSThomas Huth *pcn = ret;
1121e8a1faeSThomas Huth }
1131e8a1faeSThomas Huth
1141e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
1151e8a1faeSThomas Huth }
1161e8a1faeSThomas Huth
send_read_command(uint8_t cmd)1171e8a1faeSThomas Huth static uint8_t send_read_command(uint8_t cmd)
1181e8a1faeSThomas Huth {
1191e8a1faeSThomas Huth uint8_t drive = 0;
1201e8a1faeSThomas Huth uint8_t head = 0;
1211e8a1faeSThomas Huth uint8_t cyl = 0;
1221e8a1faeSThomas Huth uint8_t sect_addr = 1;
1231e8a1faeSThomas Huth uint8_t sect_size = 2;
1241e8a1faeSThomas Huth uint8_t eot = 1;
1251e8a1faeSThomas Huth uint8_t gap = 0x1b;
1261e8a1faeSThomas Huth uint8_t gpl = 0xff;
1271e8a1faeSThomas Huth
1281e8a1faeSThomas Huth uint8_t msr = 0;
1291e8a1faeSThomas Huth uint8_t st0;
1301e8a1faeSThomas Huth
1311e8a1faeSThomas Huth uint8_t ret = 0;
1321e8a1faeSThomas Huth
1331e8a1faeSThomas Huth floppy_send(cmd);
1341e8a1faeSThomas Huth floppy_send(head << 2 | drive);
1351e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
1361e8a1faeSThomas Huth floppy_send(cyl);
1371e8a1faeSThomas Huth floppy_send(head);
1381e8a1faeSThomas Huth floppy_send(sect_addr);
1391e8a1faeSThomas Huth floppy_send(sect_size);
1401e8a1faeSThomas Huth floppy_send(eot);
1411e8a1faeSThomas Huth floppy_send(gap);
1421e8a1faeSThomas Huth floppy_send(gpl);
1431e8a1faeSThomas Huth
1441e8a1faeSThomas Huth uint8_t i = 0;
1451e8a1faeSThomas Huth uint8_t n = 2;
1461e8a1faeSThomas Huth for (; i < n; i++) {
1471e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
1481e8a1faeSThomas Huth if (msr == 0xd0) {
1491e8a1faeSThomas Huth break;
1501e8a1faeSThomas Huth }
1511e8a1faeSThomas Huth sleep(1);
1521e8a1faeSThomas Huth }
1531e8a1faeSThomas Huth
1541e8a1faeSThomas Huth if (i >= n) {
1551e8a1faeSThomas Huth return 1;
1561e8a1faeSThomas Huth }
1571e8a1faeSThomas Huth
1581e8a1faeSThomas Huth st0 = floppy_recv();
1591e8a1faeSThomas Huth if (st0 != 0x40) {
1601e8a1faeSThomas Huth ret = 1;
1611e8a1faeSThomas Huth }
1621e8a1faeSThomas Huth
1631e8a1faeSThomas Huth floppy_recv();
1641e8a1faeSThomas Huth floppy_recv();
1651e8a1faeSThomas Huth floppy_recv();
1661e8a1faeSThomas Huth floppy_recv();
1671e8a1faeSThomas Huth floppy_recv();
1681e8a1faeSThomas Huth floppy_recv();
1691e8a1faeSThomas Huth
1701e8a1faeSThomas Huth return ret;
1711e8a1faeSThomas Huth }
1721e8a1faeSThomas Huth
send_read_no_dma_command(int nb_sect,uint8_t expected_st0)1731e8a1faeSThomas Huth static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
1741e8a1faeSThomas Huth {
1751e8a1faeSThomas Huth uint8_t drive = 0;
1761e8a1faeSThomas Huth uint8_t head = 0;
1771e8a1faeSThomas Huth uint8_t cyl = 0;
1781e8a1faeSThomas Huth uint8_t sect_addr = 1;
1791e8a1faeSThomas Huth uint8_t sect_size = 2;
1801e8a1faeSThomas Huth uint8_t eot = nb_sect;
1811e8a1faeSThomas Huth uint8_t gap = 0x1b;
1821e8a1faeSThomas Huth uint8_t gpl = 0xff;
1831e8a1faeSThomas Huth
1841e8a1faeSThomas Huth uint8_t msr = 0;
1851e8a1faeSThomas Huth uint8_t st0;
1861e8a1faeSThomas Huth
1871e8a1faeSThomas Huth uint8_t ret = 0;
1881e8a1faeSThomas Huth
1891e8a1faeSThomas Huth floppy_send(CMD_READ);
1901e8a1faeSThomas Huth floppy_send(head << 2 | drive);
1911e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
1921e8a1faeSThomas Huth floppy_send(cyl);
1931e8a1faeSThomas Huth floppy_send(head);
1941e8a1faeSThomas Huth floppy_send(sect_addr);
1951e8a1faeSThomas Huth floppy_send(sect_size);
1961e8a1faeSThomas Huth floppy_send(eot);
1971e8a1faeSThomas Huth floppy_send(gap);
1981e8a1faeSThomas Huth floppy_send(gpl);
1991e8a1faeSThomas Huth
2001e8a1faeSThomas Huth uint16_t i = 0;
2011e8a1faeSThomas Huth uint8_t n = 2;
2021e8a1faeSThomas Huth for (; i < n; i++) {
2031e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
2041e8a1faeSThomas Huth if (msr == (BUSY | NONDMA | DIO | RQM)) {
2051e8a1faeSThomas Huth break;
2061e8a1faeSThomas Huth }
2071e8a1faeSThomas Huth sleep(1);
2081e8a1faeSThomas Huth }
2091e8a1faeSThomas Huth
2101e8a1faeSThomas Huth if (i >= n) {
2111e8a1faeSThomas Huth return 1;
2121e8a1faeSThomas Huth }
2131e8a1faeSThomas Huth
2141e8a1faeSThomas Huth /* Non-DMA mode */
2151e8a1faeSThomas Huth for (i = 0; i < 512 * 2 * nb_sect; i++) {
2161e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
2171e8a1faeSThomas Huth assert_bit_set(msr, BUSY | RQM | DIO);
2181e8a1faeSThomas Huth inb(FLOPPY_BASE + reg_fifo);
2191e8a1faeSThomas Huth }
2201e8a1faeSThomas Huth
2211e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
2221e8a1faeSThomas Huth assert_bit_set(msr, BUSY | RQM | DIO);
2231e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
2241e8a1faeSThomas Huth
2251e8a1faeSThomas Huth st0 = floppy_recv();
2261e8a1faeSThomas Huth if (st0 != expected_st0) {
2271e8a1faeSThomas Huth ret = 1;
2281e8a1faeSThomas Huth }
2291e8a1faeSThomas Huth
2301e8a1faeSThomas Huth floppy_recv();
2311e8a1faeSThomas Huth floppy_recv();
2321e8a1faeSThomas Huth floppy_recv();
2331e8a1faeSThomas Huth floppy_recv();
2341e8a1faeSThomas Huth floppy_recv();
2351e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
2361e8a1faeSThomas Huth floppy_recv();
2371e8a1faeSThomas Huth
2381e8a1faeSThomas Huth /* Check that we're back in command phase */
2391e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
2401e8a1faeSThomas Huth assert_bit_clear(msr, BUSY | DIO);
2411e8a1faeSThomas Huth assert_bit_set(msr, RQM);
2421e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
2431e8a1faeSThomas Huth
2441e8a1faeSThomas Huth return ret;
2451e8a1faeSThomas Huth }
2461e8a1faeSThomas Huth
send_seek(int cyl)2471e8a1faeSThomas Huth static void send_seek(int cyl)
2481e8a1faeSThomas Huth {
2491e8a1faeSThomas Huth int drive = 0;
2501e8a1faeSThomas Huth int head = 0;
2511e8a1faeSThomas Huth
2521e8a1faeSThomas Huth floppy_send(CMD_SEEK);
2531e8a1faeSThomas Huth floppy_send(head << 2 | drive);
2541e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
2551e8a1faeSThomas Huth floppy_send(cyl);
2561e8a1faeSThomas Huth ack_irq(NULL);
2571e8a1faeSThomas Huth }
2581e8a1faeSThomas Huth
cmos_read(uint8_t reg)2591e8a1faeSThomas Huth static uint8_t cmos_read(uint8_t reg)
2601e8a1faeSThomas Huth {
2611e8a1faeSThomas Huth outb(base + 0, reg);
2621e8a1faeSThomas Huth return inb(base + 1);
2631e8a1faeSThomas Huth }
2641e8a1faeSThomas Huth
test_cmos(void)2651e8a1faeSThomas Huth static void test_cmos(void)
2661e8a1faeSThomas Huth {
2671e8a1faeSThomas Huth uint8_t cmos;
2681e8a1faeSThomas Huth
2691e8a1faeSThomas Huth cmos = cmos_read(CMOS_FLOPPY);
2701e8a1faeSThomas Huth g_assert(cmos == 0x40 || cmos == 0x50);
2711e8a1faeSThomas Huth }
2721e8a1faeSThomas Huth
test_no_media_on_start(void)2731e8a1faeSThomas Huth static void test_no_media_on_start(void)
2741e8a1faeSThomas Huth {
2751e8a1faeSThomas Huth uint8_t dir;
2761e8a1faeSThomas Huth
2771e8a1faeSThomas Huth /* Media changed bit must be set all time after start if there is
2781e8a1faeSThomas Huth * no media in drive. */
2791e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
2801e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
2811e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
2821e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
2831e8a1faeSThomas Huth send_seek(1);
2841e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
2851e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
2861e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
2871e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
2881e8a1faeSThomas Huth }
2891e8a1faeSThomas Huth
test_read_without_media(void)2901e8a1faeSThomas Huth static void test_read_without_media(void)
2911e8a1faeSThomas Huth {
2921e8a1faeSThomas Huth uint8_t ret;
2931e8a1faeSThomas Huth
2941e8a1faeSThomas Huth ret = send_read_command(CMD_READ);
2951e8a1faeSThomas Huth g_assert(ret == 0);
2961e8a1faeSThomas Huth }
2971e8a1faeSThomas Huth
test_media_insert(void)2981e8a1faeSThomas Huth static void test_media_insert(void)
2991e8a1faeSThomas Huth {
3001e8a1faeSThomas Huth uint8_t dir;
3011e8a1faeSThomas Huth
3021e8a1faeSThomas Huth /* Insert media in drive. DSKCHK should not be reset until a step pulse
3031e8a1faeSThomas Huth * is sent. */
304855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest,
305855436dbSDaniel P. Berrangé "{'execute':'blockdev-change-medium', 'arguments':{"
3061e8a1faeSThomas Huth " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}",
3071e8a1faeSThomas Huth test_image);
3081e8a1faeSThomas Huth
3091e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3101e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3111e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3121e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3131e8a1faeSThomas Huth
3141e8a1faeSThomas Huth send_seek(0);
3151e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3161e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3171e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3181e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3191e8a1faeSThomas Huth
3201e8a1faeSThomas Huth /* Step to next track should clear DSKCHG bit. */
3211e8a1faeSThomas Huth send_seek(1);
3221e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3231e8a1faeSThomas Huth assert_bit_clear(dir, DSKCHG);
3241e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3251e8a1faeSThomas Huth assert_bit_clear(dir, DSKCHG);
3261e8a1faeSThomas Huth }
3271e8a1faeSThomas Huth
test_media_change(void)3281e8a1faeSThomas Huth static void test_media_change(void)
3291e8a1faeSThomas Huth {
3301e8a1faeSThomas Huth uint8_t dir;
3311e8a1faeSThomas Huth
3321e8a1faeSThomas Huth test_media_insert();
3331e8a1faeSThomas Huth
3341e8a1faeSThomas Huth /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
3351e8a1faeSThomas Huth * reset the bit. */
336855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest,
337855436dbSDaniel P. Berrangé "{'execute':'eject', 'arguments':{"
3381e8a1faeSThomas Huth " 'id':'floppy0' }}");
3391e8a1faeSThomas Huth
3401e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3411e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3421e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3431e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3441e8a1faeSThomas Huth
3451e8a1faeSThomas Huth send_seek(0);
3461e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3471e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3481e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3491e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3501e8a1faeSThomas Huth
3511e8a1faeSThomas Huth send_seek(1);
3521e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3531e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3541e8a1faeSThomas Huth dir = inb(FLOPPY_BASE + reg_dir);
3551e8a1faeSThomas Huth assert_bit_set(dir, DSKCHG);
3561e8a1faeSThomas Huth }
3571e8a1faeSThomas Huth
test_sense_interrupt(void)3581e8a1faeSThomas Huth static void test_sense_interrupt(void)
3591e8a1faeSThomas Huth {
3601e8a1faeSThomas Huth int drive = 0;
3611e8a1faeSThomas Huth int head = 0;
3621e8a1faeSThomas Huth int cyl = 0;
3631e8a1faeSThomas Huth int ret = 0;
3641e8a1faeSThomas Huth
3651e8a1faeSThomas Huth floppy_send(CMD_SENSE_INT);
3661e8a1faeSThomas Huth ret = floppy_recv();
3671e8a1faeSThomas Huth g_assert(ret == 0x80);
3681e8a1faeSThomas Huth
3691e8a1faeSThomas Huth floppy_send(CMD_SEEK);
3701e8a1faeSThomas Huth floppy_send(head << 2 | drive);
3711e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
3721e8a1faeSThomas Huth floppy_send(cyl);
3731e8a1faeSThomas Huth
3741e8a1faeSThomas Huth floppy_send(CMD_SENSE_INT);
3751e8a1faeSThomas Huth ret = floppy_recv();
3761e8a1faeSThomas Huth g_assert(ret == 0x20);
3771e8a1faeSThomas Huth floppy_recv();
3781e8a1faeSThomas Huth }
3791e8a1faeSThomas Huth
test_relative_seek(void)3801e8a1faeSThomas Huth static void test_relative_seek(void)
3811e8a1faeSThomas Huth {
3821e8a1faeSThomas Huth uint8_t drive = 0;
3831e8a1faeSThomas Huth uint8_t head = 0;
3841e8a1faeSThomas Huth uint8_t cyl = 1;
3851e8a1faeSThomas Huth uint8_t pcn;
3861e8a1faeSThomas Huth
3871e8a1faeSThomas Huth /* Send seek to track 0 */
3881e8a1faeSThomas Huth send_seek(0);
3891e8a1faeSThomas Huth
3901e8a1faeSThomas Huth /* Send relative seek to increase track by 1 */
3911e8a1faeSThomas Huth floppy_send(CMD_RELATIVE_SEEK_IN);
3921e8a1faeSThomas Huth floppy_send(head << 2 | drive);
3931e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
3941e8a1faeSThomas Huth floppy_send(cyl);
3951e8a1faeSThomas Huth
3961e8a1faeSThomas Huth ack_irq(&pcn);
3971e8a1faeSThomas Huth g_assert(pcn == 1);
3981e8a1faeSThomas Huth
3991e8a1faeSThomas Huth /* Send relative seek to decrease track by 1 */
4001e8a1faeSThomas Huth floppy_send(CMD_RELATIVE_SEEK_OUT);
4011e8a1faeSThomas Huth floppy_send(head << 2 | drive);
4021e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4031e8a1faeSThomas Huth floppy_send(cyl);
4041e8a1faeSThomas Huth
4051e8a1faeSThomas Huth ack_irq(&pcn);
4061e8a1faeSThomas Huth g_assert(pcn == 0);
4071e8a1faeSThomas Huth }
4081e8a1faeSThomas Huth
test_read_id(void)4091e8a1faeSThomas Huth static void test_read_id(void)
4101e8a1faeSThomas Huth {
4111e8a1faeSThomas Huth uint8_t drive = 0;
4121e8a1faeSThomas Huth uint8_t head = 0;
4131e8a1faeSThomas Huth uint8_t cyl;
4141e8a1faeSThomas Huth uint8_t st0;
4151e8a1faeSThomas Huth uint8_t msr;
4161e8a1faeSThomas Huth
4171e8a1faeSThomas Huth /* Seek to track 0 and check with READ ID */
4181e8a1faeSThomas Huth send_seek(0);
4191e8a1faeSThomas Huth
4201e8a1faeSThomas Huth floppy_send(CMD_READ_ID);
4211e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4221e8a1faeSThomas Huth floppy_send(head << 2 | drive);
4231e8a1faeSThomas Huth
4241e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
4251e8a1faeSThomas Huth if (!get_irq(FLOPPY_IRQ)) {
4261e8a1faeSThomas Huth assert_bit_set(msr, BUSY);
4271e8a1faeSThomas Huth assert_bit_clear(msr, RQM);
4281e8a1faeSThomas Huth }
4291e8a1faeSThomas Huth
4301e8a1faeSThomas Huth while (!get_irq(FLOPPY_IRQ)) {
4311e8a1faeSThomas Huth /* qemu involves a timer with READ ID... */
4321e8a1faeSThomas Huth clock_step(1000000000LL / 50);
4331e8a1faeSThomas Huth }
4341e8a1faeSThomas Huth
4351e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
4361e8a1faeSThomas Huth assert_bit_set(msr, BUSY | RQM | DIO);
4371e8a1faeSThomas Huth
4381e8a1faeSThomas Huth st0 = floppy_recv();
4391e8a1faeSThomas Huth floppy_recv();
4401e8a1faeSThomas Huth floppy_recv();
4411e8a1faeSThomas Huth cyl = floppy_recv();
4421e8a1faeSThomas Huth head = floppy_recv();
4431e8a1faeSThomas Huth floppy_recv();
4441e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
4451e8a1faeSThomas Huth floppy_recv();
4461e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4471e8a1faeSThomas Huth
4481e8a1faeSThomas Huth g_assert_cmpint(cyl, ==, 0);
4491e8a1faeSThomas Huth g_assert_cmpint(head, ==, 0);
4501e8a1faeSThomas Huth g_assert_cmpint(st0, ==, head << 2);
4511e8a1faeSThomas Huth
4521e8a1faeSThomas Huth /* Seek to track 8 on head 1 and check with READ ID */
4531e8a1faeSThomas Huth head = 1;
4541e8a1faeSThomas Huth cyl = 8;
4551e8a1faeSThomas Huth
4561e8a1faeSThomas Huth floppy_send(CMD_SEEK);
4571e8a1faeSThomas Huth floppy_send(head << 2 | drive);
4581e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4591e8a1faeSThomas Huth floppy_send(cyl);
4601e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
4611e8a1faeSThomas Huth ack_irq(NULL);
4621e8a1faeSThomas Huth
4631e8a1faeSThomas Huth floppy_send(CMD_READ_ID);
4641e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4651e8a1faeSThomas Huth floppy_send(head << 2 | drive);
4661e8a1faeSThomas Huth
4671e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
4681e8a1faeSThomas Huth if (!get_irq(FLOPPY_IRQ)) {
4691e8a1faeSThomas Huth assert_bit_set(msr, BUSY);
4701e8a1faeSThomas Huth assert_bit_clear(msr, RQM);
4711e8a1faeSThomas Huth }
4721e8a1faeSThomas Huth
4731e8a1faeSThomas Huth while (!get_irq(FLOPPY_IRQ)) {
4741e8a1faeSThomas Huth /* qemu involves a timer with READ ID... */
4751e8a1faeSThomas Huth clock_step(1000000000LL / 50);
4761e8a1faeSThomas Huth }
4771e8a1faeSThomas Huth
4781e8a1faeSThomas Huth msr = inb(FLOPPY_BASE + reg_msr);
4791e8a1faeSThomas Huth assert_bit_set(msr, BUSY | RQM | DIO);
4801e8a1faeSThomas Huth
4811e8a1faeSThomas Huth st0 = floppy_recv();
4821e8a1faeSThomas Huth floppy_recv();
4831e8a1faeSThomas Huth floppy_recv();
4841e8a1faeSThomas Huth cyl = floppy_recv();
4851e8a1faeSThomas Huth head = floppy_recv();
4861e8a1faeSThomas Huth floppy_recv();
4871e8a1faeSThomas Huth g_assert(get_irq(FLOPPY_IRQ));
4881e8a1faeSThomas Huth floppy_recv();
4891e8a1faeSThomas Huth g_assert(!get_irq(FLOPPY_IRQ));
4901e8a1faeSThomas Huth
4911e8a1faeSThomas Huth g_assert_cmpint(cyl, ==, 8);
4921e8a1faeSThomas Huth g_assert_cmpint(head, ==, 1);
4931e8a1faeSThomas Huth g_assert_cmpint(st0, ==, head << 2);
4941e8a1faeSThomas Huth }
4951e8a1faeSThomas Huth
test_read_no_dma_1(void)4961e8a1faeSThomas Huth static void test_read_no_dma_1(void)
4971e8a1faeSThomas Huth {
4981e8a1faeSThomas Huth uint8_t ret;
4991e8a1faeSThomas Huth
5001e8a1faeSThomas Huth outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5011e8a1faeSThomas Huth send_seek(0);
5021e8a1faeSThomas Huth ret = send_read_no_dma_command(1, 0x04);
5031e8a1faeSThomas Huth g_assert(ret == 0);
5041e8a1faeSThomas Huth }
5051e8a1faeSThomas Huth
test_read_no_dma_18(void)5061e8a1faeSThomas Huth static void test_read_no_dma_18(void)
5071e8a1faeSThomas Huth {
5081e8a1faeSThomas Huth uint8_t ret;
5091e8a1faeSThomas Huth
5101e8a1faeSThomas Huth outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5111e8a1faeSThomas Huth send_seek(0);
5121e8a1faeSThomas Huth ret = send_read_no_dma_command(18, 0x04);
5131e8a1faeSThomas Huth g_assert(ret == 0);
5141e8a1faeSThomas Huth }
5151e8a1faeSThomas Huth
test_read_no_dma_19(void)5161e8a1faeSThomas Huth static void test_read_no_dma_19(void)
5171e8a1faeSThomas Huth {
5181e8a1faeSThomas Huth uint8_t ret;
5191e8a1faeSThomas Huth
5201e8a1faeSThomas Huth outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5211e8a1faeSThomas Huth send_seek(0);
5221e8a1faeSThomas Huth ret = send_read_no_dma_command(19, 0x20);
5231e8a1faeSThomas Huth g_assert(ret == 0);
5241e8a1faeSThomas Huth }
5251e8a1faeSThomas Huth
test_verify(void)5261e8a1faeSThomas Huth static void test_verify(void)
5271e8a1faeSThomas Huth {
5281e8a1faeSThomas Huth uint8_t ret;
5291e8a1faeSThomas Huth
5301e8a1faeSThomas Huth ret = send_read_command(CMD_VERIFY);
5311e8a1faeSThomas Huth g_assert(ret == 0);
5321e8a1faeSThomas Huth }
5331e8a1faeSThomas Huth
5341e8a1faeSThomas Huth /* success if no crash or abort */
fuzz_registers(void)5351e8a1faeSThomas Huth static void fuzz_registers(void)
5361e8a1faeSThomas Huth {
5371e8a1faeSThomas Huth unsigned int i;
5381e8a1faeSThomas Huth
5391e8a1faeSThomas Huth for (i = 0; i < 1000; i++) {
5401e8a1faeSThomas Huth uint8_t reg, val;
5411e8a1faeSThomas Huth
5421e8a1faeSThomas Huth reg = (uint8_t)g_test_rand_int_range(0, 8);
5431e8a1faeSThomas Huth val = (uint8_t)g_test_rand_int_range(0, 256);
5441e8a1faeSThomas Huth
5451e8a1faeSThomas Huth outb(FLOPPY_BASE + reg, val);
5461e8a1faeSThomas Huth inb(FLOPPY_BASE + reg);
5471e8a1faeSThomas Huth }
5481e8a1faeSThomas Huth }
5491e8a1faeSThomas Huth
qtest_check_clang_sanitizer(void)550cc20926eSPhilippe Mathieu-Daudé static bool qtest_check_clang_sanitizer(void)
551cc20926eSPhilippe Mathieu-Daudé {
552638466f7SMarc-André Lureau #ifdef QEMU_SANITIZE_ADDRESS
553cc20926eSPhilippe Mathieu-Daudé return true;
554cc20926eSPhilippe Mathieu-Daudé #else
555*cb771ac1SRichard Henderson g_test_skip("QEMU not configured using --enable-asan");
556cc20926eSPhilippe Mathieu-Daudé return false;
557cc20926eSPhilippe Mathieu-Daudé #endif
558cc20926eSPhilippe Mathieu-Daudé }
test_cve_2021_20196(void)559cc20926eSPhilippe Mathieu-Daudé static void test_cve_2021_20196(void)
560cc20926eSPhilippe Mathieu-Daudé {
561cc20926eSPhilippe Mathieu-Daudé QTestState *s;
562cc20926eSPhilippe Mathieu-Daudé
563cc20926eSPhilippe Mathieu-Daudé if (!qtest_check_clang_sanitizer()) {
564cc20926eSPhilippe Mathieu-Daudé return;
565cc20926eSPhilippe Mathieu-Daudé }
566cc20926eSPhilippe Mathieu-Daudé
567cc20926eSPhilippe Mathieu-Daudé s = qtest_initf("-nographic -m 32M -nodefaults " DRIVE_FLOPPY_BLANK);
568cc20926eSPhilippe Mathieu-Daudé
569cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0500);
570cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
571cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
572cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
573cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
574cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0400);
575cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
576cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
577cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
578cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x01);
579cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0500);
580cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
581cc20926eSPhilippe Mathieu-Daudé qtest_quit(s);
582cc20926eSPhilippe Mathieu-Daudé }
583cc20926eSPhilippe Mathieu-Daudé
test_cve_2021_3507(void)58446609b90SPhilippe Mathieu-Daudé static void test_cve_2021_3507(void)
58546609b90SPhilippe Mathieu-Daudé {
58646609b90SPhilippe Mathieu-Daudé QTestState *s;
58746609b90SPhilippe Mathieu-Daudé
58846609b90SPhilippe Mathieu-Daudé s = qtest_initf("-nographic -m 32M -nodefaults "
58946609b90SPhilippe Mathieu-Daudé "-drive file=%s,format=raw,if=floppy,snapshot=on",
59046609b90SPhilippe Mathieu-Daudé test_image);
59146609b90SPhilippe Mathieu-Daudé qtest_outl(s, 0x9, 0x0a0206);
59246609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x1600);
59346609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59446609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59546609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59646609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0200);
59746609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0200);
59846609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59946609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
60046609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
60146609b90SPhilippe Mathieu-Daudé qtest_quit(s);
60246609b90SPhilippe Mathieu-Daudé }
60346609b90SPhilippe Mathieu-Daudé
main(int argc,char ** argv)6041e8a1faeSThomas Huth int main(int argc, char **argv)
6051e8a1faeSThomas Huth {
6061e8a1faeSThomas Huth int fd;
6071e8a1faeSThomas Huth int ret;
6081e8a1faeSThomas Huth
6091e8a1faeSThomas Huth /* Create a temporary raw image */
610394bcc5bSBin Meng fd = g_file_open_tmp("qtest.XXXXXX", &test_image, NULL);
6111e8a1faeSThomas Huth g_assert(fd >= 0);
6121e8a1faeSThomas Huth ret = ftruncate(fd, TEST_IMAGE_SIZE);
6131e8a1faeSThomas Huth g_assert(ret == 0);
6141e8a1faeSThomas Huth close(fd);
6151e8a1faeSThomas Huth
6161e8a1faeSThomas Huth /* Run the tests */
6171e8a1faeSThomas Huth g_test_init(&argc, &argv, NULL);
6181e8a1faeSThomas Huth
619fedcc379SDr. David Alan Gilbert qtest_start("-machine pc -device floppy,id=floppy0");
6201e8a1faeSThomas Huth qtest_irq_intercept_in(global_qtest, "ioapic");
6211e8a1faeSThomas Huth qtest_add_func("/fdc/cmos", test_cmos);
6221e8a1faeSThomas Huth qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
6231e8a1faeSThomas Huth qtest_add_func("/fdc/read_without_media", test_read_without_media);
6241e8a1faeSThomas Huth qtest_add_func("/fdc/media_change", test_media_change);
6251e8a1faeSThomas Huth qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
6261e8a1faeSThomas Huth qtest_add_func("/fdc/relative_seek", test_relative_seek);
6271e8a1faeSThomas Huth qtest_add_func("/fdc/read_id", test_read_id);
6281e8a1faeSThomas Huth qtest_add_func("/fdc/verify", test_verify);
6291e8a1faeSThomas Huth qtest_add_func("/fdc/media_insert", test_media_insert);
6301e8a1faeSThomas Huth qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
6311e8a1faeSThomas Huth qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
6321e8a1faeSThomas Huth qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
6331e8a1faeSThomas Huth qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
634cc20926eSPhilippe Mathieu-Daudé qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196);
63546609b90SPhilippe Mathieu-Daudé qtest_add_func("/fdc/fuzz/cve_2021_3507", test_cve_2021_3507);
6361e8a1faeSThomas Huth
6371e8a1faeSThomas Huth ret = g_test_run();
6381e8a1faeSThomas Huth
6391e8a1faeSThomas Huth /* Cleanup */
6401e8a1faeSThomas Huth qtest_end();
6411e8a1faeSThomas Huth unlink(test_image);
642394bcc5bSBin Meng g_free(test_image);
6431e8a1faeSThomas Huth
6441e8a1faeSThomas Huth return ret;
6451e8a1faeSThomas Huth }
646