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