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 31055a1efcSMarkus Armbruster /* TODO actually test the results and get rid of this */ 32055a1efcSMarkus Armbruster #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) 33055a1efcSMarkus Armbruster 34cc20926eSPhilippe Mathieu-Daudé #define DRIVE_FLOPPY_BLANK \ 35cc20926eSPhilippe Mathieu-Daudé "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k" 36cc20926eSPhilippe Mathieu-Daudé 3793e9eb68SKevin Wolf #define TEST_IMAGE_SIZE 1440 * 1024 3893e9eb68SKevin Wolf 3993e9eb68SKevin Wolf #define FLOPPY_BASE 0x3f0 4093e9eb68SKevin Wolf #define FLOPPY_IRQ 6 4193e9eb68SKevin Wolf 4293e9eb68SKevin Wolf enum { 4393e9eb68SKevin Wolf reg_sra = 0x0, 4493e9eb68SKevin Wolf reg_srb = 0x1, 4593e9eb68SKevin Wolf reg_dor = 0x2, 4693e9eb68SKevin Wolf reg_msr = 0x4, 4793e9eb68SKevin Wolf reg_dsr = 0x4, 4893e9eb68SKevin Wolf reg_fifo = 0x5, 4993e9eb68SKevin Wolf reg_dir = 0x7, 5093e9eb68SKevin Wolf }; 5193e9eb68SKevin Wolf 5293e9eb68SKevin Wolf enum { 5393e9eb68SKevin Wolf CMD_SENSE_INT = 0x08, 5467f194bdSKevin Wolf CMD_READ_ID = 0x0a, 5593e9eb68SKevin Wolf CMD_SEEK = 0x0f, 566f442fe8SHervé Poussineau CMD_VERIFY = 0x16, 578b9ef60dSPavel Hrdina CMD_READ = 0xe6, 5898272dbbSPavel Hrdina CMD_RELATIVE_SEEK_OUT = 0x8f, 5998272dbbSPavel Hrdina CMD_RELATIVE_SEEK_IN = 0xcf, 6093e9eb68SKevin Wolf }; 6193e9eb68SKevin Wolf 6293e9eb68SKevin Wolf enum { 635f8ae8e2SHervé Poussineau BUSY = 0x10, 645f8ae8e2SHervé Poussineau NONDMA = 0x20, 6593e9eb68SKevin Wolf RQM = 0x80, 6693e9eb68SKevin Wolf DIO = 0x40, 6793e9eb68SKevin Wolf 6893e9eb68SKevin Wolf DSKCHG = 0x80, 6993e9eb68SKevin Wolf }; 7093e9eb68SKevin Wolf 71748bfb4eSStefan Weil static char test_image[] = "/tmp/qtest.XXXXXX"; 7293e9eb68SKevin Wolf 7393e9eb68SKevin Wolf #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) 7493e9eb68SKevin Wolf #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) 7593e9eb68SKevin Wolf 767cd33161SPavel Hrdina static uint8_t base = 0x70; 777cd33161SPavel Hrdina 787cd33161SPavel Hrdina enum { 797cd33161SPavel Hrdina CMOS_FLOPPY = 0x10, 807cd33161SPavel Hrdina }; 817cd33161SPavel Hrdina 8293e9eb68SKevin Wolf static void floppy_send(uint8_t byte) 8393e9eb68SKevin Wolf { 8493e9eb68SKevin Wolf uint8_t msr; 8593e9eb68SKevin Wolf 8693e9eb68SKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 8793e9eb68SKevin Wolf assert_bit_set(msr, RQM); 8893e9eb68SKevin Wolf assert_bit_clear(msr, DIO); 8993e9eb68SKevin Wolf 9093e9eb68SKevin Wolf outb(FLOPPY_BASE + reg_fifo, byte); 9193e9eb68SKevin Wolf } 9293e9eb68SKevin Wolf 9393e9eb68SKevin Wolf static uint8_t floppy_recv(void) 9493e9eb68SKevin Wolf { 9593e9eb68SKevin Wolf uint8_t msr; 9693e9eb68SKevin Wolf 9793e9eb68SKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 9893e9eb68SKevin Wolf assert_bit_set(msr, RQM | DIO); 9993e9eb68SKevin Wolf 10093e9eb68SKevin Wolf return inb(FLOPPY_BASE + reg_fifo); 10193e9eb68SKevin Wolf } 10293e9eb68SKevin Wolf 103c3cdc1b0SKevin Wolf /* pcn: Present Cylinder Number */ 104c3cdc1b0SKevin Wolf static void ack_irq(uint8_t *pcn) 10593e9eb68SKevin Wolf { 10698272dbbSPavel Hrdina uint8_t ret; 10798272dbbSPavel Hrdina 10893e9eb68SKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 10993e9eb68SKevin Wolf floppy_send(CMD_SENSE_INT); 11093e9eb68SKevin Wolf floppy_recv(); 11198272dbbSPavel Hrdina 112c3cdc1b0SKevin Wolf ret = floppy_recv(); 113c3cdc1b0SKevin Wolf if (pcn != NULL) { 114c3cdc1b0SKevin Wolf *pcn = ret; 115c3cdc1b0SKevin Wolf } 116c3cdc1b0SKevin Wolf 117c3cdc1b0SKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 11893e9eb68SKevin Wolf } 11993e9eb68SKevin Wolf 1206f442fe8SHervé Poussineau static uint8_t send_read_command(uint8_t cmd) 1218b9ef60dSPavel Hrdina { 1228b9ef60dSPavel Hrdina uint8_t drive = 0; 1238b9ef60dSPavel Hrdina uint8_t head = 0; 1248b9ef60dSPavel Hrdina uint8_t cyl = 0; 1258b9ef60dSPavel Hrdina uint8_t sect_addr = 1; 1268b9ef60dSPavel Hrdina uint8_t sect_size = 2; 1278b9ef60dSPavel Hrdina uint8_t eot = 1; 1288b9ef60dSPavel Hrdina uint8_t gap = 0x1b; 1298b9ef60dSPavel Hrdina uint8_t gpl = 0xff; 1308b9ef60dSPavel Hrdina 1318b9ef60dSPavel Hrdina uint8_t msr = 0; 1328b9ef60dSPavel Hrdina uint8_t st0; 1338b9ef60dSPavel Hrdina 1348b9ef60dSPavel Hrdina uint8_t ret = 0; 1358b9ef60dSPavel Hrdina 1366f442fe8SHervé Poussineau floppy_send(cmd); 1378b9ef60dSPavel Hrdina floppy_send(head << 2 | drive); 1388b9ef60dSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 1398b9ef60dSPavel Hrdina floppy_send(cyl); 1408b9ef60dSPavel Hrdina floppy_send(head); 1418b9ef60dSPavel Hrdina floppy_send(sect_addr); 1428b9ef60dSPavel Hrdina floppy_send(sect_size); 1438b9ef60dSPavel Hrdina floppy_send(eot); 1448b9ef60dSPavel Hrdina floppy_send(gap); 1458b9ef60dSPavel Hrdina floppy_send(gpl); 1468b9ef60dSPavel Hrdina 1478b9ef60dSPavel Hrdina uint8_t i = 0; 1488b9ef60dSPavel Hrdina uint8_t n = 2; 1498b9ef60dSPavel Hrdina for (; i < n; i++) { 1508b9ef60dSPavel Hrdina msr = inb(FLOPPY_BASE + reg_msr); 1518b9ef60dSPavel Hrdina if (msr == 0xd0) { 1528b9ef60dSPavel Hrdina break; 1538b9ef60dSPavel Hrdina } 1548b9ef60dSPavel Hrdina sleep(1); 1558b9ef60dSPavel Hrdina } 1568b9ef60dSPavel Hrdina 1578b9ef60dSPavel Hrdina if (i >= n) { 1588b9ef60dSPavel Hrdina return 1; 1598b9ef60dSPavel Hrdina } 1608b9ef60dSPavel Hrdina 1618b9ef60dSPavel Hrdina st0 = floppy_recv(); 162075f5532SHervé Poussineau if (st0 != 0x40) { 1638b9ef60dSPavel Hrdina ret = 1; 1648b9ef60dSPavel Hrdina } 1658b9ef60dSPavel Hrdina 1668b9ef60dSPavel Hrdina floppy_recv(); 1678b9ef60dSPavel Hrdina floppy_recv(); 1688b9ef60dSPavel Hrdina floppy_recv(); 1698b9ef60dSPavel Hrdina floppy_recv(); 1708b9ef60dSPavel Hrdina floppy_recv(); 1718b9ef60dSPavel Hrdina floppy_recv(); 1728b9ef60dSPavel Hrdina 1738b9ef60dSPavel Hrdina return ret; 1748b9ef60dSPavel Hrdina } 1758b9ef60dSPavel Hrdina 1765f8ae8e2SHervé Poussineau static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) 1775f8ae8e2SHervé Poussineau { 1785f8ae8e2SHervé Poussineau uint8_t drive = 0; 1795f8ae8e2SHervé Poussineau uint8_t head = 0; 1805f8ae8e2SHervé Poussineau uint8_t cyl = 0; 1815f8ae8e2SHervé Poussineau uint8_t sect_addr = 1; 1825f8ae8e2SHervé Poussineau uint8_t sect_size = 2; 1835f8ae8e2SHervé Poussineau uint8_t eot = nb_sect; 1845f8ae8e2SHervé Poussineau uint8_t gap = 0x1b; 1855f8ae8e2SHervé Poussineau uint8_t gpl = 0xff; 1865f8ae8e2SHervé Poussineau 1875f8ae8e2SHervé Poussineau uint8_t msr = 0; 1885f8ae8e2SHervé Poussineau uint8_t st0; 1895f8ae8e2SHervé Poussineau 1905f8ae8e2SHervé Poussineau uint8_t ret = 0; 1915f8ae8e2SHervé Poussineau 1925f8ae8e2SHervé Poussineau floppy_send(CMD_READ); 1935f8ae8e2SHervé Poussineau floppy_send(head << 2 | drive); 1945f8ae8e2SHervé Poussineau g_assert(!get_irq(FLOPPY_IRQ)); 1955f8ae8e2SHervé Poussineau floppy_send(cyl); 1965f8ae8e2SHervé Poussineau floppy_send(head); 1975f8ae8e2SHervé Poussineau floppy_send(sect_addr); 1985f8ae8e2SHervé Poussineau floppy_send(sect_size); 1995f8ae8e2SHervé Poussineau floppy_send(eot); 2005f8ae8e2SHervé Poussineau floppy_send(gap); 2015f8ae8e2SHervé Poussineau floppy_send(gpl); 2025f8ae8e2SHervé Poussineau 2035f8ae8e2SHervé Poussineau uint16_t i = 0; 2045f8ae8e2SHervé Poussineau uint8_t n = 2; 2055f8ae8e2SHervé Poussineau for (; i < n; i++) { 2065f8ae8e2SHervé Poussineau msr = inb(FLOPPY_BASE + reg_msr); 2075f8ae8e2SHervé Poussineau if (msr == (BUSY | NONDMA | DIO | RQM)) { 2085f8ae8e2SHervé Poussineau break; 2095f8ae8e2SHervé Poussineau } 2105f8ae8e2SHervé Poussineau sleep(1); 2115f8ae8e2SHervé Poussineau } 2125f8ae8e2SHervé Poussineau 2135f8ae8e2SHervé Poussineau if (i >= n) { 2145f8ae8e2SHervé Poussineau return 1; 2155f8ae8e2SHervé Poussineau } 2165f8ae8e2SHervé Poussineau 2175f8ae8e2SHervé Poussineau /* Non-DMA mode */ 2185f8ae8e2SHervé Poussineau for (i = 0; i < 512 * 2 * nb_sect; i++) { 2195f8ae8e2SHervé Poussineau msr = inb(FLOPPY_BASE + reg_msr); 2205f8ae8e2SHervé Poussineau assert_bit_set(msr, BUSY | RQM | DIO); 2215f8ae8e2SHervé Poussineau inb(FLOPPY_BASE + reg_fifo); 2225f8ae8e2SHervé Poussineau } 2235f8ae8e2SHervé Poussineau 2244964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 2254964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 2264964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 2274964e18eSKevin Wolf 2285f8ae8e2SHervé Poussineau st0 = floppy_recv(); 2295f8ae8e2SHervé Poussineau if (st0 != expected_st0) { 2305f8ae8e2SHervé Poussineau ret = 1; 2315f8ae8e2SHervé Poussineau } 2325f8ae8e2SHervé Poussineau 2335f8ae8e2SHervé Poussineau floppy_recv(); 2345f8ae8e2SHervé Poussineau floppy_recv(); 2355f8ae8e2SHervé Poussineau floppy_recv(); 2365f8ae8e2SHervé Poussineau floppy_recv(); 2375f8ae8e2SHervé Poussineau floppy_recv(); 2384964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 2395f8ae8e2SHervé Poussineau floppy_recv(); 2405f8ae8e2SHervé Poussineau 2414964e18eSKevin Wolf /* Check that we're back in command phase */ 2424964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 2434964e18eSKevin Wolf assert_bit_clear(msr, BUSY | DIO); 2444964e18eSKevin Wolf assert_bit_set(msr, RQM); 2454964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 2464964e18eSKevin Wolf 2475f8ae8e2SHervé Poussineau return ret; 2485f8ae8e2SHervé Poussineau } 2495f8ae8e2SHervé Poussineau 250c3cdc1b0SKevin Wolf static void send_seek(int cyl) 25193e9eb68SKevin Wolf { 25293e9eb68SKevin Wolf int drive = 0; 25393e9eb68SKevin Wolf int head = 0; 25493e9eb68SKevin Wolf 25593e9eb68SKevin Wolf floppy_send(CMD_SEEK); 25693e9eb68SKevin Wolf floppy_send(head << 2 | drive); 25793e9eb68SKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 25893e9eb68SKevin Wolf floppy_send(cyl); 259c3cdc1b0SKevin Wolf ack_irq(NULL); 26093e9eb68SKevin Wolf } 26193e9eb68SKevin Wolf 2627cd33161SPavel Hrdina static uint8_t cmos_read(uint8_t reg) 2637cd33161SPavel Hrdina { 2647cd33161SPavel Hrdina outb(base + 0, reg); 2657cd33161SPavel Hrdina return inb(base + 1); 2667cd33161SPavel Hrdina } 2677cd33161SPavel Hrdina 2687cd33161SPavel Hrdina static void test_cmos(void) 2697cd33161SPavel Hrdina { 2707cd33161SPavel Hrdina uint8_t cmos; 2717cd33161SPavel Hrdina 2727cd33161SPavel Hrdina cmos = cmos_read(CMOS_FLOPPY); 27362c76250SJohn Snow g_assert(cmos == 0x40 || cmos == 0x50); 2747cd33161SPavel Hrdina } 2757cd33161SPavel Hrdina 2767cd33161SPavel Hrdina static void test_no_media_on_start(void) 2777cd33161SPavel Hrdina { 2787cd33161SPavel Hrdina uint8_t dir; 2797cd33161SPavel Hrdina 2807cd33161SPavel Hrdina /* Media changed bit must be set all time after start if there is 2817cd33161SPavel Hrdina * no media in drive. */ 2827cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2837cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2847cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2857cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 286c3cdc1b0SKevin Wolf send_seek(1); 2877cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2887cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2897cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2907cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2917cd33161SPavel Hrdina } 2927cd33161SPavel Hrdina 2938b9ef60dSPavel Hrdina static void test_read_without_media(void) 2948b9ef60dSPavel Hrdina { 2958b9ef60dSPavel Hrdina uint8_t ret; 2968b9ef60dSPavel Hrdina 2976f442fe8SHervé Poussineau ret = send_read_command(CMD_READ); 2988b9ef60dSPavel Hrdina g_assert(ret == 0); 2998b9ef60dSPavel Hrdina } 3008b9ef60dSPavel Hrdina 3011f507913SHervé Poussineau static void test_media_insert(void) 30293e9eb68SKevin Wolf { 30393e9eb68SKevin Wolf uint8_t dir; 30493e9eb68SKevin Wolf 3057cd33161SPavel Hrdina /* Insert media in drive. DSKCHK should not be reset until a step pulse 3067cd33161SPavel Hrdina * is sent. */ 307ccb61bddSEric Blake qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{" 308ccb61bddSEric Blake " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", 3090d1aa05eSStefan Hajnoczi test_image); 3107cd33161SPavel Hrdina 3117cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3127cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 3137cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3147cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 3157cd33161SPavel Hrdina 316c3cdc1b0SKevin Wolf send_seek(0); 31759240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 31859240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 31959240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 32059240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 32159240c34SPavel Hrdina 32259240c34SPavel Hrdina /* Step to next track should clear DSKCHG bit. */ 323c3cdc1b0SKevin Wolf send_seek(1); 3247cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3257cd33161SPavel Hrdina assert_bit_clear(dir, DSKCHG); 32693e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 32793e9eb68SKevin Wolf assert_bit_clear(dir, DSKCHG); 3281f507913SHervé Poussineau } 3291f507913SHervé Poussineau 3301f507913SHervé Poussineau static void test_media_change(void) 3311f507913SHervé Poussineau { 3321f507913SHervé Poussineau uint8_t dir; 3331f507913SHervé Poussineau 3341f507913SHervé Poussineau test_media_insert(); 33593e9eb68SKevin Wolf 33693e9eb68SKevin Wolf /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't 33793e9eb68SKevin Wolf * reset the bit. */ 3380d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'eject', 'arguments':{" 339ccb61bddSEric Blake " 'id':'floppy0' }}"); 34093e9eb68SKevin Wolf 34193e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 34293e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 34393e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 34493e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 34593e9eb68SKevin Wolf 346c3cdc1b0SKevin Wolf send_seek(0); 34759240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 34859240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 34959240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 35059240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 35159240c34SPavel Hrdina 352c3cdc1b0SKevin Wolf send_seek(1); 35393e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 35493e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 35593e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 35693e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 35793e9eb68SKevin Wolf } 35893e9eb68SKevin Wolf 359b3ce604eSPavel Hrdina static void test_sense_interrupt(void) 360b3ce604eSPavel Hrdina { 361b3ce604eSPavel Hrdina int drive = 0; 362b3ce604eSPavel Hrdina int head = 0; 363b3ce604eSPavel Hrdina int cyl = 0; 364b3ce604eSPavel Hrdina int ret = 0; 365b3ce604eSPavel Hrdina 366b3ce604eSPavel Hrdina floppy_send(CMD_SENSE_INT); 367b3ce604eSPavel Hrdina ret = floppy_recv(); 368b3ce604eSPavel Hrdina g_assert(ret == 0x80); 369b3ce604eSPavel Hrdina 370b3ce604eSPavel Hrdina floppy_send(CMD_SEEK); 371b3ce604eSPavel Hrdina floppy_send(head << 2 | drive); 372b3ce604eSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 373b3ce604eSPavel Hrdina floppy_send(cyl); 374b3ce604eSPavel Hrdina 375b3ce604eSPavel Hrdina floppy_send(CMD_SENSE_INT); 376b3ce604eSPavel Hrdina ret = floppy_recv(); 377b3ce604eSPavel Hrdina g_assert(ret == 0x20); 378b3ce604eSPavel Hrdina floppy_recv(); 379b3ce604eSPavel Hrdina } 380b3ce604eSPavel Hrdina 38198272dbbSPavel Hrdina static void test_relative_seek(void) 38298272dbbSPavel Hrdina { 38398272dbbSPavel Hrdina uint8_t drive = 0; 38498272dbbSPavel Hrdina uint8_t head = 0; 38598272dbbSPavel Hrdina uint8_t cyl = 1; 386c3cdc1b0SKevin Wolf uint8_t pcn; 38798272dbbSPavel Hrdina 38898272dbbSPavel Hrdina /* Send seek to track 0 */ 389c3cdc1b0SKevin Wolf send_seek(0); 39098272dbbSPavel Hrdina 39198272dbbSPavel Hrdina /* Send relative seek to increase track by 1 */ 39298272dbbSPavel Hrdina floppy_send(CMD_RELATIVE_SEEK_IN); 39398272dbbSPavel Hrdina floppy_send(head << 2 | drive); 39498272dbbSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 39598272dbbSPavel Hrdina floppy_send(cyl); 39698272dbbSPavel Hrdina 397c3cdc1b0SKevin Wolf ack_irq(&pcn); 398c3cdc1b0SKevin Wolf g_assert(pcn == 1); 39998272dbbSPavel Hrdina 40098272dbbSPavel Hrdina /* Send relative seek to decrease track by 1 */ 40198272dbbSPavel Hrdina floppy_send(CMD_RELATIVE_SEEK_OUT); 40298272dbbSPavel Hrdina floppy_send(head << 2 | drive); 40398272dbbSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 40498272dbbSPavel Hrdina floppy_send(cyl); 40598272dbbSPavel Hrdina 406c3cdc1b0SKevin Wolf ack_irq(&pcn); 407c3cdc1b0SKevin Wolf g_assert(pcn == 0); 40898272dbbSPavel Hrdina } 40998272dbbSPavel Hrdina 41067f194bdSKevin Wolf static void test_read_id(void) 41167f194bdSKevin Wolf { 41267f194bdSKevin Wolf uint8_t drive = 0; 41367f194bdSKevin Wolf uint8_t head = 0; 41467f194bdSKevin Wolf uint8_t cyl; 41567f194bdSKevin Wolf uint8_t st0; 4164964e18eSKevin Wolf uint8_t msr; 41767f194bdSKevin Wolf 41867f194bdSKevin Wolf /* Seek to track 0 and check with READ ID */ 41967f194bdSKevin Wolf send_seek(0); 42067f194bdSKevin Wolf 42167f194bdSKevin Wolf floppy_send(CMD_READ_ID); 42267f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 42367f194bdSKevin Wolf floppy_send(head << 2 | drive); 42467f194bdSKevin Wolf 4254964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4264964e18eSKevin Wolf if (!get_irq(FLOPPY_IRQ)) { 4274964e18eSKevin Wolf assert_bit_set(msr, BUSY); 4284964e18eSKevin Wolf assert_bit_clear(msr, RQM); 4294964e18eSKevin Wolf } 4304964e18eSKevin Wolf 43167f194bdSKevin Wolf while (!get_irq(FLOPPY_IRQ)) { 43267f194bdSKevin Wolf /* qemu involves a timer with READ ID... */ 43367f194bdSKevin Wolf clock_step(1000000000LL / 50); 43467f194bdSKevin Wolf } 43567f194bdSKevin Wolf 4364964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4374964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 4384964e18eSKevin Wolf 43967f194bdSKevin Wolf st0 = floppy_recv(); 44067f194bdSKevin Wolf floppy_recv(); 44167f194bdSKevin Wolf floppy_recv(); 44267f194bdSKevin Wolf cyl = floppy_recv(); 44367f194bdSKevin Wolf head = floppy_recv(); 44467f194bdSKevin Wolf floppy_recv(); 4454964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 44667f194bdSKevin Wolf floppy_recv(); 4474964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 44867f194bdSKevin Wolf 44967f194bdSKevin Wolf g_assert_cmpint(cyl, ==, 0); 45067f194bdSKevin Wolf g_assert_cmpint(head, ==, 0); 45167f194bdSKevin Wolf g_assert_cmpint(st0, ==, head << 2); 45267f194bdSKevin Wolf 45367f194bdSKevin Wolf /* Seek to track 8 on head 1 and check with READ ID */ 45467f194bdSKevin Wolf head = 1; 45567f194bdSKevin Wolf cyl = 8; 45667f194bdSKevin Wolf 45767f194bdSKevin Wolf floppy_send(CMD_SEEK); 45867f194bdSKevin Wolf floppy_send(head << 2 | drive); 45967f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 46067f194bdSKevin Wolf floppy_send(cyl); 46167f194bdSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 46267f194bdSKevin Wolf ack_irq(NULL); 46367f194bdSKevin Wolf 46467f194bdSKevin Wolf floppy_send(CMD_READ_ID); 46567f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 46667f194bdSKevin Wolf floppy_send(head << 2 | drive); 46767f194bdSKevin Wolf 4684964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4694964e18eSKevin Wolf if (!get_irq(FLOPPY_IRQ)) { 4704964e18eSKevin Wolf assert_bit_set(msr, BUSY); 4714964e18eSKevin Wolf assert_bit_clear(msr, RQM); 4724964e18eSKevin Wolf } 4734964e18eSKevin Wolf 47467f194bdSKevin Wolf while (!get_irq(FLOPPY_IRQ)) { 47567f194bdSKevin Wolf /* qemu involves a timer with READ ID... */ 47667f194bdSKevin Wolf clock_step(1000000000LL / 50); 47767f194bdSKevin Wolf } 47867f194bdSKevin Wolf 4794964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4804964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 4814964e18eSKevin Wolf 48267f194bdSKevin Wolf st0 = floppy_recv(); 48367f194bdSKevin Wolf floppy_recv(); 48467f194bdSKevin Wolf floppy_recv(); 48567f194bdSKevin Wolf cyl = floppy_recv(); 48667f194bdSKevin Wolf head = floppy_recv(); 48767f194bdSKevin Wolf floppy_recv(); 4884964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 48967f194bdSKevin Wolf floppy_recv(); 4904964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 49167f194bdSKevin Wolf 49267f194bdSKevin Wolf g_assert_cmpint(cyl, ==, 8); 49367f194bdSKevin Wolf g_assert_cmpint(head, ==, 1); 49467f194bdSKevin Wolf g_assert_cmpint(st0, ==, head << 2); 49567f194bdSKevin Wolf } 49667f194bdSKevin Wolf 4975f8ae8e2SHervé Poussineau static void test_read_no_dma_1(void) 4985f8ae8e2SHervé Poussineau { 4995f8ae8e2SHervé Poussineau uint8_t ret; 5005f8ae8e2SHervé Poussineau 5015f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 5025f8ae8e2SHervé Poussineau send_seek(0); 503075f5532SHervé Poussineau ret = send_read_no_dma_command(1, 0x04); 5045f8ae8e2SHervé Poussineau g_assert(ret == 0); 5055f8ae8e2SHervé Poussineau } 5065f8ae8e2SHervé Poussineau 5075f8ae8e2SHervé Poussineau static void test_read_no_dma_18(void) 5085f8ae8e2SHervé Poussineau { 5095f8ae8e2SHervé Poussineau uint8_t ret; 5105f8ae8e2SHervé Poussineau 5115f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 5125f8ae8e2SHervé Poussineau send_seek(0); 513075f5532SHervé Poussineau ret = send_read_no_dma_command(18, 0x04); 5145f8ae8e2SHervé Poussineau g_assert(ret == 0); 5155f8ae8e2SHervé Poussineau } 5165f8ae8e2SHervé Poussineau 5175f8ae8e2SHervé Poussineau static void test_read_no_dma_19(void) 5185f8ae8e2SHervé Poussineau { 5195f8ae8e2SHervé Poussineau uint8_t ret; 5205f8ae8e2SHervé Poussineau 5215f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 5225f8ae8e2SHervé Poussineau send_seek(0); 5235f8ae8e2SHervé Poussineau ret = send_read_no_dma_command(19, 0x20); 5245f8ae8e2SHervé Poussineau g_assert(ret == 0); 5255f8ae8e2SHervé Poussineau } 5265f8ae8e2SHervé Poussineau 5276f442fe8SHervé Poussineau static void test_verify(void) 5286f442fe8SHervé Poussineau { 5296f442fe8SHervé Poussineau uint8_t ret; 5306f442fe8SHervé Poussineau 5316f442fe8SHervé Poussineau ret = send_read_command(CMD_VERIFY); 5326f442fe8SHervé Poussineau g_assert(ret == 0); 5336f442fe8SHervé Poussineau } 5346f442fe8SHervé Poussineau 5353359847eSBlue Swirl /* success if no crash or abort */ 5363359847eSBlue Swirl static void fuzz_registers(void) 5373359847eSBlue Swirl { 5383359847eSBlue Swirl unsigned int i; 5393359847eSBlue Swirl 5403359847eSBlue Swirl for (i = 0; i < 1000; i++) { 5413359847eSBlue Swirl uint8_t reg, val; 5423359847eSBlue Swirl 5433359847eSBlue Swirl reg = (uint8_t)g_test_rand_int_range(0, 8); 5443359847eSBlue Swirl val = (uint8_t)g_test_rand_int_range(0, 256); 5453359847eSBlue Swirl 5463359847eSBlue Swirl outb(FLOPPY_BASE + reg, val); 5473359847eSBlue Swirl inb(FLOPPY_BASE + reg); 5483359847eSBlue Swirl } 5493359847eSBlue Swirl } 5503359847eSBlue Swirl 551cc20926eSPhilippe Mathieu-Daudé static bool qtest_check_clang_sanitizer(void) 552cc20926eSPhilippe Mathieu-Daudé { 553*638466f7SMarc-André Lureau #ifdef QEMU_SANITIZE_ADDRESS 554cc20926eSPhilippe Mathieu-Daudé return true; 555cc20926eSPhilippe Mathieu-Daudé #else 556cc20926eSPhilippe Mathieu-Daudé g_test_skip("QEMU not configured using --enable-sanitizers"); 557cc20926eSPhilippe Mathieu-Daudé return false; 558cc20926eSPhilippe Mathieu-Daudé #endif 559cc20926eSPhilippe Mathieu-Daudé } 560cc20926eSPhilippe Mathieu-Daudé static void test_cve_2021_20196(void) 561cc20926eSPhilippe Mathieu-Daudé { 562cc20926eSPhilippe Mathieu-Daudé QTestState *s; 563cc20926eSPhilippe Mathieu-Daudé 564cc20926eSPhilippe Mathieu-Daudé if (!qtest_check_clang_sanitizer()) { 565cc20926eSPhilippe Mathieu-Daudé return; 566cc20926eSPhilippe Mathieu-Daudé } 567cc20926eSPhilippe Mathieu-Daudé 568cc20926eSPhilippe Mathieu-Daudé s = qtest_initf("-nographic -m 32M -nodefaults " DRIVE_FLOPPY_BLANK); 569cc20926eSPhilippe Mathieu-Daudé 570cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0500); 571cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00); 572cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00); 573cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000); 574cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00); 575cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0400); 576cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000); 577cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000); 578cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00); 579cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x01); 580cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0500); 581cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00); 582cc20926eSPhilippe Mathieu-Daudé qtest_quit(s); 583cc20926eSPhilippe Mathieu-Daudé } 584cc20926eSPhilippe Mathieu-Daudé 58593e9eb68SKevin Wolf int main(int argc, char **argv) 58693e9eb68SKevin Wolf { 58793e9eb68SKevin Wolf int fd; 58893e9eb68SKevin Wolf int ret; 58993e9eb68SKevin Wolf 59093e9eb68SKevin Wolf /* Create a temporary raw image */ 59193e9eb68SKevin Wolf fd = mkstemp(test_image); 59293e9eb68SKevin Wolf g_assert(fd >= 0); 59393e9eb68SKevin Wolf ret = ftruncate(fd, TEST_IMAGE_SIZE); 59493e9eb68SKevin Wolf g_assert(ret == 0); 59593e9eb68SKevin Wolf close(fd); 59693e9eb68SKevin Wolf 59793e9eb68SKevin Wolf /* Run the tests */ 59893e9eb68SKevin Wolf g_test_init(&argc, &argv, NULL); 59993e9eb68SKevin Wolf 600fedcc379SDr. David Alan Gilbert qtest_start("-machine pc -device floppy,id=floppy0"); 60193e9eb68SKevin Wolf qtest_irq_intercept_in(global_qtest, "ioapic"); 6027cd33161SPavel Hrdina qtest_add_func("/fdc/cmos", test_cmos); 6037cd33161SPavel Hrdina qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); 6048b9ef60dSPavel Hrdina qtest_add_func("/fdc/read_without_media", test_read_without_media); 60593e9eb68SKevin Wolf qtest_add_func("/fdc/media_change", test_media_change); 606b3ce604eSPavel Hrdina qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); 60798272dbbSPavel Hrdina qtest_add_func("/fdc/relative_seek", test_relative_seek); 60867f194bdSKevin Wolf qtest_add_func("/fdc/read_id", test_read_id); 6096f442fe8SHervé Poussineau qtest_add_func("/fdc/verify", test_verify); 61044212dccSHervé Poussineau qtest_add_func("/fdc/media_insert", test_media_insert); 6115f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); 6125f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18); 6135f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19); 6143359847eSBlue Swirl qtest_add_func("/fdc/fuzz-registers", fuzz_registers); 615cc20926eSPhilippe Mathieu-Daudé qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196); 61693e9eb68SKevin Wolf 61793e9eb68SKevin Wolf ret = g_test_run(); 61893e9eb68SKevin Wolf 61993e9eb68SKevin Wolf /* Cleanup */ 6201d9358e6SMarkus Armbruster qtest_end(); 62193e9eb68SKevin Wolf unlink(test_image); 62293e9eb68SKevin Wolf 62393e9eb68SKevin Wolf return ret; 62493e9eb68SKevin Wolf } 625