Releasing debian version 4.04+dfsg-9.
[syslinux-debian/hramrach.git] / core / fs / diskio.c
blob6683816106d9f4e3d6a66026033d82d973bb8b0c
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdbool.h>
5 #include <klibc/compiler.h>
6 #include <core.h>
7 #include <fs.h>
8 #include <disk.h>
9 #include <ilog2.h>
11 #define RETRY_COUNT 6
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)
21 char *ptr = buf;
22 char *tptr;
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) */
26 uint32_t t;
27 uint32_t c, h, s;
28 com32sys_t ireg, oreg;
29 size_t done = 0;
30 size_t bytes;
31 int retry;
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;
42 while (count) {
43 chunk = count;
44 if (chunk > maxtransfer)
45 chunk = maxtransfer;
47 freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
49 if ((size_t)buf <= 0xf0000 && freeseg) {
50 /* Can do a direct load */
51 tptr = ptr;
52 } else {
53 /* Either accessing high memory or we're crossing a 64K line */
54 tptr = core_xfer_buf;
55 freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
57 if (chunk > freeseg)
58 chunk = freeseg;
60 s = xlba % disk->s;
61 t = xlba / disk->s;
62 h = t % disk->h;
63 c = t / disk->h;
65 if (chunk > (disk->s - s))
66 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;
74 ireg.ecx.b[1] = c;
75 ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
76 ireg.edx.b[1] = h;
77 ireg.ebx.w[0] = OFFS(tptr);
78 ireg.es = SEG(tptr);
80 retry = RETRY_COUNT;
82 for (;;) {
83 if (c < 1024) {
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) ? "<-" : "->",
88 ptr);
90 __intcall(0x13, &ireg, &oreg);
91 if (!(oreg.eflags.l & EFLAGS_CF))
92 break;
94 dprintf("CHS: error AX = %04x\n", oreg.eax.w[0]);
96 if (retry--)
97 continue;
100 * For any starting value, this will always end with
101 * ..., 1, 0
103 chunk >>= 1;
104 if (chunk) {
105 maxtransfer = chunk;
106 retry = RETRY_COUNT;
107 ireg.eax.b[0] = chunk;
108 continue;
112 printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
113 oreg.eax.w[0],
114 is_write ? "writing" : "reading",
115 lba, c, h, s+1);
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;
127 ptr += bytes;
128 xlba += chunk;
129 count -= chunk;
130 done += chunk;
133 return done;
136 struct edd_rdwr_packet {
137 uint16_t size;
138 uint16_t blocks;
139 far_ptr_t buf;
140 uint64_t lba;
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;
147 char *ptr = buf;
148 char *tptr;
149 size_t chunk, freeseg;
150 int sector_shift = disk->sector_shift;
151 com32sys_t ireg, oreg, reset;
152 size_t done = 0;
153 size_t bytes;
154 int retry;
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;
161 ireg.ds = SEG(&pkt);
162 ireg.esi.w[0] = OFFS(&pkt);
164 memset(&reset, 0, sizeof reset);
166 lba += disk->part_start;
167 while (count) {
168 chunk = count;
169 if (chunk > maxtransfer)
170 chunk = maxtransfer;
172 freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
174 if ((size_t)ptr <= 0xf0000 && freeseg) {
175 /* Can do a direct load */
176 tptr = ptr;
177 } else {
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;
182 if (chunk > freeseg)
183 chunk = freeseg;
185 bytes = chunk << sector_shift;
187 if (tptr != ptr && is_write)
188 memcpy(tptr, ptr, bytes);
190 retry = RETRY_COUNT;
192 for (;;) {
193 pkt.size = sizeof pkt;
194 pkt.blocks = chunk;
195 pkt.buf = FAR_PTR(tptr);
196 pkt.lba = lba;
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) ? "<-" : "->",
202 ptr);
204 __intcall(0x13, &ireg, &oreg);
205 if (!(oreg.eflags.l & EFLAGS_CF))
206 break;
208 dprintf("EDD: error AX = %04x\n", oreg.eax.w[0]);
210 if (retry--)
211 continue;
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 */
222 chunk >>= 1;
223 if (chunk) {
224 maxtransfer = chunk;
225 retry = RETRY_COUNT;
226 continue;
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,
239 count, is_write);
240 if (done == (count << sector_shift)) {
241 /* Successful, assume this is a CHS disk */
242 disk->rdwr_sectors = chs_rdwr_sectors;
243 return done;
245 printf("EDD: Error %04x %s sector %llu\n",
246 oreg.eax.w[0],
247 is_write ? "writing" : "reading",
248 lba);
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;
260 ptr += bytes;
261 lba += chunk;
262 count -= chunk;
263 done += chunk;
265 return done;
268 struct edd_disk_params {
269 uint16_t len;
270 uint16_t flags;
271 uint32_t phys_c;
272 uint32_t phys_h;
273 uint32_t phys_s;
274 uint64_t sectors;
275 uint16_t sector_size;
276 far_ptr_t dpte;
277 uint16_t devpath_key;
278 uint8_t devpath_len;
279 uint8_t _pad1[3];
280 char bus_type[4];
281 char if_type[8];
282 uint8_t if_path[8];
283 uint8_t dev_path[16];
284 uint8_t _pad2;
285 uint8_t devpath_csum; /* Depends on devpath_len! */
286 } __attribute__((packed));
288 static inline bool is_power_of_2(uint32_t x)
290 return !(x & (x-1));
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;
308 bool ebios;
309 int sector_size;
310 unsigned int hard_max_transfer;
312 memset(&ireg, 0, sizeof ireg);
313 ireg.edx.b[0] = devno;
315 if (cdrom) {
317 * The query functions don't work right on some CD-ROM stacks.
318 * Known affected systems: ThinkPad T22, T23.
320 sector_size = 2048;
321 ebios = true;
322 hard_max_transfer = 32;
323 } else {
324 sector_size = 512;
325 ebios = false;
326 hard_max_transfer = 63;
328 /* CBIOS parameters */
329 disk.h = bsHeads;
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)) {
353 ebios = true;
354 hard_max_transfer = 127;
356 /* Query EBIOS parameters */
357 /* The memset() is needed once this function can be called
358 more than once */
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);
396 return &disk;
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;
418 return &dev;