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" 29055a1efcSMarkus Armbruster #include "qapi/qmp/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 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 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 */ 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 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 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 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 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 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 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 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 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. */ 304*855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest, 305*855436dbSDaniel 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 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. */ 336*855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest, 337*855436dbSDaniel 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 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 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 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 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 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 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 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 */ 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 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 555cc20926eSPhilippe Mathieu-Daudé g_test_skip("QEMU not configured using --enable-sanitizers"); 556cc20926eSPhilippe Mathieu-Daudé return false; 557cc20926eSPhilippe Mathieu-Daudé #endif 558cc20926eSPhilippe Mathieu-Daudé } 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é 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é 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