5 #include <klibc/compiler.h>
13 static inline sector_t
chs_max(const struct disk
*disk
)
15 return (sector_t
)disk
->secpercyl
<< 10;
18 static int chs_rdwr_sectors(struct disk
*disk
, void *buf
,
19 sector_t lba
, size_t count
, bool is_write
)
23 size_t chunk
, freeseg
;
24 int sector_shift
= disk
->sector_shift
;
25 uint32_t xlba
= lba
+ disk
->part_start
; /* Truncated LBA (CHS is << 2 TB) */
28 com32sys_t ireg
, oreg
;
32 uint32_t maxtransfer
= disk
->maxtransfer
;
34 if (lba
+ disk
->part_start
>= chs_max(disk
))
35 return 0; /* Impossible CHS request */
37 memset(&ireg
, 0, sizeof ireg
);
39 ireg
.eax
.b
[1] = 0x02 + is_write
;
40 ireg
.edx
.b
[0] = disk
->disk_number
;
44 if (chunk
> maxtransfer
)
47 freeseg
= (0x10000 - ((size_t)ptr
& 0xffff)) >> sector_shift
;
49 if ((size_t)buf
<= 0xf0000 && freeseg
) {
50 /* Can do a direct load */
53 /* Either accessing high memory or we're crossing a 64K line */
55 freeseg
= (0x10000 - ((size_t)tptr
& 0xffff)) >> sector_shift
;
65 if (chunk
> (disk
->s
- s
))
68 bytes
= chunk
<< sector_shift
;
70 if (tptr
!= ptr
&& is_write
)
71 memcpy(tptr
, ptr
, bytes
);
73 ireg
.eax
.b
[0] = chunk
;
75 ireg
.ecx
.b
[0] = ((c
& 0x300) >> 2) | (s
+1);
77 ireg
.ebx
.w
[0] = OFFS(tptr
);
84 dprintf("CHS[%02x]: %u @ %llu (%u/%u/%u) %04x:%04x %s %p\n",
85 ireg
.edx
.b
[0], chunk
, xlba
, c
, h
, s
+1,
86 ireg
.es
, ireg
.ebx
.w
[0],
87 (ireg
.eax
.b
[1] & 1) ? "<-" : "->",
90 __intcall(0x13, &ireg
, &oreg
);
91 if (!(oreg
.eflags
.l
& EFLAGS_CF
))
94 dprintf("CHS: error AX = %04x\n", oreg
.eax
.w
[0]);
100 * For any starting value, this will always end with
107 ireg
.eax
.b
[0] = chunk
;
112 printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
114 is_write
? "writing" : "reading",
116 return done
; /* Failure */
119 bytes
= chunk
<< sector_shift
;
121 if (tptr
!= ptr
&& !is_write
)
122 memcpy(ptr
, tptr
, bytes
);
124 /* If we dropped maxtransfer, it eventually worked, so remember it */
125 disk
->maxtransfer
= maxtransfer
;
136 struct edd_rdwr_packet
{
143 static int edd_rdwr_sectors(struct disk
*disk
, void *buf
,
144 sector_t lba
, size_t count
, bool is_write
)
146 static __lowmem
struct edd_rdwr_packet pkt
;
149 size_t chunk
, freeseg
;
150 int sector_shift
= disk
->sector_shift
;
151 com32sys_t ireg
, oreg
, reset
;
155 uint32_t maxtransfer
= disk
->maxtransfer
;
157 memset(&ireg
, 0, sizeof ireg
);
159 ireg
.eax
.b
[1] = 0x42 + is_write
;
160 ireg
.edx
.b
[0] = disk
->disk_number
;
162 ireg
.esi
.w
[0] = OFFS(&pkt
);
164 memset(&reset
, 0, sizeof reset
);
166 lba
+= disk
->part_start
;
169 if (chunk
> maxtransfer
)
172 freeseg
= (0x10000 - ((size_t)ptr
& 0xffff)) >> sector_shift
;
174 if ((size_t)ptr
<= 0xf0000 && freeseg
) {
175 /* Can do a direct load */
178 /* Either accessing high memory or we're crossing a 64K line */
179 tptr
= core_xfer_buf
;
180 freeseg
= (0x10000 - ((size_t)tptr
& 0xffff)) >> sector_shift
;
185 bytes
= chunk
<< sector_shift
;
187 if (tptr
!= ptr
&& is_write
)
188 memcpy(tptr
, ptr
, bytes
);
193 pkt
.size
= sizeof pkt
;
195 pkt
.buf
= FAR_PTR(tptr
);
198 dprintf("EDD[%02x]: %u @ %llu %04x:%04x %s %p\n",
199 ireg
.edx
.b
[0], pkt
.blocks
, pkt
.lba
,
200 pkt
.buf
.seg
, pkt
.buf
.offs
,
201 (ireg
.eax
.b
[1] & 1) ? "<-" : "->",
204 __intcall(0x13, &ireg
, &oreg
);
205 if (!(oreg
.eflags
.l
& EFLAGS_CF
))
208 dprintf("EDD: error AX = %04x\n", oreg
.eax
.w
[0]);
214 * Some systems seem to get "stuck" in an error state when
215 * using EBIOS. Doesn't happen when using CBIOS, which is
216 * good, since some other systems get timeout failures
217 * waiting for the floppy disk to spin up.
219 __intcall(0x13, &reset
, NULL
);
221 /* For any starting value, this will always end with ..., 1, 0 */
230 * Total failure. There are systems which identify as
231 * EDD-capable but aren't; the known such systems return
232 * error code AH=1 (invalid function), but let's not
233 * assume that for now.
235 * Try to fall back to CHS. If the LBA is absurd, the
236 * chs_max() test in chs_rdwr_sectors() will catch it.
238 done
= chs_rdwr_sectors(disk
, buf
, lba
- disk
->part_start
,
240 if (done
== (count
<< sector_shift
)) {
241 /* Successful, assume this is a CHS disk */
242 disk
->rdwr_sectors
= chs_rdwr_sectors
;
245 printf("EDD: Error %04x %s sector %llu\n",
247 is_write
? "writing" : "reading",
249 return done
; /* Failure */
252 bytes
= chunk
<< sector_shift
;
254 if (tptr
!= ptr
&& !is_write
)
255 memcpy(ptr
, tptr
, bytes
);
257 /* If we dropped maxtransfer, it eventually worked, so remember it */
258 disk
->maxtransfer
= maxtransfer
;
268 struct edd_disk_params
{
275 uint16_t sector_size
;
277 uint16_t devpath_key
;
283 uint8_t dev_path
[16];
285 uint8_t devpath_csum
; /* Depends on devpath_len! */
286 } __attribute__((packed
));
288 static inline bool is_power_of_2(uint32_t x
)
293 void getoneblk(struct disk
*disk
, char *buf
, block_t block
, int block_size
)
295 int sec_per_block
= block_size
/ disk
->sector_size
;
297 disk
->rdwr_sectors(disk
, buf
, block
* sec_per_block
, sec_per_block
, 0);
301 struct disk
*disk_init(uint8_t devno
, bool cdrom
, sector_t part_start
,
302 uint16_t bsHeads
, uint16_t bsSecPerTrack
,
303 uint32_t MaxTransfer
)
305 static struct disk disk
;
306 static __lowmem
struct edd_disk_params edd_params
;
307 com32sys_t ireg
, oreg
;
310 unsigned int hard_max_transfer
;
312 memset(&ireg
, 0, sizeof ireg
);
313 ireg
.edx
.b
[0] = devno
;
317 * The query functions don't work right on some CD-ROM stacks.
318 * Known affected systems: ThinkPad T22, T23.
322 hard_max_transfer
= 32;
326 hard_max_transfer
= 63;
328 /* CBIOS parameters */
330 disk
.s
= bsSecPerTrack
;
332 if ((int8_t)devno
< 0) {
333 /* Get hard disk geometry from BIOS */
335 ireg
.eax
.b
[1] = 0x08;
336 __intcall(0x13, &ireg
, &oreg
);
338 if (!(oreg
.eflags
.l
& EFLAGS_CF
)) {
339 disk
.h
= oreg
.edx
.b
[1] + 1;
340 disk
.s
= oreg
.ecx
.b
[0] & 63;
344 /* Get EBIOS support */
345 ireg
.eax
.b
[1] = 0x41;
346 ireg
.ebx
.w
[0] = 0x55aa;
347 ireg
.eflags
.b
[0] = 0x3; /* CF set */
349 __intcall(0x13, &ireg
, &oreg
);
351 if (!(oreg
.eflags
.l
& EFLAGS_CF
) &&
352 oreg
.ebx
.w
[0] == 0xaa55 && (oreg
.ecx
.b
[0] & 1)) {
354 hard_max_transfer
= 127;
356 /* Query EBIOS parameters */
357 /* The memset() is needed once this function can be called
359 /* memset(&edd_params, 0, sizeof edd_params); */
360 edd_params
.len
= sizeof edd_params
;
362 ireg
.eax
.b
[1] = 0x48;
363 ireg
.ds
= SEG(&edd_params
);
364 ireg
.esi
.w
[0] = OFFS(&edd_params
);
365 __intcall(0x13, &ireg
, &oreg
);
367 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.eax
.b
[1] == 0) {
368 if (edd_params
.len
< sizeof edd_params
)
369 memset((char *)&edd_params
+ edd_params
.len
, 0,
370 sizeof edd_params
- edd_params
.len
);
372 if (edd_params
.sector_size
>= 512 &&
373 is_power_of_2(edd_params
.sector_size
))
374 sector_size
= edd_params
.sector_size
;
380 disk
.disk_number
= devno
;
381 disk
.sector_size
= sector_size
;
382 disk
.sector_shift
= ilog2(sector_size
);
383 disk
.part_start
= part_start
;
384 disk
.secpercyl
= disk
.h
* disk
.s
;
385 disk
.rdwr_sectors
= ebios
? edd_rdwr_sectors
: chs_rdwr_sectors
;
387 if (!MaxTransfer
|| MaxTransfer
> hard_max_transfer
)
388 MaxTransfer
= hard_max_transfer
;
390 disk
.maxtransfer
= MaxTransfer
;
392 dprintf("disk %02x cdrom %d type %d sector %u/%u offset %llu limit %u\n",
393 devno
, cdrom
, ebios
, sector_size
, disk
.sector_shift
,
394 part_start
, disk
.maxtransfer
);
401 * Initialize the device structure.
403 * NOTE: the disk cache needs to be revamped to support multiple devices...
405 struct device
* device_init(uint8_t devno
, bool cdrom
, sector_t part_start
,
406 uint16_t bsHeads
, uint16_t bsSecPerTrack
,
407 uint32_t MaxTransfer
)
409 static struct device dev
;
410 static __hugebss
char diskcache
[128*1024];
412 dev
.disk
= disk_init(devno
, cdrom
, part_start
,
413 bsHeads
, bsSecPerTrack
, MaxTransfer
);
415 dev
.cache_data
= diskcache
;
416 dev
.cache_size
= sizeof diskcache
;