source: bootcd/isolinux/syslinux-6.03/core/fs/diskio_bios.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 9.7 KB
Line 
1#include <core.h>
2#include <com32.h>
3#include <fs.h>
4#include <ilog2.h>
5
6#define RETRY_COUNT 6
7
8static inline sector_t chs_max(const struct disk *disk)
9{
10    return (sector_t)disk->secpercyl << 10;
11}
12
13struct edd_rdwr_packet {
14    uint16_t size;
15    uint16_t blocks;
16    far_ptr_t buf;
17    uint64_t lba;
18};
19
20struct 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
40static inline bool is_power_of_2(uint32_t x)
41{
42    return !(x & (x-1));
43}
44
45static 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
163static 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
288struct 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
397void 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}
Note: See TracBrowser for help on using the repository browser.