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 2893e9eb68SKevin Wolf #include "libqtest.h" 2993e9eb68SKevin Wolf #include "qemu-common.h" 3093e9eb68SKevin Wolf 3193e9eb68SKevin Wolf #define TEST_IMAGE_SIZE 1440 * 1024 3293e9eb68SKevin Wolf 3393e9eb68SKevin Wolf #define FLOPPY_BASE 0x3f0 3493e9eb68SKevin Wolf #define FLOPPY_IRQ 6 3593e9eb68SKevin Wolf 3693e9eb68SKevin Wolf enum { 3793e9eb68SKevin Wolf reg_sra = 0x0, 3893e9eb68SKevin Wolf reg_srb = 0x1, 3993e9eb68SKevin Wolf reg_dor = 0x2, 4093e9eb68SKevin Wolf reg_msr = 0x4, 4193e9eb68SKevin Wolf reg_dsr = 0x4, 4293e9eb68SKevin Wolf reg_fifo = 0x5, 4393e9eb68SKevin Wolf reg_dir = 0x7, 4493e9eb68SKevin Wolf }; 4593e9eb68SKevin Wolf 4693e9eb68SKevin Wolf enum { 4793e9eb68SKevin Wolf CMD_SENSE_INT = 0x08, 4867f194bdSKevin Wolf CMD_READ_ID = 0x0a, 4993e9eb68SKevin Wolf CMD_SEEK = 0x0f, 506f442fe8SHervé Poussineau CMD_VERIFY = 0x16, 518b9ef60dSPavel Hrdina CMD_READ = 0xe6, 5298272dbbSPavel Hrdina CMD_RELATIVE_SEEK_OUT = 0x8f, 5398272dbbSPavel Hrdina CMD_RELATIVE_SEEK_IN = 0xcf, 5493e9eb68SKevin Wolf }; 5593e9eb68SKevin Wolf 5693e9eb68SKevin Wolf enum { 575f8ae8e2SHervé Poussineau BUSY = 0x10, 585f8ae8e2SHervé Poussineau NONDMA = 0x20, 5993e9eb68SKevin Wolf RQM = 0x80, 6093e9eb68SKevin Wolf DIO = 0x40, 6193e9eb68SKevin Wolf 6293e9eb68SKevin Wolf DSKCHG = 0x80, 6393e9eb68SKevin Wolf }; 6493e9eb68SKevin Wolf 65748bfb4eSStefan Weil static char test_image[] = "/tmp/qtest.XXXXXX"; 6693e9eb68SKevin Wolf 6793e9eb68SKevin Wolf #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) 6893e9eb68SKevin Wolf #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) 6993e9eb68SKevin Wolf 707cd33161SPavel Hrdina static uint8_t base = 0x70; 717cd33161SPavel Hrdina 727cd33161SPavel Hrdina enum { 737cd33161SPavel Hrdina CMOS_FLOPPY = 0x10, 747cd33161SPavel Hrdina }; 757cd33161SPavel Hrdina 7693e9eb68SKevin Wolf static void floppy_send(uint8_t byte) 7793e9eb68SKevin Wolf { 7893e9eb68SKevin Wolf uint8_t msr; 7993e9eb68SKevin Wolf 8093e9eb68SKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 8193e9eb68SKevin Wolf assert_bit_set(msr, RQM); 8293e9eb68SKevin Wolf assert_bit_clear(msr, DIO); 8393e9eb68SKevin Wolf 8493e9eb68SKevin Wolf outb(FLOPPY_BASE + reg_fifo, byte); 8593e9eb68SKevin Wolf } 8693e9eb68SKevin Wolf 8793e9eb68SKevin Wolf static uint8_t floppy_recv(void) 8893e9eb68SKevin Wolf { 8993e9eb68SKevin Wolf uint8_t msr; 9093e9eb68SKevin Wolf 9193e9eb68SKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 9293e9eb68SKevin Wolf assert_bit_set(msr, RQM | DIO); 9393e9eb68SKevin Wolf 9493e9eb68SKevin Wolf return inb(FLOPPY_BASE + reg_fifo); 9593e9eb68SKevin Wolf } 9693e9eb68SKevin Wolf 97c3cdc1b0SKevin Wolf /* pcn: Present Cylinder Number */ 98c3cdc1b0SKevin Wolf static void ack_irq(uint8_t *pcn) 9993e9eb68SKevin Wolf { 10098272dbbSPavel Hrdina uint8_t ret; 10198272dbbSPavel Hrdina 10293e9eb68SKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 10393e9eb68SKevin Wolf floppy_send(CMD_SENSE_INT); 10493e9eb68SKevin Wolf floppy_recv(); 10598272dbbSPavel Hrdina 106c3cdc1b0SKevin Wolf ret = floppy_recv(); 107c3cdc1b0SKevin Wolf if (pcn != NULL) { 108c3cdc1b0SKevin Wolf *pcn = ret; 109c3cdc1b0SKevin Wolf } 110c3cdc1b0SKevin Wolf 111c3cdc1b0SKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 11293e9eb68SKevin Wolf } 11393e9eb68SKevin Wolf 1146f442fe8SHervé Poussineau static uint8_t send_read_command(uint8_t cmd) 1158b9ef60dSPavel Hrdina { 1168b9ef60dSPavel Hrdina uint8_t drive = 0; 1178b9ef60dSPavel Hrdina uint8_t head = 0; 1188b9ef60dSPavel Hrdina uint8_t cyl = 0; 1198b9ef60dSPavel Hrdina uint8_t sect_addr = 1; 1208b9ef60dSPavel Hrdina uint8_t sect_size = 2; 1218b9ef60dSPavel Hrdina uint8_t eot = 1; 1228b9ef60dSPavel Hrdina uint8_t gap = 0x1b; 1238b9ef60dSPavel Hrdina uint8_t gpl = 0xff; 1248b9ef60dSPavel Hrdina 1258b9ef60dSPavel Hrdina uint8_t msr = 0; 1268b9ef60dSPavel Hrdina uint8_t st0; 1278b9ef60dSPavel Hrdina 1288b9ef60dSPavel Hrdina uint8_t ret = 0; 1298b9ef60dSPavel Hrdina 1306f442fe8SHervé Poussineau floppy_send(cmd); 1318b9ef60dSPavel Hrdina floppy_send(head << 2 | drive); 1328b9ef60dSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 1338b9ef60dSPavel Hrdina floppy_send(cyl); 1348b9ef60dSPavel Hrdina floppy_send(head); 1358b9ef60dSPavel Hrdina floppy_send(sect_addr); 1368b9ef60dSPavel Hrdina floppy_send(sect_size); 1378b9ef60dSPavel Hrdina floppy_send(eot); 1388b9ef60dSPavel Hrdina floppy_send(gap); 1398b9ef60dSPavel Hrdina floppy_send(gpl); 1408b9ef60dSPavel Hrdina 1418b9ef60dSPavel Hrdina uint8_t i = 0; 1428b9ef60dSPavel Hrdina uint8_t n = 2; 1438b9ef60dSPavel Hrdina for (; i < n; i++) { 1448b9ef60dSPavel Hrdina msr = inb(FLOPPY_BASE + reg_msr); 1458b9ef60dSPavel Hrdina if (msr == 0xd0) { 1468b9ef60dSPavel Hrdina break; 1478b9ef60dSPavel Hrdina } 1488b9ef60dSPavel Hrdina sleep(1); 1498b9ef60dSPavel Hrdina } 1508b9ef60dSPavel Hrdina 1518b9ef60dSPavel Hrdina if (i >= n) { 1528b9ef60dSPavel Hrdina return 1; 1538b9ef60dSPavel Hrdina } 1548b9ef60dSPavel Hrdina 1558b9ef60dSPavel Hrdina st0 = floppy_recv(); 156075f5532SHervé Poussineau if (st0 != 0x40) { 1578b9ef60dSPavel Hrdina ret = 1; 1588b9ef60dSPavel Hrdina } 1598b9ef60dSPavel Hrdina 1608b9ef60dSPavel Hrdina floppy_recv(); 1618b9ef60dSPavel Hrdina floppy_recv(); 1628b9ef60dSPavel Hrdina floppy_recv(); 1638b9ef60dSPavel Hrdina floppy_recv(); 1648b9ef60dSPavel Hrdina floppy_recv(); 1658b9ef60dSPavel Hrdina floppy_recv(); 1668b9ef60dSPavel Hrdina 1678b9ef60dSPavel Hrdina return ret; 1688b9ef60dSPavel Hrdina } 1698b9ef60dSPavel Hrdina 1705f8ae8e2SHervé Poussineau static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0) 1715f8ae8e2SHervé Poussineau { 1725f8ae8e2SHervé Poussineau uint8_t drive = 0; 1735f8ae8e2SHervé Poussineau uint8_t head = 0; 1745f8ae8e2SHervé Poussineau uint8_t cyl = 0; 1755f8ae8e2SHervé Poussineau uint8_t sect_addr = 1; 1765f8ae8e2SHervé Poussineau uint8_t sect_size = 2; 1775f8ae8e2SHervé Poussineau uint8_t eot = nb_sect; 1785f8ae8e2SHervé Poussineau uint8_t gap = 0x1b; 1795f8ae8e2SHervé Poussineau uint8_t gpl = 0xff; 1805f8ae8e2SHervé Poussineau 1815f8ae8e2SHervé Poussineau uint8_t msr = 0; 1825f8ae8e2SHervé Poussineau uint8_t st0; 1835f8ae8e2SHervé Poussineau 1845f8ae8e2SHervé Poussineau uint8_t ret = 0; 1855f8ae8e2SHervé Poussineau 1865f8ae8e2SHervé Poussineau floppy_send(CMD_READ); 1875f8ae8e2SHervé Poussineau floppy_send(head << 2 | drive); 1885f8ae8e2SHervé Poussineau g_assert(!get_irq(FLOPPY_IRQ)); 1895f8ae8e2SHervé Poussineau floppy_send(cyl); 1905f8ae8e2SHervé Poussineau floppy_send(head); 1915f8ae8e2SHervé Poussineau floppy_send(sect_addr); 1925f8ae8e2SHervé Poussineau floppy_send(sect_size); 1935f8ae8e2SHervé Poussineau floppy_send(eot); 1945f8ae8e2SHervé Poussineau floppy_send(gap); 1955f8ae8e2SHervé Poussineau floppy_send(gpl); 1965f8ae8e2SHervé Poussineau 1975f8ae8e2SHervé Poussineau uint16_t i = 0; 1985f8ae8e2SHervé Poussineau uint8_t n = 2; 1995f8ae8e2SHervé Poussineau for (; i < n; i++) { 2005f8ae8e2SHervé Poussineau msr = inb(FLOPPY_BASE + reg_msr); 2015f8ae8e2SHervé Poussineau if (msr == (BUSY | NONDMA | DIO | RQM)) { 2025f8ae8e2SHervé Poussineau break; 2035f8ae8e2SHervé Poussineau } 2045f8ae8e2SHervé Poussineau sleep(1); 2055f8ae8e2SHervé Poussineau } 2065f8ae8e2SHervé Poussineau 2075f8ae8e2SHervé Poussineau if (i >= n) { 2085f8ae8e2SHervé Poussineau return 1; 2095f8ae8e2SHervé Poussineau } 2105f8ae8e2SHervé Poussineau 2115f8ae8e2SHervé Poussineau /* Non-DMA mode */ 2125f8ae8e2SHervé Poussineau for (i = 0; i < 512 * 2 * nb_sect; i++) { 2135f8ae8e2SHervé Poussineau msr = inb(FLOPPY_BASE + reg_msr); 2145f8ae8e2SHervé Poussineau assert_bit_set(msr, BUSY | RQM | DIO); 2155f8ae8e2SHervé Poussineau inb(FLOPPY_BASE + reg_fifo); 2165f8ae8e2SHervé Poussineau } 2175f8ae8e2SHervé Poussineau 2184964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 2194964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 2204964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 2214964e18eSKevin Wolf 2225f8ae8e2SHervé Poussineau st0 = floppy_recv(); 2235f8ae8e2SHervé Poussineau if (st0 != expected_st0) { 2245f8ae8e2SHervé Poussineau ret = 1; 2255f8ae8e2SHervé Poussineau } 2265f8ae8e2SHervé Poussineau 2275f8ae8e2SHervé Poussineau floppy_recv(); 2285f8ae8e2SHervé Poussineau floppy_recv(); 2295f8ae8e2SHervé Poussineau floppy_recv(); 2305f8ae8e2SHervé Poussineau floppy_recv(); 2315f8ae8e2SHervé Poussineau floppy_recv(); 2324964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 2335f8ae8e2SHervé Poussineau floppy_recv(); 2345f8ae8e2SHervé Poussineau 2354964e18eSKevin Wolf /* Check that we're back in command phase */ 2364964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 2374964e18eSKevin Wolf assert_bit_clear(msr, BUSY | DIO); 2384964e18eSKevin Wolf assert_bit_set(msr, RQM); 2394964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 2404964e18eSKevin Wolf 2415f8ae8e2SHervé Poussineau return ret; 2425f8ae8e2SHervé Poussineau } 2435f8ae8e2SHervé Poussineau 244c3cdc1b0SKevin Wolf static void send_seek(int cyl) 24593e9eb68SKevin Wolf { 24693e9eb68SKevin Wolf int drive = 0; 24793e9eb68SKevin Wolf int head = 0; 24893e9eb68SKevin Wolf 24993e9eb68SKevin Wolf floppy_send(CMD_SEEK); 25093e9eb68SKevin Wolf floppy_send(head << 2 | drive); 25193e9eb68SKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 25293e9eb68SKevin Wolf floppy_send(cyl); 253c3cdc1b0SKevin Wolf ack_irq(NULL); 25493e9eb68SKevin Wolf } 25593e9eb68SKevin Wolf 2567cd33161SPavel Hrdina static uint8_t cmos_read(uint8_t reg) 2577cd33161SPavel Hrdina { 2587cd33161SPavel Hrdina outb(base + 0, reg); 2597cd33161SPavel Hrdina return inb(base + 1); 2607cd33161SPavel Hrdina } 2617cd33161SPavel Hrdina 2627cd33161SPavel Hrdina static void test_cmos(void) 2637cd33161SPavel Hrdina { 2647cd33161SPavel Hrdina uint8_t cmos; 2657cd33161SPavel Hrdina 2667cd33161SPavel Hrdina cmos = cmos_read(CMOS_FLOPPY); 26762c76250SJohn Snow g_assert(cmos == 0x40 || cmos == 0x50); 2687cd33161SPavel Hrdina } 2697cd33161SPavel Hrdina 2707cd33161SPavel Hrdina static void test_no_media_on_start(void) 2717cd33161SPavel Hrdina { 2727cd33161SPavel Hrdina uint8_t dir; 2737cd33161SPavel Hrdina 2747cd33161SPavel Hrdina /* Media changed bit must be set all time after start if there is 2757cd33161SPavel Hrdina * no media in drive. */ 2767cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2777cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2787cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2797cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 280c3cdc1b0SKevin Wolf send_seek(1); 2817cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2827cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2837cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 2847cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 2857cd33161SPavel Hrdina } 2867cd33161SPavel Hrdina 2878b9ef60dSPavel Hrdina static void test_read_without_media(void) 2888b9ef60dSPavel Hrdina { 2898b9ef60dSPavel Hrdina uint8_t ret; 2908b9ef60dSPavel Hrdina 2916f442fe8SHervé Poussineau ret = send_read_command(CMD_READ); 2928b9ef60dSPavel Hrdina g_assert(ret == 0); 2938b9ef60dSPavel Hrdina } 2948b9ef60dSPavel Hrdina 2951f507913SHervé Poussineau static void test_media_insert(void) 29693e9eb68SKevin Wolf { 29793e9eb68SKevin Wolf uint8_t dir; 29893e9eb68SKevin Wolf 2997cd33161SPavel Hrdina /* Insert media in drive. DSKCHK should not be reset until a step pulse 3007cd33161SPavel Hrdina * is sent. */ 301*ccb61bddSEric Blake qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{" 302*ccb61bddSEric Blake " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", 3030d1aa05eSStefan Hajnoczi test_image); 3047cd33161SPavel Hrdina 3057cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3067cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 3077cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3087cd33161SPavel Hrdina assert_bit_set(dir, DSKCHG); 3097cd33161SPavel Hrdina 310c3cdc1b0SKevin Wolf send_seek(0); 31159240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 31259240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 31359240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 31459240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 31559240c34SPavel Hrdina 31659240c34SPavel Hrdina /* Step to next track should clear DSKCHG bit. */ 317c3cdc1b0SKevin Wolf send_seek(1); 3187cd33161SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 3197cd33161SPavel Hrdina assert_bit_clear(dir, DSKCHG); 32093e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 32193e9eb68SKevin Wolf assert_bit_clear(dir, DSKCHG); 3221f507913SHervé Poussineau } 3231f507913SHervé Poussineau 3241f507913SHervé Poussineau static void test_media_change(void) 3251f507913SHervé Poussineau { 3261f507913SHervé Poussineau uint8_t dir; 3271f507913SHervé Poussineau 3281f507913SHervé Poussineau test_media_insert(); 32993e9eb68SKevin Wolf 33093e9eb68SKevin Wolf /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't 33193e9eb68SKevin Wolf * reset the bit. */ 3320d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'eject', 'arguments':{" 333*ccb61bddSEric Blake " 'id':'floppy0' }}"); 33493e9eb68SKevin Wolf 33593e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 33693e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 33793e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 33893e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 33993e9eb68SKevin Wolf 340c3cdc1b0SKevin Wolf send_seek(0); 34159240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 34259240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 34359240c34SPavel Hrdina dir = inb(FLOPPY_BASE + reg_dir); 34459240c34SPavel Hrdina assert_bit_set(dir, DSKCHG); 34559240c34SPavel Hrdina 346c3cdc1b0SKevin Wolf send_seek(1); 34793e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 34893e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 34993e9eb68SKevin Wolf dir = inb(FLOPPY_BASE + reg_dir); 35093e9eb68SKevin Wolf assert_bit_set(dir, DSKCHG); 35193e9eb68SKevin Wolf } 35293e9eb68SKevin Wolf 353b3ce604eSPavel Hrdina static void test_sense_interrupt(void) 354b3ce604eSPavel Hrdina { 355b3ce604eSPavel Hrdina int drive = 0; 356b3ce604eSPavel Hrdina int head = 0; 357b3ce604eSPavel Hrdina int cyl = 0; 358b3ce604eSPavel Hrdina int ret = 0; 359b3ce604eSPavel Hrdina 360b3ce604eSPavel Hrdina floppy_send(CMD_SENSE_INT); 361b3ce604eSPavel Hrdina ret = floppy_recv(); 362b3ce604eSPavel Hrdina g_assert(ret == 0x80); 363b3ce604eSPavel Hrdina 364b3ce604eSPavel Hrdina floppy_send(CMD_SEEK); 365b3ce604eSPavel Hrdina floppy_send(head << 2 | drive); 366b3ce604eSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 367b3ce604eSPavel Hrdina floppy_send(cyl); 368b3ce604eSPavel Hrdina 369b3ce604eSPavel Hrdina floppy_send(CMD_SENSE_INT); 370b3ce604eSPavel Hrdina ret = floppy_recv(); 371b3ce604eSPavel Hrdina g_assert(ret == 0x20); 372b3ce604eSPavel Hrdina floppy_recv(); 373b3ce604eSPavel Hrdina } 374b3ce604eSPavel Hrdina 37598272dbbSPavel Hrdina static void test_relative_seek(void) 37698272dbbSPavel Hrdina { 37798272dbbSPavel Hrdina uint8_t drive = 0; 37898272dbbSPavel Hrdina uint8_t head = 0; 37998272dbbSPavel Hrdina uint8_t cyl = 1; 380c3cdc1b0SKevin Wolf uint8_t pcn; 38198272dbbSPavel Hrdina 38298272dbbSPavel Hrdina /* Send seek to track 0 */ 383c3cdc1b0SKevin Wolf send_seek(0); 38498272dbbSPavel Hrdina 38598272dbbSPavel Hrdina /* Send relative seek to increase track by 1 */ 38698272dbbSPavel Hrdina floppy_send(CMD_RELATIVE_SEEK_IN); 38798272dbbSPavel Hrdina floppy_send(head << 2 | drive); 38898272dbbSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 38998272dbbSPavel Hrdina floppy_send(cyl); 39098272dbbSPavel Hrdina 391c3cdc1b0SKevin Wolf ack_irq(&pcn); 392c3cdc1b0SKevin Wolf g_assert(pcn == 1); 39398272dbbSPavel Hrdina 39498272dbbSPavel Hrdina /* Send relative seek to decrease track by 1 */ 39598272dbbSPavel Hrdina floppy_send(CMD_RELATIVE_SEEK_OUT); 39698272dbbSPavel Hrdina floppy_send(head << 2 | drive); 39798272dbbSPavel Hrdina g_assert(!get_irq(FLOPPY_IRQ)); 39898272dbbSPavel Hrdina floppy_send(cyl); 39998272dbbSPavel Hrdina 400c3cdc1b0SKevin Wolf ack_irq(&pcn); 401c3cdc1b0SKevin Wolf g_assert(pcn == 0); 40298272dbbSPavel Hrdina } 40398272dbbSPavel Hrdina 40467f194bdSKevin Wolf static void test_read_id(void) 40567f194bdSKevin Wolf { 40667f194bdSKevin Wolf uint8_t drive = 0; 40767f194bdSKevin Wolf uint8_t head = 0; 40867f194bdSKevin Wolf uint8_t cyl; 40967f194bdSKevin Wolf uint8_t st0; 4104964e18eSKevin Wolf uint8_t msr; 41167f194bdSKevin Wolf 41267f194bdSKevin Wolf /* Seek to track 0 and check with READ ID */ 41367f194bdSKevin Wolf send_seek(0); 41467f194bdSKevin Wolf 41567f194bdSKevin Wolf floppy_send(CMD_READ_ID); 41667f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 41767f194bdSKevin Wolf floppy_send(head << 2 | drive); 41867f194bdSKevin Wolf 4194964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4204964e18eSKevin Wolf if (!get_irq(FLOPPY_IRQ)) { 4214964e18eSKevin Wolf assert_bit_set(msr, BUSY); 4224964e18eSKevin Wolf assert_bit_clear(msr, RQM); 4234964e18eSKevin Wolf } 4244964e18eSKevin Wolf 42567f194bdSKevin Wolf while (!get_irq(FLOPPY_IRQ)) { 42667f194bdSKevin Wolf /* qemu involves a timer with READ ID... */ 42767f194bdSKevin Wolf clock_step(1000000000LL / 50); 42867f194bdSKevin Wolf } 42967f194bdSKevin Wolf 4304964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4314964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 4324964e18eSKevin Wolf 43367f194bdSKevin Wolf st0 = floppy_recv(); 43467f194bdSKevin Wolf floppy_recv(); 43567f194bdSKevin Wolf floppy_recv(); 43667f194bdSKevin Wolf cyl = floppy_recv(); 43767f194bdSKevin Wolf head = floppy_recv(); 43867f194bdSKevin Wolf floppy_recv(); 4394964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 44067f194bdSKevin Wolf floppy_recv(); 4414964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 44267f194bdSKevin Wolf 44367f194bdSKevin Wolf g_assert_cmpint(cyl, ==, 0); 44467f194bdSKevin Wolf g_assert_cmpint(head, ==, 0); 44567f194bdSKevin Wolf g_assert_cmpint(st0, ==, head << 2); 44667f194bdSKevin Wolf 44767f194bdSKevin Wolf /* Seek to track 8 on head 1 and check with READ ID */ 44867f194bdSKevin Wolf head = 1; 44967f194bdSKevin Wolf cyl = 8; 45067f194bdSKevin Wolf 45167f194bdSKevin Wolf floppy_send(CMD_SEEK); 45267f194bdSKevin Wolf floppy_send(head << 2 | drive); 45367f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 45467f194bdSKevin Wolf floppy_send(cyl); 45567f194bdSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 45667f194bdSKevin Wolf ack_irq(NULL); 45767f194bdSKevin Wolf 45867f194bdSKevin Wolf floppy_send(CMD_READ_ID); 45967f194bdSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 46067f194bdSKevin Wolf floppy_send(head << 2 | drive); 46167f194bdSKevin Wolf 4624964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4634964e18eSKevin Wolf if (!get_irq(FLOPPY_IRQ)) { 4644964e18eSKevin Wolf assert_bit_set(msr, BUSY); 4654964e18eSKevin Wolf assert_bit_clear(msr, RQM); 4664964e18eSKevin Wolf } 4674964e18eSKevin Wolf 46867f194bdSKevin Wolf while (!get_irq(FLOPPY_IRQ)) { 46967f194bdSKevin Wolf /* qemu involves a timer with READ ID... */ 47067f194bdSKevin Wolf clock_step(1000000000LL / 50); 47167f194bdSKevin Wolf } 47267f194bdSKevin Wolf 4734964e18eSKevin Wolf msr = inb(FLOPPY_BASE + reg_msr); 4744964e18eSKevin Wolf assert_bit_set(msr, BUSY | RQM | DIO); 4754964e18eSKevin Wolf 47667f194bdSKevin Wolf st0 = floppy_recv(); 47767f194bdSKevin Wolf floppy_recv(); 47867f194bdSKevin Wolf floppy_recv(); 47967f194bdSKevin Wolf cyl = floppy_recv(); 48067f194bdSKevin Wolf head = floppy_recv(); 48167f194bdSKevin Wolf floppy_recv(); 4824964e18eSKevin Wolf g_assert(get_irq(FLOPPY_IRQ)); 48367f194bdSKevin Wolf floppy_recv(); 4844964e18eSKevin Wolf g_assert(!get_irq(FLOPPY_IRQ)); 48567f194bdSKevin Wolf 48667f194bdSKevin Wolf g_assert_cmpint(cyl, ==, 8); 48767f194bdSKevin Wolf g_assert_cmpint(head, ==, 1); 48867f194bdSKevin Wolf g_assert_cmpint(st0, ==, head << 2); 48967f194bdSKevin Wolf } 49067f194bdSKevin Wolf 4915f8ae8e2SHervé Poussineau static void test_read_no_dma_1(void) 4925f8ae8e2SHervé Poussineau { 4935f8ae8e2SHervé Poussineau uint8_t ret; 4945f8ae8e2SHervé Poussineau 4955f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 4965f8ae8e2SHervé Poussineau send_seek(0); 497075f5532SHervé Poussineau ret = send_read_no_dma_command(1, 0x04); 4985f8ae8e2SHervé Poussineau g_assert(ret == 0); 4995f8ae8e2SHervé Poussineau } 5005f8ae8e2SHervé Poussineau 5015f8ae8e2SHervé Poussineau static void test_read_no_dma_18(void) 5025f8ae8e2SHervé Poussineau { 5035f8ae8e2SHervé Poussineau uint8_t ret; 5045f8ae8e2SHervé Poussineau 5055f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 5065f8ae8e2SHervé Poussineau send_seek(0); 507075f5532SHervé Poussineau ret = send_read_no_dma_command(18, 0x04); 5085f8ae8e2SHervé Poussineau g_assert(ret == 0); 5095f8ae8e2SHervé Poussineau } 5105f8ae8e2SHervé Poussineau 5115f8ae8e2SHervé Poussineau static void test_read_no_dma_19(void) 5125f8ae8e2SHervé Poussineau { 5135f8ae8e2SHervé Poussineau uint8_t ret; 5145f8ae8e2SHervé Poussineau 5155f8ae8e2SHervé Poussineau outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08); 5165f8ae8e2SHervé Poussineau send_seek(0); 5175f8ae8e2SHervé Poussineau ret = send_read_no_dma_command(19, 0x20); 5185f8ae8e2SHervé Poussineau g_assert(ret == 0); 5195f8ae8e2SHervé Poussineau } 5205f8ae8e2SHervé Poussineau 5216f442fe8SHervé Poussineau static void test_verify(void) 5226f442fe8SHervé Poussineau { 5236f442fe8SHervé Poussineau uint8_t ret; 5246f442fe8SHervé Poussineau 5256f442fe8SHervé Poussineau ret = send_read_command(CMD_VERIFY); 5266f442fe8SHervé Poussineau g_assert(ret == 0); 5276f442fe8SHervé Poussineau } 5286f442fe8SHervé Poussineau 5293359847eSBlue Swirl /* success if no crash or abort */ 5303359847eSBlue Swirl static void fuzz_registers(void) 5313359847eSBlue Swirl { 5323359847eSBlue Swirl unsigned int i; 5333359847eSBlue Swirl 5343359847eSBlue Swirl for (i = 0; i < 1000; i++) { 5353359847eSBlue Swirl uint8_t reg, val; 5363359847eSBlue Swirl 5373359847eSBlue Swirl reg = (uint8_t)g_test_rand_int_range(0, 8); 5383359847eSBlue Swirl val = (uint8_t)g_test_rand_int_range(0, 256); 5393359847eSBlue Swirl 5403359847eSBlue Swirl outb(FLOPPY_BASE + reg, val); 5413359847eSBlue Swirl inb(FLOPPY_BASE + reg); 5423359847eSBlue Swirl } 5433359847eSBlue Swirl } 5443359847eSBlue Swirl 54593e9eb68SKevin Wolf int main(int argc, char **argv) 54693e9eb68SKevin Wolf { 54793e9eb68SKevin Wolf const char *arch = qtest_get_arch(); 54893e9eb68SKevin Wolf int fd; 54993e9eb68SKevin Wolf int ret; 55093e9eb68SKevin Wolf 55193e9eb68SKevin Wolf /* Check architecture */ 55293e9eb68SKevin Wolf if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { 55393e9eb68SKevin Wolf g_test_message("Skipping test for non-x86\n"); 55493e9eb68SKevin Wolf return 0; 55593e9eb68SKevin Wolf } 55693e9eb68SKevin Wolf 55793e9eb68SKevin Wolf /* Create a temporary raw image */ 55893e9eb68SKevin Wolf fd = mkstemp(test_image); 55993e9eb68SKevin Wolf g_assert(fd >= 0); 56093e9eb68SKevin Wolf ret = ftruncate(fd, TEST_IMAGE_SIZE); 56193e9eb68SKevin Wolf g_assert(ret == 0); 56293e9eb68SKevin Wolf close(fd); 56393e9eb68SKevin Wolf 56493e9eb68SKevin Wolf /* Run the tests */ 56593e9eb68SKevin Wolf g_test_init(&argc, &argv, NULL); 56693e9eb68SKevin Wolf 567*ccb61bddSEric Blake qtest_start("-device floppy,id=floppy0"); 56893e9eb68SKevin Wolf qtest_irq_intercept_in(global_qtest, "ioapic"); 5697cd33161SPavel Hrdina qtest_add_func("/fdc/cmos", test_cmos); 5707cd33161SPavel Hrdina qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); 5718b9ef60dSPavel Hrdina qtest_add_func("/fdc/read_without_media", test_read_without_media); 57293e9eb68SKevin Wolf qtest_add_func("/fdc/media_change", test_media_change); 573b3ce604eSPavel Hrdina qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); 57498272dbbSPavel Hrdina qtest_add_func("/fdc/relative_seek", test_relative_seek); 57567f194bdSKevin Wolf qtest_add_func("/fdc/read_id", test_read_id); 5766f442fe8SHervé Poussineau qtest_add_func("/fdc/verify", test_verify); 57744212dccSHervé Poussineau qtest_add_func("/fdc/media_insert", test_media_insert); 5785f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); 5795f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18); 5805f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19); 5813359847eSBlue Swirl qtest_add_func("/fdc/fuzz-registers", fuzz_registers); 58293e9eb68SKevin Wolf 58393e9eb68SKevin Wolf ret = g_test_run(); 58493e9eb68SKevin Wolf 58593e9eb68SKevin Wolf /* Cleanup */ 5861d9358e6SMarkus Armbruster qtest_end(); 58793e9eb68SKevin Wolf unlink(test_image); 58893e9eb68SKevin Wolf 58993e9eb68SKevin Wolf return ret; 59093e9eb68SKevin Wolf } 591