xref: /qemu/tests/qtest/fdc-test.c (revision 407bc4bf9027f7ac4333e47cd900d773b99a23e3)
193e9eb68SKevin Wolf /*
293e9eb68SKevin Wolf  * Floppy test cases.
393e9eb68SKevin Wolf  *
493e9eb68SKevin Wolf  * Copyright (c) 2012 Kevin Wolf <kwolf@redhat.com>
593e9eb68SKevin Wolf  *
693e9eb68SKevin Wolf  * Permission is hereby granted, free of charge, to any person obtaining a copy
793e9eb68SKevin Wolf  * of this software and associated documentation files (the "Software"), to deal
893e9eb68SKevin Wolf  * in the Software without restriction, including without limitation the rights
993e9eb68SKevin Wolf  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1093e9eb68SKevin Wolf  * copies of the Software, and to permit persons to whom the Software is
1193e9eb68SKevin Wolf  * furnished to do so, subject to the following conditions:
1293e9eb68SKevin Wolf  *
1393e9eb68SKevin Wolf  * The above copyright notice and this permission notice shall be included in
1493e9eb68SKevin Wolf  * all copies or substantial portions of the Software.
1593e9eb68SKevin Wolf  *
1693e9eb68SKevin Wolf  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1793e9eb68SKevin Wolf  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1893e9eb68SKevin Wolf  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1993e9eb68SKevin Wolf  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2093e9eb68SKevin Wolf  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2193e9eb68SKevin Wolf  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2293e9eb68SKevin Wolf  * THE SOFTWARE.
2393e9eb68SKevin Wolf  */
2493e9eb68SKevin Wolf 
25681c28a3SPeter Maydell #include "qemu/osdep.h"
2693e9eb68SKevin Wolf 
2793e9eb68SKevin Wolf 
28dd210749SThomas Huth #include "libqtest-single.h"
29*407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h"
3093e9eb68SKevin Wolf 
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é 
3493e9eb68SKevin Wolf #define TEST_IMAGE_SIZE 1440 * 1024
3593e9eb68SKevin Wolf 
3693e9eb68SKevin Wolf #define FLOPPY_BASE 0x3f0
3793e9eb68SKevin Wolf #define FLOPPY_IRQ 6
3893e9eb68SKevin Wolf 
3993e9eb68SKevin Wolf enum {
4093e9eb68SKevin Wolf     reg_sra         = 0x0,
4193e9eb68SKevin Wolf     reg_srb         = 0x1,
4293e9eb68SKevin Wolf     reg_dor         = 0x2,
4393e9eb68SKevin Wolf     reg_msr         = 0x4,
4493e9eb68SKevin Wolf     reg_dsr         = 0x4,
4593e9eb68SKevin Wolf     reg_fifo        = 0x5,
4693e9eb68SKevin Wolf     reg_dir         = 0x7,
4793e9eb68SKevin Wolf };
4893e9eb68SKevin Wolf 
4993e9eb68SKevin Wolf enum {
5093e9eb68SKevin Wolf     CMD_SENSE_INT           = 0x08,
5167f194bdSKevin Wolf     CMD_READ_ID             = 0x0a,
5293e9eb68SKevin Wolf     CMD_SEEK                = 0x0f,
536f442fe8SHervé Poussineau     CMD_VERIFY              = 0x16,
548b9ef60dSPavel Hrdina     CMD_READ                = 0xe6,
5598272dbbSPavel Hrdina     CMD_RELATIVE_SEEK_OUT   = 0x8f,
5698272dbbSPavel Hrdina     CMD_RELATIVE_SEEK_IN    = 0xcf,
5793e9eb68SKevin Wolf };
5893e9eb68SKevin Wolf 
5993e9eb68SKevin Wolf enum {
605f8ae8e2SHervé Poussineau     BUSY    = 0x10,
615f8ae8e2SHervé Poussineau     NONDMA  = 0x20,
6293e9eb68SKevin Wolf     RQM     = 0x80,
6393e9eb68SKevin Wolf     DIO     = 0x40,
6493e9eb68SKevin Wolf 
6593e9eb68SKevin Wolf     DSKCHG  = 0x80,
6693e9eb68SKevin Wolf };
6793e9eb68SKevin Wolf 
68394bcc5bSBin Meng static char *test_image;
6993e9eb68SKevin Wolf 
7093e9eb68SKevin Wolf #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
7193e9eb68SKevin Wolf #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
7293e9eb68SKevin Wolf 
737cd33161SPavel Hrdina static uint8_t base = 0x70;
747cd33161SPavel Hrdina 
757cd33161SPavel Hrdina enum {
767cd33161SPavel Hrdina     CMOS_FLOPPY     = 0x10,
777cd33161SPavel Hrdina };
787cd33161SPavel Hrdina 
floppy_send(uint8_t byte)7993e9eb68SKevin Wolf static void floppy_send(uint8_t byte)
8093e9eb68SKevin Wolf {
8193e9eb68SKevin Wolf     uint8_t msr;
8293e9eb68SKevin Wolf 
8393e9eb68SKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
8493e9eb68SKevin Wolf     assert_bit_set(msr, RQM);
8593e9eb68SKevin Wolf     assert_bit_clear(msr, DIO);
8693e9eb68SKevin Wolf 
8793e9eb68SKevin Wolf     outb(FLOPPY_BASE + reg_fifo, byte);
8893e9eb68SKevin Wolf }
8993e9eb68SKevin Wolf 
floppy_recv(void)9093e9eb68SKevin Wolf static uint8_t floppy_recv(void)
9193e9eb68SKevin Wolf {
9293e9eb68SKevin Wolf     uint8_t msr;
9393e9eb68SKevin Wolf 
9493e9eb68SKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
9593e9eb68SKevin Wolf     assert_bit_set(msr, RQM | DIO);
9693e9eb68SKevin Wolf 
9793e9eb68SKevin Wolf     return inb(FLOPPY_BASE + reg_fifo);
9893e9eb68SKevin Wolf }
9993e9eb68SKevin Wolf 
100c3cdc1b0SKevin Wolf /* pcn: Present Cylinder Number */
ack_irq(uint8_t * pcn)101c3cdc1b0SKevin Wolf static void ack_irq(uint8_t *pcn)
10293e9eb68SKevin Wolf {
10398272dbbSPavel Hrdina     uint8_t ret;
10498272dbbSPavel Hrdina 
10593e9eb68SKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
10693e9eb68SKevin Wolf     floppy_send(CMD_SENSE_INT);
10793e9eb68SKevin Wolf     floppy_recv();
10898272dbbSPavel Hrdina 
109c3cdc1b0SKevin Wolf     ret = floppy_recv();
110c3cdc1b0SKevin Wolf     if (pcn != NULL) {
111c3cdc1b0SKevin Wolf         *pcn = ret;
112c3cdc1b0SKevin Wolf     }
113c3cdc1b0SKevin Wolf 
114c3cdc1b0SKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
11593e9eb68SKevin Wolf }
11693e9eb68SKevin Wolf 
send_read_command(uint8_t cmd)1176f442fe8SHervé Poussineau static uint8_t send_read_command(uint8_t cmd)
1188b9ef60dSPavel Hrdina {
1198b9ef60dSPavel Hrdina     uint8_t drive = 0;
1208b9ef60dSPavel Hrdina     uint8_t head = 0;
1218b9ef60dSPavel Hrdina     uint8_t cyl = 0;
1228b9ef60dSPavel Hrdina     uint8_t sect_addr = 1;
1238b9ef60dSPavel Hrdina     uint8_t sect_size = 2;
1248b9ef60dSPavel Hrdina     uint8_t eot = 1;
1258b9ef60dSPavel Hrdina     uint8_t gap = 0x1b;
1268b9ef60dSPavel Hrdina     uint8_t gpl = 0xff;
1278b9ef60dSPavel Hrdina 
1288b9ef60dSPavel Hrdina     uint8_t msr = 0;
1298b9ef60dSPavel Hrdina     uint8_t st0;
1308b9ef60dSPavel Hrdina 
1318b9ef60dSPavel Hrdina     uint8_t ret = 0;
1328b9ef60dSPavel Hrdina 
1336f442fe8SHervé Poussineau     floppy_send(cmd);
1348b9ef60dSPavel Hrdina     floppy_send(head << 2 | drive);
1358b9ef60dSPavel Hrdina     g_assert(!get_irq(FLOPPY_IRQ));
1368b9ef60dSPavel Hrdina     floppy_send(cyl);
1378b9ef60dSPavel Hrdina     floppy_send(head);
1388b9ef60dSPavel Hrdina     floppy_send(sect_addr);
1398b9ef60dSPavel Hrdina     floppy_send(sect_size);
1408b9ef60dSPavel Hrdina     floppy_send(eot);
1418b9ef60dSPavel Hrdina     floppy_send(gap);
1428b9ef60dSPavel Hrdina     floppy_send(gpl);
1438b9ef60dSPavel Hrdina 
1448b9ef60dSPavel Hrdina     uint8_t i = 0;
1458b9ef60dSPavel Hrdina     uint8_t n = 2;
1468b9ef60dSPavel Hrdina     for (; i < n; i++) {
1478b9ef60dSPavel Hrdina         msr = inb(FLOPPY_BASE + reg_msr);
1488b9ef60dSPavel Hrdina         if (msr == 0xd0) {
1498b9ef60dSPavel Hrdina             break;
1508b9ef60dSPavel Hrdina         }
1518b9ef60dSPavel Hrdina         sleep(1);
1528b9ef60dSPavel Hrdina     }
1538b9ef60dSPavel Hrdina 
1548b9ef60dSPavel Hrdina     if (i >= n) {
1558b9ef60dSPavel Hrdina         return 1;
1568b9ef60dSPavel Hrdina     }
1578b9ef60dSPavel Hrdina 
1588b9ef60dSPavel Hrdina     st0 = floppy_recv();
159075f5532SHervé Poussineau     if (st0 != 0x40) {
1608b9ef60dSPavel Hrdina         ret = 1;
1618b9ef60dSPavel Hrdina     }
1628b9ef60dSPavel Hrdina 
1638b9ef60dSPavel Hrdina     floppy_recv();
1648b9ef60dSPavel Hrdina     floppy_recv();
1658b9ef60dSPavel Hrdina     floppy_recv();
1668b9ef60dSPavel Hrdina     floppy_recv();
1678b9ef60dSPavel Hrdina     floppy_recv();
1688b9ef60dSPavel Hrdina     floppy_recv();
1698b9ef60dSPavel Hrdina 
1708b9ef60dSPavel Hrdina     return ret;
1718b9ef60dSPavel Hrdina }
1728b9ef60dSPavel Hrdina 
send_read_no_dma_command(int nb_sect,uint8_t expected_st0)1735f8ae8e2SHervé Poussineau static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
1745f8ae8e2SHervé Poussineau {
1755f8ae8e2SHervé Poussineau     uint8_t drive = 0;
1765f8ae8e2SHervé Poussineau     uint8_t head = 0;
1775f8ae8e2SHervé Poussineau     uint8_t cyl = 0;
1785f8ae8e2SHervé Poussineau     uint8_t sect_addr = 1;
1795f8ae8e2SHervé Poussineau     uint8_t sect_size = 2;
1805f8ae8e2SHervé Poussineau     uint8_t eot = nb_sect;
1815f8ae8e2SHervé Poussineau     uint8_t gap = 0x1b;
1825f8ae8e2SHervé Poussineau     uint8_t gpl = 0xff;
1835f8ae8e2SHervé Poussineau 
1845f8ae8e2SHervé Poussineau     uint8_t msr = 0;
1855f8ae8e2SHervé Poussineau     uint8_t st0;
1865f8ae8e2SHervé Poussineau 
1875f8ae8e2SHervé Poussineau     uint8_t ret = 0;
1885f8ae8e2SHervé Poussineau 
1895f8ae8e2SHervé Poussineau     floppy_send(CMD_READ);
1905f8ae8e2SHervé Poussineau     floppy_send(head << 2 | drive);
1915f8ae8e2SHervé Poussineau     g_assert(!get_irq(FLOPPY_IRQ));
1925f8ae8e2SHervé Poussineau     floppy_send(cyl);
1935f8ae8e2SHervé Poussineau     floppy_send(head);
1945f8ae8e2SHervé Poussineau     floppy_send(sect_addr);
1955f8ae8e2SHervé Poussineau     floppy_send(sect_size);
1965f8ae8e2SHervé Poussineau     floppy_send(eot);
1975f8ae8e2SHervé Poussineau     floppy_send(gap);
1985f8ae8e2SHervé Poussineau     floppy_send(gpl);
1995f8ae8e2SHervé Poussineau 
2005f8ae8e2SHervé Poussineau     uint16_t i = 0;
2015f8ae8e2SHervé Poussineau     uint8_t n = 2;
2025f8ae8e2SHervé Poussineau     for (; i < n; i++) {
2035f8ae8e2SHervé Poussineau         msr = inb(FLOPPY_BASE + reg_msr);
2045f8ae8e2SHervé Poussineau         if (msr == (BUSY | NONDMA | DIO | RQM)) {
2055f8ae8e2SHervé Poussineau             break;
2065f8ae8e2SHervé Poussineau         }
2075f8ae8e2SHervé Poussineau         sleep(1);
2085f8ae8e2SHervé Poussineau     }
2095f8ae8e2SHervé Poussineau 
2105f8ae8e2SHervé Poussineau     if (i >= n) {
2115f8ae8e2SHervé Poussineau         return 1;
2125f8ae8e2SHervé Poussineau     }
2135f8ae8e2SHervé Poussineau 
2145f8ae8e2SHervé Poussineau     /* Non-DMA mode */
2155f8ae8e2SHervé Poussineau     for (i = 0; i < 512 * 2 * nb_sect; i++) {
2165f8ae8e2SHervé Poussineau         msr = inb(FLOPPY_BASE + reg_msr);
2175f8ae8e2SHervé Poussineau         assert_bit_set(msr, BUSY | RQM | DIO);
2185f8ae8e2SHervé Poussineau         inb(FLOPPY_BASE + reg_fifo);
2195f8ae8e2SHervé Poussineau     }
2205f8ae8e2SHervé Poussineau 
2214964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
2224964e18eSKevin Wolf     assert_bit_set(msr, BUSY | RQM | DIO);
2234964e18eSKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
2244964e18eSKevin Wolf 
2255f8ae8e2SHervé Poussineau     st0 = floppy_recv();
2265f8ae8e2SHervé Poussineau     if (st0 != expected_st0) {
2275f8ae8e2SHervé Poussineau         ret = 1;
2285f8ae8e2SHervé Poussineau     }
2295f8ae8e2SHervé Poussineau 
2305f8ae8e2SHervé Poussineau     floppy_recv();
2315f8ae8e2SHervé Poussineau     floppy_recv();
2325f8ae8e2SHervé Poussineau     floppy_recv();
2335f8ae8e2SHervé Poussineau     floppy_recv();
2345f8ae8e2SHervé Poussineau     floppy_recv();
2354964e18eSKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
2365f8ae8e2SHervé Poussineau     floppy_recv();
2375f8ae8e2SHervé Poussineau 
2384964e18eSKevin Wolf     /* Check that we're back in command phase */
2394964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
2404964e18eSKevin Wolf     assert_bit_clear(msr, BUSY | DIO);
2414964e18eSKevin Wolf     assert_bit_set(msr, RQM);
2424964e18eSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
2434964e18eSKevin Wolf 
2445f8ae8e2SHervé Poussineau     return ret;
2455f8ae8e2SHervé Poussineau }
2465f8ae8e2SHervé Poussineau 
send_seek(int cyl)247c3cdc1b0SKevin Wolf static void send_seek(int cyl)
24893e9eb68SKevin Wolf {
24993e9eb68SKevin Wolf     int drive = 0;
25093e9eb68SKevin Wolf     int head = 0;
25193e9eb68SKevin Wolf 
25293e9eb68SKevin Wolf     floppy_send(CMD_SEEK);
25393e9eb68SKevin Wolf     floppy_send(head << 2 | drive);
25493e9eb68SKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
25593e9eb68SKevin Wolf     floppy_send(cyl);
256c3cdc1b0SKevin Wolf     ack_irq(NULL);
25793e9eb68SKevin Wolf }
25893e9eb68SKevin Wolf 
cmos_read(uint8_t reg)2597cd33161SPavel Hrdina static uint8_t cmos_read(uint8_t reg)
2607cd33161SPavel Hrdina {
2617cd33161SPavel Hrdina     outb(base + 0, reg);
2627cd33161SPavel Hrdina     return inb(base + 1);
2637cd33161SPavel Hrdina }
2647cd33161SPavel Hrdina 
test_cmos(void)2657cd33161SPavel Hrdina static void test_cmos(void)
2667cd33161SPavel Hrdina {
2677cd33161SPavel Hrdina     uint8_t cmos;
2687cd33161SPavel Hrdina 
2697cd33161SPavel Hrdina     cmos = cmos_read(CMOS_FLOPPY);
27062c76250SJohn Snow     g_assert(cmos == 0x40 || cmos == 0x50);
2717cd33161SPavel Hrdina }
2727cd33161SPavel Hrdina 
test_no_media_on_start(void)2737cd33161SPavel Hrdina static void test_no_media_on_start(void)
2747cd33161SPavel Hrdina {
2757cd33161SPavel Hrdina     uint8_t dir;
2767cd33161SPavel Hrdina 
2777cd33161SPavel Hrdina     /* Media changed bit must be set all time after start if there is
2787cd33161SPavel Hrdina      * no media in drive. */
2797cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
2807cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
2817cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
2827cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
283c3cdc1b0SKevin Wolf     send_seek(1);
2847cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
2857cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
2867cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
2877cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
2887cd33161SPavel Hrdina }
2897cd33161SPavel Hrdina 
test_read_without_media(void)2908b9ef60dSPavel Hrdina static void test_read_without_media(void)
2918b9ef60dSPavel Hrdina {
2928b9ef60dSPavel Hrdina     uint8_t ret;
2938b9ef60dSPavel Hrdina 
2946f442fe8SHervé Poussineau     ret = send_read_command(CMD_READ);
2958b9ef60dSPavel Hrdina     g_assert(ret == 0);
2968b9ef60dSPavel Hrdina }
2978b9ef60dSPavel Hrdina 
test_media_insert(void)2981f507913SHervé Poussineau static void test_media_insert(void)
29993e9eb68SKevin Wolf {
30093e9eb68SKevin Wolf     uint8_t dir;
30193e9eb68SKevin Wolf 
3027cd33161SPavel Hrdina     /* Insert media in drive. DSKCHK should not be reset until a step pulse
3037cd33161SPavel Hrdina      * is sent. */
304855436dbSDaniel P. Berrangé     qtest_qmp_assert_success(global_qtest,
305855436dbSDaniel P. Berrangé                              "{'execute':'blockdev-change-medium', 'arguments':{"
306ccb61bddSEric Blake                              " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}",
3070d1aa05eSStefan Hajnoczi                              test_image);
3087cd33161SPavel Hrdina 
3097cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
3107cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
3117cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
3127cd33161SPavel Hrdina     assert_bit_set(dir, DSKCHG);
3137cd33161SPavel Hrdina 
314c3cdc1b0SKevin Wolf     send_seek(0);
31559240c34SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
31659240c34SPavel Hrdina     assert_bit_set(dir, DSKCHG);
31759240c34SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
31859240c34SPavel Hrdina     assert_bit_set(dir, DSKCHG);
31959240c34SPavel Hrdina 
32059240c34SPavel Hrdina     /* Step to next track should clear DSKCHG bit. */
321c3cdc1b0SKevin Wolf     send_seek(1);
3227cd33161SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
3237cd33161SPavel Hrdina     assert_bit_clear(dir, DSKCHG);
32493e9eb68SKevin Wolf     dir = inb(FLOPPY_BASE + reg_dir);
32593e9eb68SKevin Wolf     assert_bit_clear(dir, DSKCHG);
3261f507913SHervé Poussineau }
3271f507913SHervé Poussineau 
test_media_change(void)3281f507913SHervé Poussineau static void test_media_change(void)
3291f507913SHervé Poussineau {
3301f507913SHervé Poussineau     uint8_t dir;
3311f507913SHervé Poussineau 
3321f507913SHervé Poussineau     test_media_insert();
33393e9eb68SKevin Wolf 
33493e9eb68SKevin Wolf     /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
33593e9eb68SKevin Wolf      * reset the bit. */
336855436dbSDaniel P. Berrangé     qtest_qmp_assert_success(global_qtest,
337855436dbSDaniel P. Berrangé                              "{'execute':'eject', 'arguments':{"
338ccb61bddSEric Blake                              " 'id':'floppy0' }}");
33993e9eb68SKevin Wolf 
34093e9eb68SKevin Wolf     dir = inb(FLOPPY_BASE + reg_dir);
34193e9eb68SKevin Wolf     assert_bit_set(dir, DSKCHG);
34293e9eb68SKevin Wolf     dir = inb(FLOPPY_BASE + reg_dir);
34393e9eb68SKevin Wolf     assert_bit_set(dir, DSKCHG);
34493e9eb68SKevin Wolf 
345c3cdc1b0SKevin Wolf     send_seek(0);
34659240c34SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
34759240c34SPavel Hrdina     assert_bit_set(dir, DSKCHG);
34859240c34SPavel Hrdina     dir = inb(FLOPPY_BASE + reg_dir);
34959240c34SPavel Hrdina     assert_bit_set(dir, DSKCHG);
35059240c34SPavel Hrdina 
351c3cdc1b0SKevin Wolf     send_seek(1);
35293e9eb68SKevin Wolf     dir = inb(FLOPPY_BASE + reg_dir);
35393e9eb68SKevin Wolf     assert_bit_set(dir, DSKCHG);
35493e9eb68SKevin Wolf     dir = inb(FLOPPY_BASE + reg_dir);
35593e9eb68SKevin Wolf     assert_bit_set(dir, DSKCHG);
35693e9eb68SKevin Wolf }
35793e9eb68SKevin Wolf 
test_sense_interrupt(void)358b3ce604eSPavel Hrdina static void test_sense_interrupt(void)
359b3ce604eSPavel Hrdina {
360b3ce604eSPavel Hrdina     int drive = 0;
361b3ce604eSPavel Hrdina     int head = 0;
362b3ce604eSPavel Hrdina     int cyl = 0;
363b3ce604eSPavel Hrdina     int ret = 0;
364b3ce604eSPavel Hrdina 
365b3ce604eSPavel Hrdina     floppy_send(CMD_SENSE_INT);
366b3ce604eSPavel Hrdina     ret = floppy_recv();
367b3ce604eSPavel Hrdina     g_assert(ret == 0x80);
368b3ce604eSPavel Hrdina 
369b3ce604eSPavel Hrdina     floppy_send(CMD_SEEK);
370b3ce604eSPavel Hrdina     floppy_send(head << 2 | drive);
371b3ce604eSPavel Hrdina     g_assert(!get_irq(FLOPPY_IRQ));
372b3ce604eSPavel Hrdina     floppy_send(cyl);
373b3ce604eSPavel Hrdina 
374b3ce604eSPavel Hrdina     floppy_send(CMD_SENSE_INT);
375b3ce604eSPavel Hrdina     ret = floppy_recv();
376b3ce604eSPavel Hrdina     g_assert(ret == 0x20);
377b3ce604eSPavel Hrdina     floppy_recv();
378b3ce604eSPavel Hrdina }
379b3ce604eSPavel Hrdina 
test_relative_seek(void)38098272dbbSPavel Hrdina static void test_relative_seek(void)
38198272dbbSPavel Hrdina {
38298272dbbSPavel Hrdina     uint8_t drive = 0;
38398272dbbSPavel Hrdina     uint8_t head = 0;
38498272dbbSPavel Hrdina     uint8_t cyl = 1;
385c3cdc1b0SKevin Wolf     uint8_t pcn;
38698272dbbSPavel Hrdina 
38798272dbbSPavel Hrdina     /* Send seek to track 0 */
388c3cdc1b0SKevin Wolf     send_seek(0);
38998272dbbSPavel Hrdina 
39098272dbbSPavel Hrdina     /* Send relative seek to increase track by 1 */
39198272dbbSPavel Hrdina     floppy_send(CMD_RELATIVE_SEEK_IN);
39298272dbbSPavel Hrdina     floppy_send(head << 2 | drive);
39398272dbbSPavel Hrdina     g_assert(!get_irq(FLOPPY_IRQ));
39498272dbbSPavel Hrdina     floppy_send(cyl);
39598272dbbSPavel Hrdina 
396c3cdc1b0SKevin Wolf     ack_irq(&pcn);
397c3cdc1b0SKevin Wolf     g_assert(pcn == 1);
39898272dbbSPavel Hrdina 
39998272dbbSPavel Hrdina     /* Send relative seek to decrease track by 1 */
40098272dbbSPavel Hrdina     floppy_send(CMD_RELATIVE_SEEK_OUT);
40198272dbbSPavel Hrdina     floppy_send(head << 2 | drive);
40298272dbbSPavel Hrdina     g_assert(!get_irq(FLOPPY_IRQ));
40398272dbbSPavel Hrdina     floppy_send(cyl);
40498272dbbSPavel Hrdina 
405c3cdc1b0SKevin Wolf     ack_irq(&pcn);
406c3cdc1b0SKevin Wolf     g_assert(pcn == 0);
40798272dbbSPavel Hrdina }
40898272dbbSPavel Hrdina 
test_read_id(void)40967f194bdSKevin Wolf static void test_read_id(void)
41067f194bdSKevin Wolf {
41167f194bdSKevin Wolf     uint8_t drive = 0;
41267f194bdSKevin Wolf     uint8_t head = 0;
41367f194bdSKevin Wolf     uint8_t cyl;
41467f194bdSKevin Wolf     uint8_t st0;
4154964e18eSKevin Wolf     uint8_t msr;
41667f194bdSKevin Wolf 
41767f194bdSKevin Wolf     /* Seek to track 0 and check with READ ID */
41867f194bdSKevin Wolf     send_seek(0);
41967f194bdSKevin Wolf 
42067f194bdSKevin Wolf     floppy_send(CMD_READ_ID);
42167f194bdSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
42267f194bdSKevin Wolf     floppy_send(head << 2 | drive);
42367f194bdSKevin Wolf 
4244964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
4254964e18eSKevin Wolf     if (!get_irq(FLOPPY_IRQ)) {
4264964e18eSKevin Wolf         assert_bit_set(msr, BUSY);
4274964e18eSKevin Wolf         assert_bit_clear(msr, RQM);
4284964e18eSKevin Wolf     }
4294964e18eSKevin Wolf 
43067f194bdSKevin Wolf     while (!get_irq(FLOPPY_IRQ)) {
43167f194bdSKevin Wolf         /* qemu involves a timer with READ ID... */
43267f194bdSKevin Wolf         clock_step(1000000000LL / 50);
43367f194bdSKevin Wolf     }
43467f194bdSKevin Wolf 
4354964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
4364964e18eSKevin Wolf     assert_bit_set(msr, BUSY | RQM | DIO);
4374964e18eSKevin Wolf 
43867f194bdSKevin Wolf     st0 = floppy_recv();
43967f194bdSKevin Wolf     floppy_recv();
44067f194bdSKevin Wolf     floppy_recv();
44167f194bdSKevin Wolf     cyl = floppy_recv();
44267f194bdSKevin Wolf     head = floppy_recv();
44367f194bdSKevin Wolf     floppy_recv();
4444964e18eSKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
44567f194bdSKevin Wolf     floppy_recv();
4464964e18eSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
44767f194bdSKevin Wolf 
44867f194bdSKevin Wolf     g_assert_cmpint(cyl, ==, 0);
44967f194bdSKevin Wolf     g_assert_cmpint(head, ==, 0);
45067f194bdSKevin Wolf     g_assert_cmpint(st0, ==, head << 2);
45167f194bdSKevin Wolf 
45267f194bdSKevin Wolf     /* Seek to track 8 on head 1 and check with READ ID */
45367f194bdSKevin Wolf     head = 1;
45467f194bdSKevin Wolf     cyl = 8;
45567f194bdSKevin Wolf 
45667f194bdSKevin Wolf     floppy_send(CMD_SEEK);
45767f194bdSKevin Wolf     floppy_send(head << 2 | drive);
45867f194bdSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
45967f194bdSKevin Wolf     floppy_send(cyl);
46067f194bdSKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
46167f194bdSKevin Wolf     ack_irq(NULL);
46267f194bdSKevin Wolf 
46367f194bdSKevin Wolf     floppy_send(CMD_READ_ID);
46467f194bdSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
46567f194bdSKevin Wolf     floppy_send(head << 2 | drive);
46667f194bdSKevin Wolf 
4674964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
4684964e18eSKevin Wolf     if (!get_irq(FLOPPY_IRQ)) {
4694964e18eSKevin Wolf         assert_bit_set(msr, BUSY);
4704964e18eSKevin Wolf         assert_bit_clear(msr, RQM);
4714964e18eSKevin Wolf     }
4724964e18eSKevin Wolf 
47367f194bdSKevin Wolf     while (!get_irq(FLOPPY_IRQ)) {
47467f194bdSKevin Wolf         /* qemu involves a timer with READ ID... */
47567f194bdSKevin Wolf         clock_step(1000000000LL / 50);
47667f194bdSKevin Wolf     }
47767f194bdSKevin Wolf 
4784964e18eSKevin Wolf     msr = inb(FLOPPY_BASE + reg_msr);
4794964e18eSKevin Wolf     assert_bit_set(msr, BUSY | RQM | DIO);
4804964e18eSKevin Wolf 
48167f194bdSKevin Wolf     st0 = floppy_recv();
48267f194bdSKevin Wolf     floppy_recv();
48367f194bdSKevin Wolf     floppy_recv();
48467f194bdSKevin Wolf     cyl = floppy_recv();
48567f194bdSKevin Wolf     head = floppy_recv();
48667f194bdSKevin Wolf     floppy_recv();
4874964e18eSKevin Wolf     g_assert(get_irq(FLOPPY_IRQ));
48867f194bdSKevin Wolf     floppy_recv();
4894964e18eSKevin Wolf     g_assert(!get_irq(FLOPPY_IRQ));
49067f194bdSKevin Wolf 
49167f194bdSKevin Wolf     g_assert_cmpint(cyl, ==, 8);
49267f194bdSKevin Wolf     g_assert_cmpint(head, ==, 1);
49367f194bdSKevin Wolf     g_assert_cmpint(st0, ==, head << 2);
49467f194bdSKevin Wolf }
49567f194bdSKevin Wolf 
test_read_no_dma_1(void)4965f8ae8e2SHervé Poussineau static void test_read_no_dma_1(void)
4975f8ae8e2SHervé Poussineau {
4985f8ae8e2SHervé Poussineau     uint8_t ret;
4995f8ae8e2SHervé Poussineau 
5005f8ae8e2SHervé Poussineau     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5015f8ae8e2SHervé Poussineau     send_seek(0);
502075f5532SHervé Poussineau     ret = send_read_no_dma_command(1, 0x04);
5035f8ae8e2SHervé Poussineau     g_assert(ret == 0);
5045f8ae8e2SHervé Poussineau }
5055f8ae8e2SHervé Poussineau 
test_read_no_dma_18(void)5065f8ae8e2SHervé Poussineau static void test_read_no_dma_18(void)
5075f8ae8e2SHervé Poussineau {
5085f8ae8e2SHervé Poussineau     uint8_t ret;
5095f8ae8e2SHervé Poussineau 
5105f8ae8e2SHervé Poussineau     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5115f8ae8e2SHervé Poussineau     send_seek(0);
512075f5532SHervé Poussineau     ret = send_read_no_dma_command(18, 0x04);
5135f8ae8e2SHervé Poussineau     g_assert(ret == 0);
5145f8ae8e2SHervé Poussineau }
5155f8ae8e2SHervé Poussineau 
test_read_no_dma_19(void)5165f8ae8e2SHervé Poussineau static void test_read_no_dma_19(void)
5175f8ae8e2SHervé Poussineau {
5185f8ae8e2SHervé Poussineau     uint8_t ret;
5195f8ae8e2SHervé Poussineau 
5205f8ae8e2SHervé Poussineau     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
5215f8ae8e2SHervé Poussineau     send_seek(0);
5225f8ae8e2SHervé Poussineau     ret = send_read_no_dma_command(19, 0x20);
5235f8ae8e2SHervé Poussineau     g_assert(ret == 0);
5245f8ae8e2SHervé Poussineau }
5255f8ae8e2SHervé Poussineau 
test_verify(void)5266f442fe8SHervé Poussineau static void test_verify(void)
5276f442fe8SHervé Poussineau {
5286f442fe8SHervé Poussineau     uint8_t ret;
5296f442fe8SHervé Poussineau 
5306f442fe8SHervé Poussineau     ret = send_read_command(CMD_VERIFY);
5316f442fe8SHervé Poussineau     g_assert(ret == 0);
5326f442fe8SHervé Poussineau }
5336f442fe8SHervé Poussineau 
5343359847eSBlue Swirl /* success if no crash or abort */
fuzz_registers(void)5353359847eSBlue Swirl static void fuzz_registers(void)
5363359847eSBlue Swirl {
5373359847eSBlue Swirl     unsigned int i;
5383359847eSBlue Swirl 
5393359847eSBlue Swirl     for (i = 0; i < 1000; i++) {
5403359847eSBlue Swirl         uint8_t reg, val;
5413359847eSBlue Swirl 
5423359847eSBlue Swirl         reg = (uint8_t)g_test_rand_int_range(0, 8);
5433359847eSBlue Swirl         val = (uint8_t)g_test_rand_int_range(0, 256);
5443359847eSBlue Swirl 
5453359847eSBlue Swirl         outb(FLOPPY_BASE + reg, val);
5463359847eSBlue Swirl         inb(FLOPPY_BASE + reg);
5473359847eSBlue Swirl     }
5483359847eSBlue Swirl }
5493359847eSBlue Swirl 
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
555cb771ac1SRichard 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)60493e9eb68SKevin Wolf int main(int argc, char **argv)
60593e9eb68SKevin Wolf {
60693e9eb68SKevin Wolf     int fd;
60793e9eb68SKevin Wolf     int ret;
60893e9eb68SKevin Wolf 
60993e9eb68SKevin Wolf     /* Create a temporary raw image */
610394bcc5bSBin Meng     fd = g_file_open_tmp("qtest.XXXXXX", &test_image, NULL);
61193e9eb68SKevin Wolf     g_assert(fd >= 0);
61293e9eb68SKevin Wolf     ret = ftruncate(fd, TEST_IMAGE_SIZE);
61393e9eb68SKevin Wolf     g_assert(ret == 0);
61493e9eb68SKevin Wolf     close(fd);
61593e9eb68SKevin Wolf 
61693e9eb68SKevin Wolf     /* Run the tests */
61793e9eb68SKevin Wolf     g_test_init(&argc, &argv, NULL);
61893e9eb68SKevin Wolf 
619fedcc379SDr. David Alan Gilbert     qtest_start("-machine pc -device floppy,id=floppy0");
62093e9eb68SKevin Wolf     qtest_irq_intercept_in(global_qtest, "ioapic");
6217cd33161SPavel Hrdina     qtest_add_func("/fdc/cmos", test_cmos);
6227cd33161SPavel Hrdina     qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
6238b9ef60dSPavel Hrdina     qtest_add_func("/fdc/read_without_media", test_read_without_media);
62493e9eb68SKevin Wolf     qtest_add_func("/fdc/media_change", test_media_change);
625b3ce604eSPavel Hrdina     qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
62698272dbbSPavel Hrdina     qtest_add_func("/fdc/relative_seek", test_relative_seek);
62767f194bdSKevin Wolf     qtest_add_func("/fdc/read_id", test_read_id);
6286f442fe8SHervé Poussineau     qtest_add_func("/fdc/verify", test_verify);
62944212dccSHervé Poussineau     qtest_add_func("/fdc/media_insert", test_media_insert);
6305f8ae8e2SHervé Poussineau     qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
6315f8ae8e2SHervé Poussineau     qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
6325f8ae8e2SHervé Poussineau     qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
6333359847eSBlue Swirl     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);
63693e9eb68SKevin Wolf 
63793e9eb68SKevin Wolf     ret = g_test_run();
63893e9eb68SKevin Wolf 
63993e9eb68SKevin Wolf     /* Cleanup */
6401d9358e6SMarkus Armbruster     qtest_end();
64193e9eb68SKevin Wolf     unlink(test_image);
642394bcc5bSBin Meng     g_free(test_image);
64393e9eb68SKevin Wolf 
64493e9eb68SKevin Wolf     return ret;
64593e9eb68SKevin Wolf }
646