]>
Commit | Line | Data |
---|---|---|
4e082566 VK |
1 | /* |
2 | * QEMU boot sector testing helpers. | |
3 | * | |
4 | * Copyright (c) 2016 Red Hat Inc. | |
5 | * | |
6 | * Authors: | |
7 | * Michael S. Tsirkin <[email protected]> | |
8 | * Victor Kaplansky <[email protected]> | |
9 | * | |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
11 | * See the COPYING file in the top-level directory. | |
12 | */ | |
974dc73d | 13 | #include "qemu/osdep.h" |
4e082566 | 14 | #include "boot-sector.h" |
4e082566 VK |
15 | #include "qemu-common.h" |
16 | #include "libqtest.h" | |
17 | ||
18 | #define LOW(x) ((x) & 0xff) | |
19 | #define HIGH(x) ((x) >> 8) | |
20 | ||
21 | #define SIGNATURE 0xdead | |
22 | #define SIGNATURE_OFFSET 0x10 | |
23 | #define BOOT_SECTOR_ADDRESS 0x7c00 | |
24 | ||
25 | /* Boot sector code: write SIGNATURE into memory, | |
26 | * then halt. | |
27 | * Q35 machine requires a minimum 0x7e000 bytes disk. | |
28 | * (bug or feature?) | |
29 | */ | |
30 | static uint8_t boot_sector[0x7e000] = { | |
31 | /* The first sector will be placed at RAM address 00007C00, and | |
32 | * the BIOS transfers control to 00007C00 | |
33 | */ | |
34 | ||
35 | /* Data Segment register should be initialized, since pxe | |
36 | * boot loader can leave it dirty. | |
37 | */ | |
38 | ||
39 | /* 7c00: move $0000,%ax */ | |
40 | [0x00] = 0xb8, | |
41 | [0x01] = 0x00, | |
42 | [0x02] = 0x00, | |
43 | /* 7c03: move %ax,%ds */ | |
44 | [0x03] = 0x8e, | |
45 | [0x04] = 0xd8, | |
46 | ||
47 | /* 7c05: mov $0xdead,%ax */ | |
48 | [0x05] = 0xb8, | |
49 | [0x06] = LOW(SIGNATURE), | |
50 | [0x07] = HIGH(SIGNATURE), | |
51 | /* 7c08: mov %ax,0x7c10 */ | |
52 | [0x08] = 0xa3, | |
53 | [0x09] = LOW(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), | |
54 | [0x0a] = HIGH(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET), | |
55 | ||
56 | /* 7c0b cli */ | |
57 | [0x0b] = 0xfa, | |
58 | /* 7c0c: hlt */ | |
59 | [0x0c] = 0xf4, | |
60 | /* 7c0e: jmp 0x7c07=0x7c0f-3 */ | |
61 | [0x0d] = 0xeb, | |
62 | [0x0e] = LOW(-3), | |
63 | /* We mov 0xdead here: set value to make debugging easier */ | |
64 | [SIGNATURE_OFFSET] = LOW(0xface), | |
65 | [SIGNATURE_OFFSET + 1] = HIGH(0xface), | |
66 | /* End of boot sector marker */ | |
67 | [0x1FE] = 0x55, | |
68 | [0x1FF] = 0xAA, | |
69 | }; | |
70 | ||
71 | /* Create boot disk file. */ | |
72 | int boot_sector_init(const char *fname) | |
73 | { | |
74 | FILE *f = fopen(fname, "w"); | |
75 | ||
76 | if (!f) { | |
77 | fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); | |
78 | return 1; | |
79 | } | |
80 | fwrite(boot_sector, 1, sizeof boot_sector, f); | |
81 | fclose(f); | |
82 | return 0; | |
83 | } | |
84 | ||
85 | /* Loop until signature in memory is OK. */ | |
86 | void boot_sector_test(void) | |
87 | { | |
88 | uint8_t signature_low; | |
89 | uint8_t signature_high; | |
90 | uint16_t signature; | |
91 | int i; | |
92 | ||
93 | /* Wait at most 1 minute */ | |
94 | #define TEST_DELAY (1 * G_USEC_PER_SEC / 10) | |
95 | #define TEST_CYCLES MAX((60 * G_USEC_PER_SEC / TEST_DELAY), 1) | |
96 | ||
97 | /* Poll until code has run and modified memory. Once it has we know BIOS | |
98 | * initialization is done. TODO: check that IP reached the halt | |
99 | * instruction. | |
100 | */ | |
101 | for (i = 0; i < TEST_CYCLES; ++i) { | |
102 | signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); | |
103 | signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); | |
104 | signature = (signature_high << 8) | signature_low; | |
105 | if (signature == SIGNATURE) { | |
106 | break; | |
107 | } | |
108 | g_usleep(TEST_DELAY); | |
109 | } | |
110 | ||
111 | g_assert_cmphex(signature, ==, SIGNATURE); | |
112 | } | |
113 | ||
114 | /* unlink boot disk file. */ | |
115 | void boot_sector_cleanup(const char *fname) | |
116 | { | |
117 | unlink(fname); | |
118 | } |