1 #include <core.h>
2 #include <com32.h>
3 #include <fs.h>
4 #include <ilog2.h>
5
6 #define RETRY_COUNT 6
7
chs_max(const struct disk * disk)8 static inline sector_t chs_max(const struct disk *disk)
9 {
10 return (sector_t)disk->secpercyl << 10;
11 }
12
13 struct edd_rdwr_packet {
14 uint16_t size;
15 uint16_t blocks;
16 far_ptr_t buf;
17 uint64_t lba;
18 };
19
20 struct edd_disk_params {
21 uint16_t len;
22 uint16_t flags;
23 uint32_t phys_c;
24 uint32_t phys_h;
25 uint32_t phys_s;
26 uint64_t sectors;
27 uint16_t sector_size;
28 far_ptr_t dpte;
29 uint16_t devpath_key;
30 uint8_t devpath_len;
31 uint8_t _pad1[3];
32 char bus_type[4];
33 char if_type[8];
34 uint8_t if_path[8];
35 uint8_t dev_path[16];
36 uint8_t _pad2;
37 uint8_t devpath_csum; /* Depends on devpath_len! */
38 } __attribute__((packed));
39
is_power_of_2(uint32_t x)40 static inline bool is_power_of_2(uint32_t x)
41 {
42 return !(x & (x-1));
43 }
44
chs_rdwr_sectors(struct disk * disk,void * buf,sector_t lba,size_t count,bool is_write)45 static int chs_rdwr_sectors(struct disk *disk, void *buf,
46 sector_t lba, size_t count, bool is_write)
47 {
48 char *ptr = buf;
49 char *tptr;
50 size_t chunk, freeseg;
51 int sector_shift = disk->sector_shift;
52 uint32_t xlba = lba + disk->part_start; /* Truncated LBA (CHS is << 2 TB) */
53 uint32_t t;
54 uint32_t c, h, s;
55 com32sys_t ireg, oreg;
56 size_t done = 0;
57 size_t bytes;
58 int retry;
59 uint32_t maxtransfer = disk->maxtransfer;
60
61 if (lba + disk->part_start >= chs_max(disk))
62 return 0; /* Impossible CHS request */
63
64 memset(&ireg, 0, sizeof ireg);
65
66 ireg.eax.b[1] = 0x02 + is_write;
67 ireg.edx.b[0] = disk->disk_number;
68
69 while (count) {
70 chunk = count;
71 if (chunk > maxtransfer)
72 chunk = maxtransfer;
73
74 freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
75
76 if ((size_t)buf <= 0xf0000 && freeseg) {
77 /* Can do a direct load */
78 tptr = ptr;
79 } else {
80 /* Either accessing high memory or we're crossing a 64K line */
81 tptr = core_xfer_buf;
82 freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
83 }
84 if (chunk > freeseg)
85 chunk = freeseg;
86
87 s = xlba % disk->s;
88 t = xlba / disk->s;
89 h = t % disk->h;
90 c = t / disk->h;
91
92 if (chunk > (disk->s - s))
93 chunk = disk->s - s;
94
95 bytes = chunk << sector_shift;
96
97 if (tptr != ptr && is_write)
98 memcpy(tptr, ptr, bytes);
99
100 ireg.eax.b[0] = chunk;
101 ireg.ecx.b[1] = c;
102 ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
103 ireg.edx.b[1] = h;
104 ireg.ebx.w[0] = OFFS(tptr);
105 ireg.es = SEG(tptr);
106
107 retry = RETRY_COUNT;
108
109 for (;;) {
110 if (c < 1024) {
111 dprintf("CHS[%02x]: %u @ %llu (%u/%u/%u) %04x:%04x %s %p\n",
112 ireg.edx.b[0], chunk, xlba, c, h, s+1,
113 ireg.es, ireg.ebx.w[0],
114 (ireg.eax.b[1] & 1) ? "<-" : "->",
115 ptr);
116
117 __intcall(0x13, &ireg, &oreg);
118 if (!(oreg.eflags.l & EFLAGS_CF))
119 break;
120
121 dprintf("CHS: error AX = %04x\n", oreg.eax.w[0]);
122
123 if (retry--)
124 continue;
125
126 /*
127 * For any starting value, this will always end with
128 * ..., 1, 0
129 */
130 chunk >>= 1;
131 if (chunk) {
132 maxtransfer = chunk;
133 retry = RETRY_COUNT;
134 ireg.eax.b[0] = chunk;
135 continue;
136 }
137 }
138
139 printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
140 oreg.eax.w[0],
141 is_write ? "writing" : "reading",
142 lba, c, h, s+1);
143 return done; /* Failure */
144 }
145
146 bytes = chunk << sector_shift;
147
148 if (tptr != ptr && !is_write)
149 memcpy(ptr, tptr, bytes);
150
151 /* If we dropped maxtransfer, it eventually worked, so remember it */
152 disk->maxtransfer = maxtransfer;
153
154 ptr += bytes;
155 xlba += chunk;
156 count -= chunk;
157 done += chunk;
158 }
159
160 return done;
161 }
162
edd_rdwr_sectors(struct disk * disk,void * buf,sector_t lba,size_t count,bool is_write)163 static int edd_rdwr_sectors(struct disk *disk, void *buf,
164 sector_t lba, size_t count, bool is_write)
165 {
166 static __lowmem struct edd_rdwr_packet pkt;
167 char *ptr = buf;
168 char *tptr;
169 size_t chunk, freeseg;
170 int sector_shift = disk->sector_shift;
171 com32sys_t ireg, oreg, reset;
172 size_t done = 0;
173 size_t bytes;
174 int retry;
175 uint32_t maxtransfer = disk->maxtransfer;
176
177 memset(&ireg, 0, sizeof ireg);
178
179 ireg.eax.b[1] = 0x42 + is_write;
180 ireg.edx.b[0] = disk->disk_number;
181 ireg.ds = SEG(&pkt);
182 ireg.esi.w[0] = OFFS(&pkt);
183
184 memset(&reset, 0, sizeof reset);
185
186 lba += disk->part_start;
187 while (count) {
188 chunk = count;
189 if (chunk > maxtransfer)
190 chunk = maxtransfer;
191
192 freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
193
194 if ((size_t)ptr <= 0xf0000 && freeseg) {
195 /* Can do a direct load */
196 tptr = ptr;
197 } else {
198 /* Either accessing high memory or we're crossing a 64K line */
199 tptr = core_xfer_buf;
200 freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
201 }
202 if (chunk > freeseg)
203 chunk = freeseg;
204
205 bytes = chunk << sector_shift;
206
207 if (tptr != ptr && is_write)
208 memcpy(tptr, ptr, bytes);
209
210 retry = RETRY_COUNT;
211
212 for (;;) {
213 pkt.size = sizeof pkt;
214 pkt.blocks = chunk;
215 pkt.buf = FAR_PTR(tptr);
216 pkt.lba = lba;
217
218 dprintf("EDD[%02x]: %u @ %llu %04x:%04x %s %p\n",
219 ireg.edx.b[0], pkt.blocks, pkt.lba,
220 pkt.buf.seg, pkt.buf.offs,
221 (ireg.eax.b[1] & 1) ? "<-" : "->",
222 ptr);
223
224 __intcall(0x13, &ireg, &oreg);
225 if (!(oreg.eflags.l & EFLAGS_CF))
226 break;
227
228 dprintf("EDD: error AX = %04x\n", oreg.eax.w[0]);
229
230 if (retry--)
231 continue;
232
233 /*
234 * Some systems seem to get "stuck" in an error state when
235 * using EBIOS. Doesn't happen when using CBIOS, which is
236 * good, since some other systems get timeout failures
237 * waiting for the floppy disk to spin up.
238 */
239 __intcall(0x13, &reset, NULL);
240
241 /* For any starting value, this will always end with ..., 1, 0 */
242 chunk >>= 1;
243 if (chunk) {
244 maxtransfer = chunk;
245 retry = RETRY_COUNT;
246 continue;
247 }
248
249 /*
250 * Total failure. There are systems which identify as
251 * EDD-capable but aren't; the known such systems return
252 * error code AH=1 (invalid function), but let's not
253 * assume that for now.
254 *
255 * Try to fall back to CHS. If the LBA is absurd, the
256 * chs_max() test in chs_rdwr_sectors() will catch it.
257 */
258 done = chs_rdwr_sectors(disk, buf, lba - disk->part_start,
259 count, is_write);
260 if (done == (count << sector_shift)) {
261 /* Successful, assume this is a CHS disk */
262 disk->rdwr_sectors = chs_rdwr_sectors;
263 return done;
264 }
265 printf("EDD: Error %04x %s sector %llu\n",
266 oreg.eax.w[0],
267 is_write ? "writing" : "reading",
268 lba);
269 return done; /* Failure */
270 }
271
272 bytes = chunk << sector_shift;
273
274 if (tptr != ptr && !is_write)
275 memcpy(ptr, tptr, bytes);
276
277 /* If we dropped maxtransfer, it eventually worked, so remember it */
278 disk->maxtransfer = maxtransfer;
279
280 ptr += bytes;
281 lba += chunk;
282 count -= chunk;
283 done += chunk;
284 }
285 return done;
286 }
287
bios_disk_init(void * private)288 struct disk *bios_disk_init(void *private)
289 {
290 static struct disk disk;
291 struct bios_disk_private *priv = (struct bios_disk_private *)private;
292 com32sys_t *regs = priv->regs;
293 static __lowmem struct edd_disk_params edd_params;
294 com32sys_t ireg, oreg;
295 uint8_t devno = regs->edx.b[0];
296 bool cdrom = regs->edx.b[1];
297 sector_t part_start = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
298 uint16_t bsHeads = regs->esi.w[0];
299 uint16_t bsSecPerTrack = regs->edi.w[0];
300 uint32_t MaxTransfer = regs->ebp.l;
301 bool ebios;
302 int sector_size;
303 unsigned int hard_max_transfer;
304
305 memset(&ireg, 0, sizeof ireg);
306 ireg.edx.b[0] = devno;
307
308 if (cdrom) {
309 /*
310 * The query functions don't work right on some CD-ROM stacks.
311 * Known affected systems: ThinkPad T22, T23.
312 */
313 sector_size = 2048;
314 ebios = true;
315 hard_max_transfer = 32;
316 } else {
317 sector_size = 512;
318 ebios = false;
319 hard_max_transfer = 63;
320
321 /* CBIOS parameters */
322 disk.h = bsHeads;
323 disk.s = bsSecPerTrack;
324
325 if ((int8_t)devno < 0) {
326 /* Get hard disk geometry from BIOS */
327
328 ireg.eax.b[1] = 0x08;
329 __intcall(0x13, &ireg, &oreg);
330
331 if (!(oreg.eflags.l & EFLAGS_CF)) {
332 disk.h = oreg.edx.b[1] + 1;
333 disk.s = oreg.ecx.b[0] & 63;
334 }
335 }
336
337 memset(&ireg, 0, sizeof ireg);
338 /* Get EBIOS support */
339 ireg.eax.b[1] = 0x41;
340 ireg.ebx.w[0] = 0x55aa;
341 ireg.edx.b[0] = devno;
342 ireg.eflags.b[0] = 0x3; /* CF set */
343
344 __intcall(0x13, &ireg, &oreg);
345
346 if (!(oreg.eflags.l & EFLAGS_CF) &&
347 oreg.ebx.w[0] == 0xaa55 && (oreg.ecx.b[0] & 1)) {
348 ebios = true;
349 hard_max_transfer = 127;
350
351 /* Query EBIOS parameters */
352 /* The memset() is needed once this function can be called
353 more than once */
354 /* memset(&edd_params, 0, sizeof edd_params); */
355 edd_params.len = sizeof edd_params;
356
357 memset(&ireg, 0, sizeof ireg);
358 ireg.eax.b[1] = 0x48;
359 ireg.edx.b[0] = devno;
360 ireg.ds = SEG(&edd_params);
361 ireg.esi.w[0] = OFFS(&edd_params);
362 __intcall(0x13, &ireg, &oreg);
363
364 if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.b[1] == 0) {
365 if (edd_params.len < sizeof edd_params)
366 memset((char *)&edd_params + edd_params.len, 0,
367 sizeof edd_params - edd_params.len);
368
369 if (edd_params.sector_size >= 512 &&
370 is_power_of_2(edd_params.sector_size))
371 sector_size = edd_params.sector_size;
372 }
373 }
374
375 }
376
377 disk.disk_number = devno;
378 disk.sector_size = sector_size;
379 disk.sector_shift = ilog2(sector_size);
380 disk.part_start = part_start;
381 disk.secpercyl = disk.h * disk.s;
382 disk.rdwr_sectors = ebios ? edd_rdwr_sectors : chs_rdwr_sectors;
383
384 if (!MaxTransfer || MaxTransfer > hard_max_transfer)
385 MaxTransfer = hard_max_transfer;
386
387 disk.maxtransfer = MaxTransfer;
388
389 dprintf("disk %02x cdrom %d type %d sector %u/%u offset %llu limit %u\n",
390 devno, cdrom, ebios, sector_size, disk.sector_shift,
391 part_start, disk.maxtransfer);
392
393 disk.private = private;
394 return &disk;
395 }
396
pm_fs_init(com32sys_t * regs)397 void pm_fs_init(com32sys_t *regs)
398 {
399 static struct bios_disk_private priv;
400
401 priv.regs = regs;
402 fs_init((const struct fs_ops **)regs->eax.l, (void *)&priv);
403 }
404