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 2593e9eb68SKevin Wolf #include <stdint.h> 2693e9eb68SKevin Wolf #include <string.h> 2793e9eb68SKevin Wolf #include <stdio.h> 2893e9eb68SKevin Wolf 2993e9eb68SKevin Wolf #include <glib.h> 3093e9eb68SKevin Wolf 3193e9eb68SKevin Wolf #include "libqtest.h" 3293e9eb68SKevin Wolf #include "qemu-common.h" 3393e9eb68SKevin Wolf 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 68748bfb4eSStefan Weil static char test_image[] = "/tmp/qtest.XXXXXX"; 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); 270*62c76250SJohn 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. */ 3040d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'change', 'arguments':{" 305b8e665e4SKevin Wolf " 'device':'floppy0', 'target': %s, 'arg': 'raw' }}", 3060d1aa05eSStefan Hajnoczi test_image); 3072e1280e8SMax Reitz qmp_discard_response(""); /* ignore event (open -> close) */ 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. */ 3360d1aa05eSStefan Hajnoczi qmp_discard_response("{'execute':'eject', 'arguments':{" 3370d1aa05eSStefan Hajnoczi " 'device':'floppy0' }}"); 3380d1aa05eSStefan Hajnoczi qmp_discard_response(""); /* ignore event */ 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 55093e9eb68SKevin Wolf int main(int argc, char **argv) 55193e9eb68SKevin Wolf { 55293e9eb68SKevin Wolf const char *arch = qtest_get_arch(); 55393e9eb68SKevin Wolf int fd; 55493e9eb68SKevin Wolf int ret; 55593e9eb68SKevin Wolf 55693e9eb68SKevin Wolf /* Check architecture */ 55793e9eb68SKevin Wolf if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { 55893e9eb68SKevin Wolf g_test_message("Skipping test for non-x86\n"); 55993e9eb68SKevin Wolf return 0; 56093e9eb68SKevin Wolf } 56193e9eb68SKevin Wolf 56293e9eb68SKevin Wolf /* Create a temporary raw image */ 56393e9eb68SKevin Wolf fd = mkstemp(test_image); 56493e9eb68SKevin Wolf g_assert(fd >= 0); 56593e9eb68SKevin Wolf ret = ftruncate(fd, TEST_IMAGE_SIZE); 56693e9eb68SKevin Wolf g_assert(ret == 0); 56793e9eb68SKevin Wolf close(fd); 56893e9eb68SKevin Wolf 56993e9eb68SKevin Wolf /* Run the tests */ 57093e9eb68SKevin Wolf g_test_init(&argc, &argv, NULL); 57193e9eb68SKevin Wolf 572b7fcff01SKewei Yu qtest_start(NULL); 57393e9eb68SKevin Wolf qtest_irq_intercept_in(global_qtest, "ioapic"); 5747cd33161SPavel Hrdina qtest_add_func("/fdc/cmos", test_cmos); 5757cd33161SPavel Hrdina qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start); 5768b9ef60dSPavel Hrdina qtest_add_func("/fdc/read_without_media", test_read_without_media); 57793e9eb68SKevin Wolf qtest_add_func("/fdc/media_change", test_media_change); 578b3ce604eSPavel Hrdina qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); 57998272dbbSPavel Hrdina qtest_add_func("/fdc/relative_seek", test_relative_seek); 58067f194bdSKevin Wolf qtest_add_func("/fdc/read_id", test_read_id); 5816f442fe8SHervé Poussineau qtest_add_func("/fdc/verify", test_verify); 58244212dccSHervé Poussineau qtest_add_func("/fdc/media_insert", test_media_insert); 5835f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); 5845f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18); 5855f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19); 5863359847eSBlue Swirl qtest_add_func("/fdc/fuzz-registers", fuzz_registers); 58793e9eb68SKevin Wolf 58893e9eb68SKevin Wolf ret = g_test_run(); 58993e9eb68SKevin Wolf 59093e9eb68SKevin Wolf /* Cleanup */ 5911d9358e6SMarkus Armbruster qtest_end(); 59293e9eb68SKevin Wolf unlink(test_image); 59393e9eb68SKevin Wolf 59493e9eb68SKevin Wolf return ret; 59593e9eb68SKevin Wolf } 596