1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include "qemu/osdep.h"
34#include "sysemu/block-backend.h"
35#include "qemu/bswap.h"
36#include "hw/block/block.h"
37#include "trace.h"
38
39struct partition {
40 uint8_t boot_ind;
41 uint8_t head;
42 uint8_t sector;
43 uint8_t cyl;
44 uint8_t sys_ind;
45 uint8_t end_head;
46 uint8_t end_sector;
47 uint8_t end_cyl;
48 uint32_t start_sect;
49 uint32_t nr_sects;
50} QEMU_PACKED;
51
52
53
54static int guess_disk_lchs(BlockBackend *blk,
55 int *pcylinders, int *pheads, int *psectors)
56{
57 uint8_t buf[BDRV_SECTOR_SIZE];
58 int i, heads, sectors, cylinders;
59 struct partition *p;
60 uint32_t nr_sects;
61 uint64_t nb_sectors;
62
63 blk_get_geometry(blk, &nb_sectors);
64
65
66
67
68
69
70 if (blk_pread_unthrottled(blk, 0, buf, BDRV_SECTOR_SIZE) < 0) {
71 return -1;
72 }
73
74 if (buf[510] != 0x55 || buf[511] != 0xaa) {
75 return -1;
76 }
77 for (i = 0; i < 4; i++) {
78 p = ((struct partition *)(buf + 0x1be)) + i;
79 nr_sects = le32_to_cpu(p->nr_sects);
80 if (nr_sects && p->end_head) {
81
82
83 heads = p->end_head + 1;
84 sectors = p->end_sector & 63;
85 if (sectors == 0) {
86 continue;
87 }
88 cylinders = nb_sectors / (heads * sectors);
89 if (cylinders < 1 || cylinders > 16383) {
90 continue;
91 }
92 *pheads = heads;
93 *psectors = sectors;
94 *pcylinders = cylinders;
95 trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors);
96 return 0;
97 }
98 }
99 return -1;
100}
101
102static void guess_chs_for_size(BlockBackend *blk,
103 uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs)
104{
105 uint64_t nb_sectors;
106 int cylinders;
107
108 blk_get_geometry(blk, &nb_sectors);
109
110 cylinders = nb_sectors / (16 * 63);
111 if (cylinders > 16383) {
112 cylinders = 16383;
113 } else if (cylinders < 2) {
114 cylinders = 2;
115 }
116 *pcyls = cylinders;
117 *pheads = 16;
118 *psecs = 63;
119}
120
121void hd_geometry_guess(BlockBackend *blk,
122 uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs,
123 int *ptrans)
124{
125 int cylinders, heads, secs, translation;
126 HDGeometry geo;
127
128
129
130 if (blk_probe_geometry(blk, &geo) == 0) {
131 *pcyls = geo.cylinders;
132 *psecs = geo.sectors;
133 *pheads = geo.heads;
134 translation = BIOS_ATA_TRANSLATION_NONE;
135 } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) {
136
137 guess_chs_for_size(blk, pcyls, pheads, psecs);
138 translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs);
139 } else if (heads > 16) {
140
141
142
143 guess_chs_for_size(blk, pcyls, pheads, psecs);
144 translation = *pcyls * *pheads <= 131072
145 ? BIOS_ATA_TRANSLATION_LARGE
146 : BIOS_ATA_TRANSLATION_LBA;
147 } else {
148
149 *pcyls = cylinders;
150 *pheads = heads;
151 *psecs = secs;
152
153
154 translation = BIOS_ATA_TRANSLATION_NONE;
155 }
156 if (ptrans) {
157 *ptrans = translation;
158 }
159 trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation);
160}
161
162int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs)
163{
164 return cyls <= 1024 && heads <= 16 && secs <= 63
165 ? BIOS_ATA_TRANSLATION_NONE
166 : BIOS_ATA_TRANSLATION_LBA;
167}
168