fdc-test.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * Floppy test cases.
  3. *
  4. * Copyright (c) 2012 Kevin Wolf <kwolf@redhat.com>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "libqtest-single.h"
  26. #include "qapi/qmp/qdict.h"
  27. #include "qemu-common.h"
  28. /* TODO actually test the results and get rid of this */
  29. #define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
  30. #define TEST_IMAGE_SIZE 1440 * 1024
  31. #define FLOPPY_BASE 0x3f0
  32. #define FLOPPY_IRQ 6
  33. enum {
  34. reg_sra = 0x0,
  35. reg_srb = 0x1,
  36. reg_dor = 0x2,
  37. reg_msr = 0x4,
  38. reg_dsr = 0x4,
  39. reg_fifo = 0x5,
  40. reg_dir = 0x7,
  41. };
  42. enum {
  43. CMD_SENSE_INT = 0x08,
  44. CMD_READ_ID = 0x0a,
  45. CMD_SEEK = 0x0f,
  46. CMD_VERIFY = 0x16,
  47. CMD_READ = 0xe6,
  48. CMD_RELATIVE_SEEK_OUT = 0x8f,
  49. CMD_RELATIVE_SEEK_IN = 0xcf,
  50. };
  51. enum {
  52. BUSY = 0x10,
  53. NONDMA = 0x20,
  54. RQM = 0x80,
  55. DIO = 0x40,
  56. DSKCHG = 0x80,
  57. };
  58. static char test_image[] = "/tmp/qtest.XXXXXX";
  59. #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
  60. #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
  61. static uint8_t base = 0x70;
  62. enum {
  63. CMOS_FLOPPY = 0x10,
  64. };
  65. static void floppy_send(uint8_t byte)
  66. {
  67. uint8_t msr;
  68. msr = inb(FLOPPY_BASE + reg_msr);
  69. assert_bit_set(msr, RQM);
  70. assert_bit_clear(msr, DIO);
  71. outb(FLOPPY_BASE + reg_fifo, byte);
  72. }
  73. static uint8_t floppy_recv(void)
  74. {
  75. uint8_t msr;
  76. msr = inb(FLOPPY_BASE + reg_msr);
  77. assert_bit_set(msr, RQM | DIO);
  78. return inb(FLOPPY_BASE + reg_fifo);
  79. }
  80. /* pcn: Present Cylinder Number */
  81. static void ack_irq(uint8_t *pcn)
  82. {
  83. uint8_t ret;
  84. g_assert(get_irq(FLOPPY_IRQ));
  85. floppy_send(CMD_SENSE_INT);
  86. floppy_recv();
  87. ret = floppy_recv();
  88. if (pcn != NULL) {
  89. *pcn = ret;
  90. }
  91. g_assert(!get_irq(FLOPPY_IRQ));
  92. }
  93. static uint8_t send_read_command(uint8_t cmd)
  94. {
  95. uint8_t drive = 0;
  96. uint8_t head = 0;
  97. uint8_t cyl = 0;
  98. uint8_t sect_addr = 1;
  99. uint8_t sect_size = 2;
  100. uint8_t eot = 1;
  101. uint8_t gap = 0x1b;
  102. uint8_t gpl = 0xff;
  103. uint8_t msr = 0;
  104. uint8_t st0;
  105. uint8_t ret = 0;
  106. floppy_send(cmd);
  107. floppy_send(head << 2 | drive);
  108. g_assert(!get_irq(FLOPPY_IRQ));
  109. floppy_send(cyl);
  110. floppy_send(head);
  111. floppy_send(sect_addr);
  112. floppy_send(sect_size);
  113. floppy_send(eot);
  114. floppy_send(gap);
  115. floppy_send(gpl);
  116. uint8_t i = 0;
  117. uint8_t n = 2;
  118. for (; i < n; i++) {
  119. msr = inb(FLOPPY_BASE + reg_msr);
  120. if (msr == 0xd0) {
  121. break;
  122. }
  123. sleep(1);
  124. }
  125. if (i >= n) {
  126. return 1;
  127. }
  128. st0 = floppy_recv();
  129. if (st0 != 0x40) {
  130. ret = 1;
  131. }
  132. floppy_recv();
  133. floppy_recv();
  134. floppy_recv();
  135. floppy_recv();
  136. floppy_recv();
  137. floppy_recv();
  138. return ret;
  139. }
  140. static uint8_t send_read_no_dma_command(int nb_sect, uint8_t expected_st0)
  141. {
  142. uint8_t drive = 0;
  143. uint8_t head = 0;
  144. uint8_t cyl = 0;
  145. uint8_t sect_addr = 1;
  146. uint8_t sect_size = 2;
  147. uint8_t eot = nb_sect;
  148. uint8_t gap = 0x1b;
  149. uint8_t gpl = 0xff;
  150. uint8_t msr = 0;
  151. uint8_t st0;
  152. uint8_t ret = 0;
  153. floppy_send(CMD_READ);
  154. floppy_send(head << 2 | drive);
  155. g_assert(!get_irq(FLOPPY_IRQ));
  156. floppy_send(cyl);
  157. floppy_send(head);
  158. floppy_send(sect_addr);
  159. floppy_send(sect_size);
  160. floppy_send(eot);
  161. floppy_send(gap);
  162. floppy_send(gpl);
  163. uint16_t i = 0;
  164. uint8_t n = 2;
  165. for (; i < n; i++) {
  166. msr = inb(FLOPPY_BASE + reg_msr);
  167. if (msr == (BUSY | NONDMA | DIO | RQM)) {
  168. break;
  169. }
  170. sleep(1);
  171. }
  172. if (i >= n) {
  173. return 1;
  174. }
  175. /* Non-DMA mode */
  176. for (i = 0; i < 512 * 2 * nb_sect; i++) {
  177. msr = inb(FLOPPY_BASE + reg_msr);
  178. assert_bit_set(msr, BUSY | RQM | DIO);
  179. inb(FLOPPY_BASE + reg_fifo);
  180. }
  181. msr = inb(FLOPPY_BASE + reg_msr);
  182. assert_bit_set(msr, BUSY | RQM | DIO);
  183. g_assert(get_irq(FLOPPY_IRQ));
  184. st0 = floppy_recv();
  185. if (st0 != expected_st0) {
  186. ret = 1;
  187. }
  188. floppy_recv();
  189. floppy_recv();
  190. floppy_recv();
  191. floppy_recv();
  192. floppy_recv();
  193. g_assert(get_irq(FLOPPY_IRQ));
  194. floppy_recv();
  195. /* Check that we're back in command phase */
  196. msr = inb(FLOPPY_BASE + reg_msr);
  197. assert_bit_clear(msr, BUSY | DIO);
  198. assert_bit_set(msr, RQM);
  199. g_assert(!get_irq(FLOPPY_IRQ));
  200. return ret;
  201. }
  202. static void send_seek(int cyl)
  203. {
  204. int drive = 0;
  205. int head = 0;
  206. floppy_send(CMD_SEEK);
  207. floppy_send(head << 2 | drive);
  208. g_assert(!get_irq(FLOPPY_IRQ));
  209. floppy_send(cyl);
  210. ack_irq(NULL);
  211. }
  212. static uint8_t cmos_read(uint8_t reg)
  213. {
  214. outb(base + 0, reg);
  215. return inb(base + 1);
  216. }
  217. static void test_cmos(void)
  218. {
  219. uint8_t cmos;
  220. cmos = cmos_read(CMOS_FLOPPY);
  221. g_assert(cmos == 0x40 || cmos == 0x50);
  222. }
  223. static void test_no_media_on_start(void)
  224. {
  225. uint8_t dir;
  226. /* Media changed bit must be set all time after start if there is
  227. * no media in drive. */
  228. dir = inb(FLOPPY_BASE + reg_dir);
  229. assert_bit_set(dir, DSKCHG);
  230. dir = inb(FLOPPY_BASE + reg_dir);
  231. assert_bit_set(dir, DSKCHG);
  232. send_seek(1);
  233. dir = inb(FLOPPY_BASE + reg_dir);
  234. assert_bit_set(dir, DSKCHG);
  235. dir = inb(FLOPPY_BASE + reg_dir);
  236. assert_bit_set(dir, DSKCHG);
  237. }
  238. static void test_read_without_media(void)
  239. {
  240. uint8_t ret;
  241. ret = send_read_command(CMD_READ);
  242. g_assert(ret == 0);
  243. }
  244. static void test_media_insert(void)
  245. {
  246. uint8_t dir;
  247. /* Insert media in drive. DSKCHK should not be reset until a step pulse
  248. * is sent. */
  249. qmp_discard_response("{'execute':'blockdev-change-medium', 'arguments':{"
  250. " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}",
  251. test_image);
  252. dir = inb(FLOPPY_BASE + reg_dir);
  253. assert_bit_set(dir, DSKCHG);
  254. dir = inb(FLOPPY_BASE + reg_dir);
  255. assert_bit_set(dir, DSKCHG);
  256. send_seek(0);
  257. dir = inb(FLOPPY_BASE + reg_dir);
  258. assert_bit_set(dir, DSKCHG);
  259. dir = inb(FLOPPY_BASE + reg_dir);
  260. assert_bit_set(dir, DSKCHG);
  261. /* Step to next track should clear DSKCHG bit. */
  262. send_seek(1);
  263. dir = inb(FLOPPY_BASE + reg_dir);
  264. assert_bit_clear(dir, DSKCHG);
  265. dir = inb(FLOPPY_BASE + reg_dir);
  266. assert_bit_clear(dir, DSKCHG);
  267. }
  268. static void test_media_change(void)
  269. {
  270. uint8_t dir;
  271. test_media_insert();
  272. /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't
  273. * reset the bit. */
  274. qmp_discard_response("{'execute':'eject', 'arguments':{"
  275. " 'id':'floppy0' }}");
  276. dir = inb(FLOPPY_BASE + reg_dir);
  277. assert_bit_set(dir, DSKCHG);
  278. dir = inb(FLOPPY_BASE + reg_dir);
  279. assert_bit_set(dir, DSKCHG);
  280. send_seek(0);
  281. dir = inb(FLOPPY_BASE + reg_dir);
  282. assert_bit_set(dir, DSKCHG);
  283. dir = inb(FLOPPY_BASE + reg_dir);
  284. assert_bit_set(dir, DSKCHG);
  285. send_seek(1);
  286. dir = inb(FLOPPY_BASE + reg_dir);
  287. assert_bit_set(dir, DSKCHG);
  288. dir = inb(FLOPPY_BASE + reg_dir);
  289. assert_bit_set(dir, DSKCHG);
  290. }
  291. static void test_sense_interrupt(void)
  292. {
  293. int drive = 0;
  294. int head = 0;
  295. int cyl = 0;
  296. int ret = 0;
  297. floppy_send(CMD_SENSE_INT);
  298. ret = floppy_recv();
  299. g_assert(ret == 0x80);
  300. floppy_send(CMD_SEEK);
  301. floppy_send(head << 2 | drive);
  302. g_assert(!get_irq(FLOPPY_IRQ));
  303. floppy_send(cyl);
  304. floppy_send(CMD_SENSE_INT);
  305. ret = floppy_recv();
  306. g_assert(ret == 0x20);
  307. floppy_recv();
  308. }
  309. static void test_relative_seek(void)
  310. {
  311. uint8_t drive = 0;
  312. uint8_t head = 0;
  313. uint8_t cyl = 1;
  314. uint8_t pcn;
  315. /* Send seek to track 0 */
  316. send_seek(0);
  317. /* Send relative seek to increase track by 1 */
  318. floppy_send(CMD_RELATIVE_SEEK_IN);
  319. floppy_send(head << 2 | drive);
  320. g_assert(!get_irq(FLOPPY_IRQ));
  321. floppy_send(cyl);
  322. ack_irq(&pcn);
  323. g_assert(pcn == 1);
  324. /* Send relative seek to decrease track by 1 */
  325. floppy_send(CMD_RELATIVE_SEEK_OUT);
  326. floppy_send(head << 2 | drive);
  327. g_assert(!get_irq(FLOPPY_IRQ));
  328. floppy_send(cyl);
  329. ack_irq(&pcn);
  330. g_assert(pcn == 0);
  331. }
  332. static void test_read_id(void)
  333. {
  334. uint8_t drive = 0;
  335. uint8_t head = 0;
  336. uint8_t cyl;
  337. uint8_t st0;
  338. uint8_t msr;
  339. /* Seek to track 0 and check with READ ID */
  340. send_seek(0);
  341. floppy_send(CMD_READ_ID);
  342. g_assert(!get_irq(FLOPPY_IRQ));
  343. floppy_send(head << 2 | drive);
  344. msr = inb(FLOPPY_BASE + reg_msr);
  345. if (!get_irq(FLOPPY_IRQ)) {
  346. assert_bit_set(msr, BUSY);
  347. assert_bit_clear(msr, RQM);
  348. }
  349. while (!get_irq(FLOPPY_IRQ)) {
  350. /* qemu involves a timer with READ ID... */
  351. clock_step(1000000000LL / 50);
  352. }
  353. msr = inb(FLOPPY_BASE + reg_msr);
  354. assert_bit_set(msr, BUSY | RQM | DIO);
  355. st0 = floppy_recv();
  356. floppy_recv();
  357. floppy_recv();
  358. cyl = floppy_recv();
  359. head = floppy_recv();
  360. floppy_recv();
  361. g_assert(get_irq(FLOPPY_IRQ));
  362. floppy_recv();
  363. g_assert(!get_irq(FLOPPY_IRQ));
  364. g_assert_cmpint(cyl, ==, 0);
  365. g_assert_cmpint(head, ==, 0);
  366. g_assert_cmpint(st0, ==, head << 2);
  367. /* Seek to track 8 on head 1 and check with READ ID */
  368. head = 1;
  369. cyl = 8;
  370. floppy_send(CMD_SEEK);
  371. floppy_send(head << 2 | drive);
  372. g_assert(!get_irq(FLOPPY_IRQ));
  373. floppy_send(cyl);
  374. g_assert(get_irq(FLOPPY_IRQ));
  375. ack_irq(NULL);
  376. floppy_send(CMD_READ_ID);
  377. g_assert(!get_irq(FLOPPY_IRQ));
  378. floppy_send(head << 2 | drive);
  379. msr = inb(FLOPPY_BASE + reg_msr);
  380. if (!get_irq(FLOPPY_IRQ)) {
  381. assert_bit_set(msr, BUSY);
  382. assert_bit_clear(msr, RQM);
  383. }
  384. while (!get_irq(FLOPPY_IRQ)) {
  385. /* qemu involves a timer with READ ID... */
  386. clock_step(1000000000LL / 50);
  387. }
  388. msr = inb(FLOPPY_BASE + reg_msr);
  389. assert_bit_set(msr, BUSY | RQM | DIO);
  390. st0 = floppy_recv();
  391. floppy_recv();
  392. floppy_recv();
  393. cyl = floppy_recv();
  394. head = floppy_recv();
  395. floppy_recv();
  396. g_assert(get_irq(FLOPPY_IRQ));
  397. floppy_recv();
  398. g_assert(!get_irq(FLOPPY_IRQ));
  399. g_assert_cmpint(cyl, ==, 8);
  400. g_assert_cmpint(head, ==, 1);
  401. g_assert_cmpint(st0, ==, head << 2);
  402. }
  403. static void test_read_no_dma_1(void)
  404. {
  405. uint8_t ret;
  406. outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
  407. send_seek(0);
  408. ret = send_read_no_dma_command(1, 0x04);
  409. g_assert(ret == 0);
  410. }
  411. static void test_read_no_dma_18(void)
  412. {
  413. uint8_t ret;
  414. outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
  415. send_seek(0);
  416. ret = send_read_no_dma_command(18, 0x04);
  417. g_assert(ret == 0);
  418. }
  419. static void test_read_no_dma_19(void)
  420. {
  421. uint8_t ret;
  422. outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
  423. send_seek(0);
  424. ret = send_read_no_dma_command(19, 0x20);
  425. g_assert(ret == 0);
  426. }
  427. static void test_verify(void)
  428. {
  429. uint8_t ret;
  430. ret = send_read_command(CMD_VERIFY);
  431. g_assert(ret == 0);
  432. }
  433. /* success if no crash or abort */
  434. static void fuzz_registers(void)
  435. {
  436. unsigned int i;
  437. for (i = 0; i < 1000; i++) {
  438. uint8_t reg, val;
  439. reg = (uint8_t)g_test_rand_int_range(0, 8);
  440. val = (uint8_t)g_test_rand_int_range(0, 256);
  441. outb(FLOPPY_BASE + reg, val);
  442. inb(FLOPPY_BASE + reg);
  443. }
  444. }
  445. int main(int argc, char **argv)
  446. {
  447. int fd;
  448. int ret;
  449. /* Create a temporary raw image */
  450. fd = mkstemp(test_image);
  451. g_assert(fd >= 0);
  452. ret = ftruncate(fd, TEST_IMAGE_SIZE);
  453. g_assert(ret == 0);
  454. close(fd);
  455. /* Run the tests */
  456. g_test_init(&argc, &argv, NULL);
  457. qtest_start("-device floppy,id=floppy0");
  458. qtest_irq_intercept_in(global_qtest, "ioapic");
  459. qtest_add_func("/fdc/cmos", test_cmos);
  460. qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
  461. qtest_add_func("/fdc/read_without_media", test_read_without_media);
  462. qtest_add_func("/fdc/media_change", test_media_change);
  463. qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
  464. qtest_add_func("/fdc/relative_seek", test_relative_seek);
  465. qtest_add_func("/fdc/read_id", test_read_id);
  466. qtest_add_func("/fdc/verify", test_verify);
  467. qtest_add_func("/fdc/media_insert", test_media_insert);
  468. qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
  469. qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
  470. qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
  471. qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
  472. ret = g_test_run();
  473. /* Cleanup */
  474. qtest_end();
  475. unlink(test_image);
  476. return ret;
  477. }