1 /*
2 * boot driver for BIOS devices
3 */
4 #include <u.h>
5 #include "lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "fs.h"
12
13 typedef uvlong Devbytes, Devsects;
14
15 typedef struct Biosdrive Biosdrive; /* 1 drive -> ndevs */
16 typedef struct Biosdev Biosdev;
17
18 enum {
19 Debug = 0,
20 Maxdevs = 4,
21
22 CF = 1,
23 Flopid = 0, /* first floppy */
24 Baseid = 0x80, /* first disk */
25
26 /* bios calls: int 13 disk services */
27 Biosinit = 0, /* initialise disk & floppy ctlrs */
28 Biosdrvsts,
29 Bioschsrdsects,
30 Biosdrvparam = 8,
31 Biosctlrinit,
32 Biosreset = 0xd, /* reset disk */
33 Biosdrvrdy = 0x10,
34 Biosdrvtype = 0x15,
35 Biosckext = 0x41,
36 Biosrdsect,
37 Biosedrvparam = 0x48,
38
39 /* disk types */
40 Typenone = 0,
41 Typedisk = 3,
42 };
43
44 struct Biosdrive {
45 int ndevs;
46 };
47 struct Biosdev {
48 Devbytes size;
49 Devbytes offset;
50 uchar id; /* drive number; e.g., 0x80 */
51 char type;
52 ushort sectsz;
53 };
54
55 typedef struct Extread {
56 uchar size;
57 uchar unused1;
58 uchar nsects;
59 uchar unused2;
60 ulong addr; /* segment:offset */
61 uvlong stsect; /* starting sector */
62 } Extread;
63 typedef struct Edrvparam {
64 /* from edd 1.1 spec */
65 ushort size; /* max. buffer size */
66 ushort flags;
67 ulong physcyls;
68 ulong physheads;
69 ulong phystracksects;
70 uvlong physsects;
71 ushort sectsz;
72 void *dpte; /* ~0ull: invalid */
73
74 /* remainder from edd 3.0 spec */
75 ushort key; /* 0xbedd if present */
76 uchar dpilen;
77 uchar unused1;
78 ushort unused2;
79 char bustype[4]; /* "PCI" or "ISA" */
80 char ifctype[8]; /* "ATA", "ATAPI", "SCSI", "USB", "1394", "FIBRE" */
81 uvlong ifcpath;
82 uvlong devpath;
83 uchar unused3;
84 uchar dpicksum;
85 } Edrvparam;
86
87 void realmode(int intr, Ureg *ureg); /* from trap.c */
88
89 int onlybios0;
90 int biosinited;
91
92 static Biosdev bdev[Maxdevs];
93 static Biosdrive bdrive;
94 static Ureg regs;
95
96 static int dreset(uchar drive);
97 static Devbytes extgetsize(Biosdev *);
98 static Devsects getsize(uchar drive, char *type);
99 static int islba(uchar drive);
100
101 static int
biosdiskcall(Ureg * rp,uchar op,ulong bx,ulong dx,ulong si)102 biosdiskcall(Ureg *rp, uchar op, ulong bx, ulong dx, ulong si)
103 {
104 memset(rp, 0, sizeof *rp);
105 rp->ax = op << 8;
106 rp->bx = bx;
107 rp->dx = dx; /* often drive id */
108 rp->si = si;
109 /* pass command in *rp, get results from there */
110 realmode(0x13, rp);
111 if (rp->flags & CF) {
112 // print("biosdiskcall: int 13 op 0x%ux drive 0x%lux failed, "
113 // "ah error code 0x%ux\n", op, dx, (uchar)(rp->ax >> 8));
114 return -1;
115 }
116 return 0;
117 }
118
119 /*
120 * Find out what the bios knows about devices.
121 * our boot device could be usb; ghod only knows where it will appear.
122 */
123 int
biosinit(void)124 biosinit(void)
125 {
126 int devid, lba, mask, lastbit;
127 Devbytes size;
128 char type;
129 Biosdev *bdp;
130 static int beenhere;
131
132 mask = lastbit = 0;
133 if (beenhere)
134 return mask;
135 beenhere = 1;
136 /* 9pxeload can't use bios int 13 calls; they wedge the machine */
137 if (pxe || getconf("*nobiosload") != nil || onlybios0 || !biosinited)
138 return mask;
139 for (devid = 0; devid < (1 << 8) && bdrive.ndevs < Maxdevs; devid++) {
140 lba = islba(devid);
141 if(!lba /* || devid != Baseid && dreset(devid) < 0 */ )
142 continue;
143 type = Typedisk; /* HACK */
144 if (getsize(devid, &type) == 0) { /* no device, end of range */
145 devid &= ~0xf;
146 devid += 0x10;
147 devid--;
148 continue;
149 }
150 lastbit = 1 << bdrive.ndevs;
151 mask |= lastbit;
152 bdp = &bdev[bdrive.ndevs];
153 bdp->id = devid;
154 bdp->type = type;
155 size = extgetsize(bdp);
156 bdp->size = size;
157 print("bios%d: drive 0x%ux: %llud bytes, type %d\n",
158 bdrive.ndevs, devid, size, type);
159 bdrive.ndevs++;
160 }
161 /*
162 * bioses seem to only be able to read from drive number 0x80
163 * and certainly can't read from the highest drive number when we
164 * call them, even if there is only one. attempting to read from
165 * the last drive number yields a hung machine or a two-minute pause.
166 */
167 if (bdrive.ndevs > 0) {
168 if (bdrive.ndevs == 1) {
169 print("biosinit: sorry, only one bios drive; "
170 "can't read last one\n");
171 onlybios0 = 1;
172 } else
173 biosinited = 1;
174 bdrive.ndevs--; /* omit last drive number; it can't be read */
175 mask &= ~lastbit;
176 }
177 return mask;
178 }
179
180 void
biosinitdev(int i,char * name)181 biosinitdev(int i, char *name)
182 {
183 if(i >= bdrive.ndevs)
184 panic("biosinitdev");
185 sprint(name, "bios%d", i);
186 }
187
188 void
biosprintdevs(int i)189 biosprintdevs(int i)
190 {
191 if(i >= bdrive.ndevs){
192 print("got a print for %d, only got %d\n", i, bdrive.ndevs);
193 panic("biosprintdevs");
194 }
195 print(" bios%d", i);
196 }
197
198 int
biosboot(int dev,char * file,Boot * b)199 biosboot(int dev, char *file, Boot *b)
200 {
201 Fs *fs;
202
203 if(strncmp(file, "dos!", 4) == 0)
204 file += 4;
205 if(strchr(file, '!') != nil || strcmp(file, "") == 0) {
206 print("syntax is bios0!file\n");
207 return -1;
208 }
209
210 fs = biosgetfspart(dev, "9fat", 1);
211 if(fs == nil)
212 return -1;
213 return fsboot(fs, file, b);
214 }
215
216 /* read n bytes at sector offset into a from drive id */
217 long
sectread(Biosdev * bdp,void * a,long n,Devsects offset)218 sectread(Biosdev *bdp, void *a, long n, Devsects offset)
219 {
220 uchar *biosparam, *cp;
221 Extread *erp;
222
223 if(n < 0 || n > bdp->sectsz)
224 return -1;
225 if(Debug)
226 memset((uchar *)BIOSXCHG, 'r', bdp->sectsz); /* preclean the buffer. */
227
228 biosdiskcall(®s, Biosdrvrdy, 0, bdp->id, 0);
229
230 /* space for a BIG sector, just in case... */
231 biosparam = (uchar *)BIOSXCHG + 2*1024;
232
233 /* read into BIOSXCHG */
234 erp = (Extread *)biosparam;
235 memset(erp, 0, sizeof *erp);
236 erp->size = sizeof *erp;
237 erp->nsects = 1;
238 erp->addr = PADDR(BIOSXCHG);
239 erp->stsect = offset;
240 if (biosdiskcall(®s, Biosrdsect, 0, bdp->id, PADDR(erp)) < 0) {
241 print("sectread: bios failed to read %ld @ sector %lld of 0x%ux\n",
242 n, offset, bdp->id);
243 return -1;
244 }
245
246 /* copy into caller's buffer */
247 memmove(a, (char *)BIOSXCHG, n);
248 if(Debug){
249 cp = (uchar *)BIOSXCHG;
250 print("-%ux %ux %ux %ux--%16.16s-\n",
251 cp[0], cp[1], cp[2], cp[3], (char *)cp + 480);
252 }
253 return n;
254 }
255
256 /* not tested yet. */
257 static int
dreset(uchar drive)258 dreset(uchar drive)
259 {
260 if (0) {
261 print("devbios: resetting disk controllers...");
262 biosdiskcall(®s, Biosinit, 0, drive, 0);
263 print("\n");
264 }
265 return regs.ax? -1: 0; /* ax!=0 on error */
266 }
267
268 static int
islba(uchar drive)269 islba(uchar drive)
270 {
271 if (biosdiskcall(®s, Biosckext, 0x55aa, drive, 0) < 0)
272 return 0;
273 if(regs.bx != 0xaa55){
274 print("islba: buggy bios\n");
275 return 0;
276 }
277 if (Debug)
278 print("islba: drive 0x%ux extensions version %d.%d cx 0x%lux\n",
279 drive, (uchar)(regs.ax >> 8),
280 (uchar)regs.ax, regs.cx); /* cx: 4=edd, 1=use dap */
281 return regs.cx & 1; /* dap bit */
282 }
283
284 /*
285 * works so so... some floppies are 0x80+x when they shouldn't be,
286 * and report lba even if they cannot...
287 */
288 static Devsects
getsize(uchar id,char * typep)289 getsize(uchar id, char *typep)
290 {
291 int dtype;
292
293 if (biosdiskcall(®s, Biosdrvtype, 0x55aa, id, 0) < 0)
294 return 0;
295
296 dtype = (ushort)regs.ax >> 8;
297 if(dtype == Typenone){
298 print("no such device 0x%ux of type %d\n", id, dtype);
299 return 0;
300 }
301 if(dtype != Typedisk){
302 print("non-disk device 0x%ux of type %d\n", id, dtype);
303 return 0;
304 }
305 *typep = dtype;
306 return (ushort)regs.cx | regs.dx << 16;
307 }
308
309 /* extended get size */
310 static Devbytes
extgetsize(Biosdev * bdp)311 extgetsize(Biosdev *bdp)
312 {
313 Edrvparam *edp;
314
315 edp = (Edrvparam *)BIOSXCHG;
316 memset(edp, 0, sizeof *edp);
317 edp->size = sizeof *edp;
318 edp->dpilen = 36;
319 if (biosdiskcall(®s, Biosedrvparam, 0, bdp->id, PADDR(edp)) < 0)
320 return 0;
321 if(Debug) {
322 print("extgetsize: drive 0x%ux info flags 0x%ux",
323 bdp->id, edp->flags);
324 if (edp->key == 0xbedd)
325 print(" %s %s", edp->bustype, edp->ifctype);
326 print("\n");
327 }
328 if (edp->sectsz <= 0) {
329 print("extgetsize: drive 0x%ux: non-positive sector size\n",
330 bdp->id);
331 edp->sectsz = 1; /* don't divide by zero */
332 }
333 bdp->sectsz = edp->sectsz;
334 return edp->physsects * edp->sectsz;
335 }
336
337 long
biosread(Fs * fs,void * a,long n)338 biosread(Fs *fs, void *a, long n)
339 {
340 int want, got, part;
341 long totnr, stuck;
342 Devbytes offset;
343 Biosdev *bdp;
344
345 if(fs->dev > bdrive.ndevs)
346 return -1;
347 if (n <= 0)
348 return n;
349 bdp = &bdev[fs->dev];
350 offset = bdp->offset;
351 stuck = 0;
352 for (totnr = 0; totnr < n && stuck < 4; totnr += got) {
353 want = bdp->sectsz;
354 if (totnr + want > n)
355 want = n - totnr;
356 if(Debug)
357 print("bios%d, read: %ld @ off %lld, want: %d, id: 0x%ux\n",
358 fs->dev, n, offset, want, bdp->id);
359 part = offset % bdp->sectsz;
360 if (part != 0) { /* back up to start of sector */
361 offset -= part;
362 totnr -= part;
363 if (totnr < 0) {
364 print("biosread: negative count %ld\n", totnr);
365 return -1;
366 }
367 }
368 if ((vlong)offset < 0) {
369 print("biosread: negative offset %lld\n", offset);
370 return -1;
371 }
372 got = sectread(bdp, (char *)a + totnr, want, offset/bdp->sectsz);
373 if(got <= 0){
374 // print("biosread: failed to read %ld @ off %lld of 0x%ux, "
375 // "want %d got %d\n",
376 // n, offset, bdp->id, want, got);
377 return -1;
378 }
379 offset += got;
380 bdp->offset = offset;
381 if (got < bdp->sectsz)
382 stuck++; /* we'll have to re-read this sector */
383 else
384 stuck = 0;
385 }
386 return totnr;
387 }
388
389 vlong
biosseek(Fs * fs,vlong off)390 biosseek(Fs *fs, vlong off)
391 {
392 if (off < 0) {
393 print("biosseek(fs, %lld) is illegal\n", off);
394 return -1;
395 }
396 if(fs->dev > bdrive.ndevs) {
397 print("biosseek: fs->dev %d > bdrive.ndevs %d\n",
398 fs->dev, bdrive.ndevs);
399 return -1;
400 }
401 bdev[fs->dev].offset = off; /* do not know size... (yet) */
402 return off;
403 }
404
405 void *
biosgetfspart(int i,char * name,int chatty)406 biosgetfspart(int i, char *name, int chatty)
407 {
408 static Fs fs;
409
410 if(strcmp(name, "9fat") != 0){
411 if(chatty)
412 print("unknown partition bios%d!%s (use bios%d!9fat)\n",
413 i, name, i);
414 return nil;
415 }
416
417 fs.dev = i;
418 fs.diskread = biosread;
419 fs.diskseek = biosseek;
420
421 if(dosinit(&fs) < 0){
422 if(chatty)
423 print("bios%d!%s does not contain a FAT file system\n",
424 i, name);
425 return nil;
426 }
427 return &fs;
428 }
429