193e9eb68SKevin Wolf /*
293e9eb68SKevin Wolf * Floppy test cases.
393e9eb68SKevin Wolf *
493e9eb68SKevin Wolf * Copyright (c) 2012 Kevin Wolf <kwolf@redhat.com>
593e9eb68SKevin Wolf *
693e9eb68SKevin Wolf * Permission is hereby granted, free of charge, to any person obtaining a copy
793e9eb68SKevin Wolf * of this software and associated documentation files (the "Software"), to deal
893e9eb68SKevin Wolf * in the Software without restriction, including without limitation the rights
993e9eb68SKevin Wolf * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1093e9eb68SKevin Wolf * copies of the Software, and to permit persons to whom the Software is
1193e9eb68SKevin Wolf * furnished to do so, subject to the following conditions:
1293e9eb68SKevin Wolf *
1393e9eb68SKevin Wolf * The above copyright notice and this permission notice shall be included in
1493e9eb68SKevin Wolf * all copies or substantial portions of the Software.
1593e9eb68SKevin Wolf *
1693e9eb68SKevin Wolf * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1793e9eb68SKevin Wolf * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1893e9eb68SKevin Wolf * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1993e9eb68SKevin Wolf * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2093e9eb68SKevin Wolf * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2193e9eb68SKevin Wolf * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2293e9eb68SKevin Wolf * THE SOFTWARE.
2393e9eb68SKevin Wolf */
2493e9eb68SKevin Wolf
25681c28a3SPeter Maydell #include "qemu/osdep.h"
2693e9eb68SKevin Wolf
2793e9eb68SKevin Wolf
28dd210749SThomas Huth #include "libqtest-single.h"
29*407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h"
3093e9eb68SKevin Wolf
31cc20926eSPhilippe Mathieu-Daudé #define DRIVE_FLOPPY_BLANK \
32cc20926eSPhilippe Mathieu-Daudé "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k"
33cc20926eSPhilippe Mathieu-Daudé
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
68394bcc5bSBin Meng static char *test_image;
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
floppy_send(uint8_t byte)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
floppy_recv(void)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 */
ack_irq(uint8_t * pcn)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
send_read_command(uint8_t cmd)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
send_read_no_dma_command(int nb_sect,uint8_t expected_st0)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
send_seek(int cyl)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
cmos_read(uint8_t reg)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
test_cmos(void)2657cd33161SPavel Hrdina static void test_cmos(void)
2667cd33161SPavel Hrdina {
2677cd33161SPavel Hrdina uint8_t cmos;
2687cd33161SPavel Hrdina
2697cd33161SPavel Hrdina cmos = cmos_read(CMOS_FLOPPY);
27062c76250SJohn Snow g_assert(cmos == 0x40 || cmos == 0x50);
2717cd33161SPavel Hrdina }
2727cd33161SPavel Hrdina
test_no_media_on_start(void)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
test_read_without_media(void)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
test_media_insert(void)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. */
304855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest,
305855436dbSDaniel P. Berrangé "{'execute':'blockdev-change-medium', 'arguments':{"
306ccb61bddSEric Blake " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}",
3070d1aa05eSStefan Hajnoczi test_image);
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
test_media_change(void)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. */
336855436dbSDaniel P. Berrangé qtest_qmp_assert_success(global_qtest,
337855436dbSDaniel P. Berrangé "{'execute':'eject', 'arguments':{"
338ccb61bddSEric Blake " 'id':'floppy0' }}");
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
test_sense_interrupt(void)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
test_relative_seek(void)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
test_read_id(void)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
test_read_no_dma_1(void)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
test_read_no_dma_18(void)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
test_read_no_dma_19(void)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
test_verify(void)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 */
fuzz_registers(void)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
qtest_check_clang_sanitizer(void)550cc20926eSPhilippe Mathieu-Daudé static bool qtest_check_clang_sanitizer(void)
551cc20926eSPhilippe Mathieu-Daudé {
552638466f7SMarc-André Lureau #ifdef QEMU_SANITIZE_ADDRESS
553cc20926eSPhilippe Mathieu-Daudé return true;
554cc20926eSPhilippe Mathieu-Daudé #else
555cb771ac1SRichard Henderson g_test_skip("QEMU not configured using --enable-asan");
556cc20926eSPhilippe Mathieu-Daudé return false;
557cc20926eSPhilippe Mathieu-Daudé #endif
558cc20926eSPhilippe Mathieu-Daudé }
test_cve_2021_20196(void)559cc20926eSPhilippe Mathieu-Daudé static void test_cve_2021_20196(void)
560cc20926eSPhilippe Mathieu-Daudé {
561cc20926eSPhilippe Mathieu-Daudé QTestState *s;
562cc20926eSPhilippe Mathieu-Daudé
563cc20926eSPhilippe Mathieu-Daudé if (!qtest_check_clang_sanitizer()) {
564cc20926eSPhilippe Mathieu-Daudé return;
565cc20926eSPhilippe Mathieu-Daudé }
566cc20926eSPhilippe Mathieu-Daudé
567cc20926eSPhilippe Mathieu-Daudé s = qtest_initf("-nographic -m 32M -nodefaults " DRIVE_FLOPPY_BLANK);
568cc20926eSPhilippe Mathieu-Daudé
569cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0500);
570cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
571cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
572cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
573cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
574cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0400);
575cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
576cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
577cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
578cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x01);
579cc20926eSPhilippe Mathieu-Daudé qtest_outw(s, 0x3f1, 0x0500);
580cc20926eSPhilippe Mathieu-Daudé qtest_outb(s, 0x3f5, 0x00);
581cc20926eSPhilippe Mathieu-Daudé qtest_quit(s);
582cc20926eSPhilippe Mathieu-Daudé }
583cc20926eSPhilippe Mathieu-Daudé
test_cve_2021_3507(void)58446609b90SPhilippe Mathieu-Daudé static void test_cve_2021_3507(void)
58546609b90SPhilippe Mathieu-Daudé {
58646609b90SPhilippe Mathieu-Daudé QTestState *s;
58746609b90SPhilippe Mathieu-Daudé
58846609b90SPhilippe Mathieu-Daudé s = qtest_initf("-nographic -m 32M -nodefaults "
58946609b90SPhilippe Mathieu-Daudé "-drive file=%s,format=raw,if=floppy,snapshot=on",
59046609b90SPhilippe Mathieu-Daudé test_image);
59146609b90SPhilippe Mathieu-Daudé qtest_outl(s, 0x9, 0x0a0206);
59246609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x1600);
59346609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59446609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59546609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59646609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0200);
59746609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0200);
59846609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
59946609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
60046609b90SPhilippe Mathieu-Daudé qtest_outw(s, 0x3f4, 0x0000);
60146609b90SPhilippe Mathieu-Daudé qtest_quit(s);
60246609b90SPhilippe Mathieu-Daudé }
60346609b90SPhilippe Mathieu-Daudé
main(int argc,char ** argv)60493e9eb68SKevin Wolf int main(int argc, char **argv)
60593e9eb68SKevin Wolf {
60693e9eb68SKevin Wolf int fd;
60793e9eb68SKevin Wolf int ret;
60893e9eb68SKevin Wolf
60993e9eb68SKevin Wolf /* Create a temporary raw image */
610394bcc5bSBin Meng fd = g_file_open_tmp("qtest.XXXXXX", &test_image, NULL);
61193e9eb68SKevin Wolf g_assert(fd >= 0);
61293e9eb68SKevin Wolf ret = ftruncate(fd, TEST_IMAGE_SIZE);
61393e9eb68SKevin Wolf g_assert(ret == 0);
61493e9eb68SKevin Wolf close(fd);
61593e9eb68SKevin Wolf
61693e9eb68SKevin Wolf /* Run the tests */
61793e9eb68SKevin Wolf g_test_init(&argc, &argv, NULL);
61893e9eb68SKevin Wolf
619fedcc379SDr. David Alan Gilbert qtest_start("-machine pc -device floppy,id=floppy0");
62093e9eb68SKevin Wolf qtest_irq_intercept_in(global_qtest, "ioapic");
6217cd33161SPavel Hrdina qtest_add_func("/fdc/cmos", test_cmos);
6227cd33161SPavel Hrdina qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
6238b9ef60dSPavel Hrdina qtest_add_func("/fdc/read_without_media", test_read_without_media);
62493e9eb68SKevin Wolf qtest_add_func("/fdc/media_change", test_media_change);
625b3ce604eSPavel Hrdina qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
62698272dbbSPavel Hrdina qtest_add_func("/fdc/relative_seek", test_relative_seek);
62767f194bdSKevin Wolf qtest_add_func("/fdc/read_id", test_read_id);
6286f442fe8SHervé Poussineau qtest_add_func("/fdc/verify", test_verify);
62944212dccSHervé Poussineau qtest_add_func("/fdc/media_insert", test_media_insert);
6305f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
6315f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
6325f8ae8e2SHervé Poussineau qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
6333359847eSBlue Swirl qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
634cc20926eSPhilippe Mathieu-Daudé qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196);
63546609b90SPhilippe Mathieu-Daudé qtest_add_func("/fdc/fuzz/cve_2021_3507", test_cve_2021_3507);
63693e9eb68SKevin Wolf
63793e9eb68SKevin Wolf ret = g_test_run();
63893e9eb68SKevin Wolf
63993e9eb68SKevin Wolf /* Cleanup */
6401d9358e6SMarkus Armbruster qtest_end();
64193e9eb68SKevin Wolf unlink(test_image);
642394bcc5bSBin Meng g_free(test_image);
64393e9eb68SKevin Wolf
64493e9eb68SKevin Wolf return ret;
64593e9eb68SKevin Wolf }
646