• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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