]>
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. */ | |
3e353773 | 72 | int boot_sector_init(char *fname) |
4e082566 | 73 | { |
3e353773 | 74 | int fd, ret; |
1ef2ef96 | 75 | size_t len = sizeof boot_sector; |
4e082566 | 76 | |
3e353773 TH |
77 | fd = mkstemp(fname); |
78 | if (fd < 0) { | |
4e082566 VK |
79 | fprintf(stderr, "Couldn't open \"%s\": %s", fname, strerror(errno)); |
80 | return 1; | |
81 | } | |
1485ef1c TH |
82 | |
83 | /* For Open Firmware based system, we can use a Forth script instead */ | |
84 | if (strcmp(qtest_get_arch(), "ppc64") == 0) { | |
1ef2ef96 | 85 | len = sprintf((char *)boot_sector, "\\ Bootscript\n%x %x c! %x %x c!\n", |
1485ef1c TH |
86 | LOW(SIGNATURE), BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET, |
87 | HIGH(SIGNATURE), BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); | |
88 | } | |
89 | ||
3e353773 TH |
90 | ret = write(fd, boot_sector, len); |
91 | close(fd); | |
92 | ||
93 | if (ret != len) { | |
94 | fprintf(stderr, "Could not write \"%s\"", fname); | |
95 | return 1; | |
96 | } | |
97 | ||
4e082566 VK |
98 | return 0; |
99 | } | |
100 | ||
101 | /* Loop until signature in memory is OK. */ | |
102 | void boot_sector_test(void) | |
103 | { | |
104 | uint8_t signature_low; | |
105 | uint8_t signature_high; | |
106 | uint16_t signature; | |
107 | int i; | |
108 | ||
74cba2b3 | 109 | /* Wait at most 90 seconds */ |
4e082566 | 110 | #define TEST_DELAY (1 * G_USEC_PER_SEC / 10) |
74cba2b3 | 111 | #define TEST_CYCLES MAX((90 * G_USEC_PER_SEC / TEST_DELAY), 1) |
4e082566 VK |
112 | |
113 | /* Poll until code has run and modified memory. Once it has we know BIOS | |
114 | * initialization is done. TODO: check that IP reached the halt | |
115 | * instruction. | |
116 | */ | |
117 | for (i = 0; i < TEST_CYCLES; ++i) { | |
118 | signature_low = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET); | |
119 | signature_high = readb(BOOT_SECTOR_ADDRESS + SIGNATURE_OFFSET + 1); | |
120 | signature = (signature_high << 8) | signature_low; | |
121 | if (signature == SIGNATURE) { | |
122 | break; | |
123 | } | |
124 | g_usleep(TEST_DELAY); | |
125 | } | |
126 | ||
127 | g_assert_cmphex(signature, ==, SIGNATURE); | |
128 | } | |
129 | ||
130 | /* unlink boot disk file. */ | |
131 | void boot_sector_cleanup(const char *fname) | |
132 | { | |
133 | unlink(fname); | |
134 | } |