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