xref: /qemu/tests/qtest/fdc-test.c (revision ccb61bdd73a6d9a3eb717d1b69125ec7ca638a3b)
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