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 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 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 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 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 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 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 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 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 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 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 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 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 * 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