8 static inline sector_t
chs_max(const struct disk
*disk
)
10 return (sector_t
)disk
->secpercyl
<< 10;
13 struct edd_rdwr_packet
{
20 struct edd_disk_params
{
37 uint8_t devpath_csum
; /* Depends on devpath_len! */
38 } __attribute__((packed
));
40 static inline bool is_power_of_2(uint32_t x
)
45 static int chs_rdwr_sectors(struct disk
*disk
, void *buf
,
46 sector_t lba
, size_t count
, bool is_write
)
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) */
55 com32sys_t ireg
, oreg
;
59 uint32_t maxtransfer
= disk
->maxtransfer
;
61 if (lba
+ disk
->part_start
>= chs_max(disk
))
62 return 0; /* Impossible CHS request */
64 memset(&ireg
, 0, sizeof ireg
);
66 ireg
.eax
.b
[1] = 0x02 + is_write
;
67 ireg
.edx
.b
[0] = disk
->disk_number
;
71 if (chunk
> maxtransfer
)
74 freeseg
= (0x10000 - ((size_t)ptr
& 0xffff)) >> sector_shift
;
76 if ((size_t)buf
<= 0xf0000 && freeseg
) {
77 /* Can do a direct load */
80 /* Either accessing high memory or we're crossing a 64K line */
82 freeseg
= (0x10000 - ((size_t)tptr
& 0xffff)) >> sector_shift
;
92 if (chunk
> (disk
->s
- s
))
95 bytes
= chunk
<< sector_shift
;
97 if (tptr
!= ptr
&& is_write
)
98 memcpy(tptr
, ptr
, bytes
);
100 ireg
.eax
.b
[0] = chunk
;
102 ireg
.ecx
.b
[0] = ((c
& 0x300) >> 2) | (s
+1);
104 ireg
.ebx
.w
[0] = OFFS(tptr
);
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) ? "<-" : "->",
117 __intcall(0x13, &ireg
, &oreg
);
118 if (!(oreg
.eflags
.l
& EFLAGS_CF
))
121 dprintf("CHS: error AX = %04x\n", oreg
.eax
.w
[0]);
127 * For any starting value, this will always end with
134 ireg
.eax
.b
[0] = chunk
;
139 printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
141 is_write
? "writing" : "reading",
143 return done
; /* Failure */
146 bytes
= chunk
<< sector_shift
;
148 if (tptr
!= ptr
&& !is_write
)
149 memcpy(ptr
, tptr
, bytes
);
151 /* If we dropped maxtransfer, it eventually worked, so remember it */
152 disk
->maxtransfer
= maxtransfer
;
163 static int edd_rdwr_sectors(struct disk
*disk
, void *buf
,
164 sector_t lba
, size_t count
, bool is_write
)
166 static __lowmem
struct edd_rdwr_packet pkt
;
169 size_t chunk
, freeseg
;
170 int sector_shift
= disk
->sector_shift
;
171 com32sys_t ireg
, oreg
, reset
;
175 uint32_t maxtransfer
= disk
->maxtransfer
;
177 memset(&ireg
, 0, sizeof ireg
);
179 ireg
.eax
.b
[1] = 0x42 + is_write
;
180 ireg
.edx
.b
[0] = disk
->disk_number
;
182 ireg
.esi
.w
[0] = OFFS(&pkt
);
184 memset(&reset
, 0, sizeof reset
);
186 lba
+= disk
->part_start
;
189 if (chunk
> maxtransfer
)
192 freeseg
= (0x10000 - ((size_t)ptr
& 0xffff)) >> sector_shift
;
194 if ((size_t)ptr
<= 0xf0000 && freeseg
) {
195 /* Can do a direct load */
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
;
205 bytes
= chunk
<< sector_shift
;
207 if (tptr
!= ptr
&& is_write
)
208 memcpy(tptr
, ptr
, bytes
);
213 pkt
.size
= sizeof pkt
;
215 pkt
.buf
= FAR_PTR(tptr
);
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) ? "<-" : "->",
224 __intcall(0x13, &ireg
, &oreg
);
225 if (!(oreg
.eflags
.l
& EFLAGS_CF
))
228 dprintf("EDD: error AX = %04x\n", oreg
.eax
.w
[0]);
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.
239 __intcall(0x13, &reset
, NULL
);
241 /* For any starting value, this will always end with ..., 1, 0 */
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.
255 * Try to fall back to CHS. If the LBA is absurd, the
256 * chs_max() test in chs_rdwr_sectors() will catch it.
258 done
= chs_rdwr_sectors(disk
, buf
, lba
- disk
->part_start
,
260 if (done
== (count
<< sector_shift
)) {
261 /* Successful, assume this is a CHS disk */
262 disk
->rdwr_sectors
= chs_rdwr_sectors
;
265 printf("EDD: Error %04x %s sector %llu\n",
267 is_write
? "writing" : "reading",
269 return done
; /* Failure */
272 bytes
= chunk
<< sector_shift
;
274 if (tptr
!= ptr
&& !is_write
)
275 memcpy(ptr
, tptr
, bytes
);
277 /* If we dropped maxtransfer, it eventually worked, so remember it */
278 disk
->maxtransfer
= maxtransfer
;
288 struct disk
*bios_disk_init(void *private)
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
;
303 unsigned int hard_max_transfer
;
305 memset(&ireg
, 0, sizeof ireg
);
306 ireg
.edx
.b
[0] = devno
;
310 * The query functions don't work right on some CD-ROM stacks.
311 * Known affected systems: ThinkPad T22, T23.
315 hard_max_transfer
= 32;
319 hard_max_transfer
= 63;
321 /* CBIOS parameters */
323 disk
.s
= bsSecPerTrack
;
325 if ((int8_t)devno
< 0) {
326 /* Get hard disk geometry from BIOS */
328 ireg
.eax
.b
[1] = 0x08;
329 __intcall(0x13, &ireg
, &oreg
);
331 if (!(oreg
.eflags
.l
& EFLAGS_CF
)) {
332 disk
.h
= oreg
.edx
.b
[1] + 1;
333 disk
.s
= oreg
.ecx
.b
[0] & 63;
337 /* Get EBIOS support */
338 ireg
.eax
.b
[1] = 0x41;
339 ireg
.ebx
.w
[0] = 0x55aa;
340 ireg
.eflags
.b
[0] = 0x3; /* CF set */
342 __intcall(0x13, &ireg
, &oreg
);
344 if (!(oreg
.eflags
.l
& EFLAGS_CF
) &&
345 oreg
.ebx
.w
[0] == 0xaa55 && (oreg
.ecx
.b
[0] & 1)) {
347 hard_max_transfer
= 127;
349 /* Query EBIOS parameters */
350 /* The memset() is needed once this function can be called
352 /* memset(&edd_params, 0, sizeof edd_params); */
353 edd_params
.len
= sizeof edd_params
;
355 ireg
.eax
.b
[1] = 0x48;
356 ireg
.ds
= SEG(&edd_params
);
357 ireg
.esi
.w
[0] = OFFS(&edd_params
);
358 __intcall(0x13, &ireg
, &oreg
);
360 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.eax
.b
[1] == 0) {
361 if (edd_params
.len
< sizeof edd_params
)
362 memset((char *)&edd_params
+ edd_params
.len
, 0,
363 sizeof edd_params
- edd_params
.len
);
365 if (edd_params
.sector_size
>= 512 &&
366 is_power_of_2(edd_params
.sector_size
))
367 sector_size
= edd_params
.sector_size
;
373 disk
.disk_number
= devno
;
374 disk
.sector_size
= sector_size
;
375 disk
.sector_shift
= ilog2(sector_size
);
376 disk
.part_start
= part_start
;
377 disk
.secpercyl
= disk
.h
* disk
.s
;
378 disk
.rdwr_sectors
= ebios
? edd_rdwr_sectors
: chs_rdwr_sectors
;
380 if (!MaxTransfer
|| MaxTransfer
> hard_max_transfer
)
381 MaxTransfer
= hard_max_transfer
;
383 disk
.maxtransfer
= MaxTransfer
;
385 dprintf("disk %02x cdrom %d type %d sector %u/%u offset %llu limit %u\n",
386 devno
, cdrom
, ebios
, sector_size
, disk
.sector_shift
,
387 part_start
, disk
.maxtransfer
);
389 disk
.private = private;
393 void pm_fs_init(com32sys_t
*regs
)
395 static struct bios_disk_private priv
;
398 fs_init((const struct fs_ops
**)regs
->eax
.l
, (void *)&priv
);