1 /* 2 * Copyright 2003-2005, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "bios.h" 8 9 #include <KernelExport.h> 10 #include <boot/platform.h> 11 #include <boot/partitions.h> 12 #include <boot/stdio.h> 13 #include <boot/stage2.h> 14 15 #include <string.h> 16 17 //#define TRACE_DEVICES 18 #ifdef TRACE_DEVICES 19 # define TRACE(x) dprintf x 20 #else 21 # define TRACE(x) ; 22 #endif 23 24 25 // exported from shell.S 26 extern uint8 gBootedFromImage; 27 extern uint8 gBootDriveID; 28 extern uint32 gBootPartitionOffset; 29 30 // int 0x13 definitions 31 #define BIOS_RESET_DISK_SYSTEM 0x0000 32 #define BIOS_READ 0x0200 33 #define BIOS_GET_DRIVE_PARAMETERS 0x0800 34 #define BIOS_IS_EXT_PRESENT 0x4100 35 #define BIOS_EXT_READ 0x4200 36 #define BIOS_GET_EXT_DRIVE_PARAMETERS 0x4800 37 #define BIOS_BOOT_CD_GET_STATUS 0x4b01 38 39 struct real_addr { 40 uint16 offset; 41 uint16 segment; 42 }; 43 44 struct disk_address_packet { 45 uint8 size; 46 uint8 reserved; 47 uint16 number_of_blocks; 48 uint32 buffer; 49 uint64 lba; 50 uint64 flat_buffer; 51 }; 52 53 static const uint16 kParametersSizeVersion1 = 0x1a; 54 static const uint16 kParametersSizeVersion2 = 0x1e; 55 static const uint16 kParametersSizeVersion3 = 0x42; 56 57 static const uint16 kDevicePathSignature = 0xbedd; 58 59 struct drive_parameters { 60 uint16 parameters_size; 61 uint16 flags; 62 uint32 cylinders; 63 uint32 heads; 64 uint32 sectors_per_track; 65 uint64 sectors; 66 uint16 bytes_per_sector; 67 /* edd 2.0 */ 68 real_addr device_table; 69 /* edd 3.0 */ 70 uint16 device_path_signature; 71 uint8 device_path_size; 72 uint8 reserved1[3]; 73 char host_bus[4]; 74 char interface_type[8]; 75 union { 76 struct { 77 uint16 base_address; 78 } legacy; 79 struct { 80 uint8 bus; 81 uint8 slot; 82 uint8 function; 83 } pci; 84 uint8 reserved[8]; 85 } interface; 86 union { 87 struct { 88 uint8 slave; 89 } ata; 90 struct { 91 uint8 slave; 92 uint8 logical_unit; 93 } atapi; 94 struct { 95 uint8 logical_unit; 96 } scsi; 97 struct { 98 uint8 tbd; 99 } usb; 100 struct { 101 uint64 guid; 102 } firewire; 103 struct { 104 uint64 wwd; 105 } fibre; 106 } device; 107 uint8 reserved2; 108 uint8 checksum; 109 } _PACKED; 110 111 struct device_table { 112 uint16 base_address; 113 uint16 control_port_address; 114 uint8 _reserved1 : 4; 115 uint8 is_slave : 1; 116 uint8 _reserved2 : 1; 117 uint8 lba_enabled : 1; 118 } _PACKED; 119 120 struct specification_packet { 121 uint8 size; 122 uint8 media_type; 123 uint8 drive_number; 124 uint8 controller_index; 125 uint32 start_emulation; 126 uint16 device_specification; 127 uint8 _more_[9]; 128 } _PACKED; 129 130 class BIOSDrive : public Node { 131 public: 132 BIOSDrive(uint8 driveID); 133 virtual ~BIOSDrive(); 134 135 status_t InitCheck() const; 136 137 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize); 138 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize); 139 140 virtual off_t Size() const; 141 142 uint32 BlockSize() const { return fBlockSize; } 143 144 bool HasParameters() const { return fHasParameters; } 145 const drive_parameters &Parameters() const { return fParameters; } 146 uint8 DriveID() const { return fDriveID; } 147 148 protected: 149 uint8 fDriveID; 150 bool fLBA; 151 uint64 fSize; 152 uint32 fBlockSize; 153 bool fHasParameters; 154 drive_parameters fParameters; 155 }; 156 157 158 static void 159 check_cd_boot(BIOSDrive *drive) 160 { 161 gKernelArgs.boot_disk.cd = false; 162 163 if (drive->DriveID() != 0) 164 return; 165 166 struct bios_regs regs; 167 regs.eax = BIOS_BOOT_CD_GET_STATUS; 168 regs.edx = 0; 169 regs.esi = kDataSegmentScratch; 170 call_bios(0x13, ®s); 171 172 if ((regs.flags & CARRY_FLAG) != 0) 173 return; 174 175 // we obviously were booted from CD! 176 177 specification_packet *packet = (specification_packet *)kDataSegmentScratch; 178 gKernelArgs.boot_disk.cd = packet->media_type != 0; 179 180 #if 0 181 dprintf("got CD boot spec:\n"); 182 dprintf(" size: %#x\n", packet->size); 183 dprintf(" media type: %u\n", packet->media_type); 184 dprintf(" drive_number: %u\n", packet->drive_number); 185 dprintf(" controller index: %u\n", packet->controller_index); 186 dprintf(" start emulation: %lu\n", packet->start_emulation); 187 dprintf(" device_specification: %u\n", packet->device_specification); 188 #endif 189 } 190 191 192 static status_t 193 get_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters) 194 { 195 drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch; 196 197 memset(parameter, 0, sizeof(drive_parameters)); 198 parameter->parameters_size = sizeof(drive_parameters); 199 200 struct bios_regs regs; 201 regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS; 202 regs.edx = drive; 203 regs.esi = (addr_t)parameter - kDataSegmentBase; 204 call_bios(0x13, ®s); 205 206 // filter out faulty BIOS return codes 207 if ((regs.flags & CARRY_FLAG) != 0 208 || parameter->sectors == 0) 209 return B_ERROR; 210 211 memcpy(targetParameters, parameter, sizeof(drive_parameters)); 212 return B_OK; 213 } 214 215 216 static status_t 217 get_drive_parameters(uint8 drive, drive_parameters *parameters) 218 { 219 struct bios_regs regs; 220 regs.eax = BIOS_GET_DRIVE_PARAMETERS; 221 regs.edx = drive; 222 regs.es = 0; 223 regs.edi = 0; // guard against faulty BIOS, see Ralf Brown's interrupt list 224 call_bios(0x13, ®s); 225 226 if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0) 227 return B_ERROR; 228 229 // fill drive_parameters structure with useful values 230 parameters->parameters_size = kParametersSizeVersion1; 231 parameters->flags = 0; 232 parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1; 233 parameters->heads = ((regs.edx >> 8) & 0xff) + 1; 234 // heads and cylinders start counting from 0 235 parameters->sectors_per_track = regs.ecx & 0x3f; 236 parameters->sectors = parameters->cylinders * parameters->heads 237 * parameters->sectors_per_track; 238 parameters->bytes_per_sector = 512; 239 240 return B_OK; 241 } 242 243 244 static status_t 245 get_number_of_drives(uint8 *_count) 246 { 247 struct bios_regs regs; 248 regs.eax = BIOS_GET_DRIVE_PARAMETERS; 249 regs.edx = 0x80; 250 regs.es = 0; 251 regs.edi = 0; 252 call_bios(0x13, ®s); 253 254 if (regs.flags & CARRY_FLAG) 255 return B_ERROR; 256 257 *_count = regs.edx & 0xff; 258 return B_OK; 259 } 260 261 262 /** parse EDD 3.0 drive path information */ 263 264 static status_t 265 fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters ¶meters) 266 { 267 if (parameters.parameters_size < kParametersSizeVersion3 268 || parameters.device_path_signature != kDevicePathSignature) 269 return B_BAD_TYPE; 270 271 // parse host bus 272 273 if (!strncmp(parameters.host_bus, "PCI", 3)) { 274 disk.bus_type = PCI_BUS; 275 276 disk.bus.pci.bus = parameters.interface.pci.bus; 277 disk.bus.pci.slot = parameters.interface.pci.slot; 278 disk.bus.pci.function = parameters.interface.pci.function; 279 } else if (!strncmp(parameters.host_bus, "ISA", 3)) { 280 disk.bus_type = LEGACY_BUS; 281 282 disk.bus.legacy.base_address = parameters.interface.legacy.base_address; 283 } else { 284 dprintf("unknown host bus \"%s\"\n", parameters.host_bus); 285 return B_BAD_DATA; 286 } 287 288 // parse interface 289 290 if (!strncmp(parameters.interface_type, "ATA", 3)) { 291 disk.device_type = ATA_DEVICE; 292 disk.device.ata.master = !parameters.device.ata.slave; 293 } else if (!strncmp(parameters.interface_type, "ATAPI", 3)) { 294 disk.device_type = ATAPI_DEVICE; 295 disk.device.atapi.master = !parameters.device.ata.slave; 296 disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit; 297 } else if (!strncmp(parameters.interface_type, "SCSI", 3)) { 298 disk.device_type = SCSI_DEVICE; 299 disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit; 300 } else if (!strncmp(parameters.interface_type, "USB", 3)) { 301 disk.device_type = USB_DEVICE; 302 disk.device.usb.tbd = parameters.device.usb.tbd; 303 } else if (!strncmp(parameters.interface_type, "1394", 3)) { 304 disk.device_type = FIREWIRE_DEVICE; 305 disk.device.firewire.guid = parameters.device.firewire.guid; 306 } else if (!strncmp(parameters.interface_type, "FIBRE", 3)) { 307 disk.device_type = FIBRE_DEVICE; 308 disk.device.fibre.wwd = parameters.device.fibre.wwd; 309 } else { 310 dprintf("unknown interface type \"%s\"\n", parameters.interface_type); 311 return B_BAD_DATA; 312 } 313 314 return B_OK; 315 } 316 317 318 /** EDD 2.0 drive table information */ 319 320 static status_t 321 fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters ¶meters) 322 { 323 if (parameters.device_table.segment == 0xffff 324 && parameters.device_table.offset == 0xffff) 325 return B_BAD_TYPE; 326 327 device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment, 328 parameters.device_table.offset); 329 330 disk.bus_type = LEGACY_BUS; 331 disk.bus.legacy.base_address = table->base_address; 332 333 disk.device_type = ATA_DEVICE; 334 disk.device.ata.master = !table->is_slave; 335 336 return B_OK; 337 } 338 339 340 #define DUMPED_BLOCK_SIZE 16 341 342 void 343 dumpBlock(const char *buffer, int size, const char *prefix) 344 { 345 int i; 346 347 for (i = 0; i < size;) { 348 int start = i; 349 350 dprintf(prefix); 351 for (; i < start+DUMPED_BLOCK_SIZE; i++) { 352 if (!(i % 4)) 353 dprintf(" "); 354 355 if (i >= size) 356 dprintf(" "); 357 else 358 dprintf("%02x", *(unsigned char *)(buffer + i)); 359 } 360 dprintf(" "); 361 362 for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) { 363 if (i < size) { 364 char c = buffer[i]; 365 366 if (c < 30) 367 dprintf("."); 368 else 369 dprintf("%c", c); 370 } else 371 break; 372 } 373 dprintf("\n"); 374 } 375 } 376 377 378 // #pragma mark - 379 380 381 BIOSDrive::BIOSDrive(uint8 driveID) 382 : 383 fDriveID(driveID) 384 { 385 TRACE(("drive ID %u\n", driveID)); 386 387 if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) { 388 // old style CHS support 389 390 if (get_drive_parameters(driveID, &fParameters) != B_OK) { 391 dprintf("getting drive parameters for: %u failed!\n", fDriveID); 392 return; 393 } 394 395 TRACE((" cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n", 396 fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track, 397 fParameters.bytes_per_sector)); 398 TRACE((" total sectors: %Ld\n", fParameters.sectors)); 399 400 fBlockSize = 512; 401 fSize = fParameters.sectors * fBlockSize; 402 fLBA = false; 403 fHasParameters = false; 404 } else { 405 TRACE(("size: %x\n", fParameters.parameters_size)); 406 TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature)); 407 TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus, 408 fParameters.interface_type)); 409 TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n", 410 fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track, 411 fParameters.bytes_per_sector)); 412 TRACE(("total sectors: %Ld\n", fParameters.sectors)); 413 414 fBlockSize = fParameters.bytes_per_sector; 415 fSize = fParameters.sectors * fBlockSize; 416 fLBA = true; 417 fHasParameters = true; 418 } 419 } 420 421 422 BIOSDrive::~BIOSDrive() 423 { 424 } 425 426 427 status_t 428 BIOSDrive::InitCheck() const 429 { 430 return fSize > 0 ? B_OK : B_ERROR; 431 } 432 433 434 ssize_t 435 BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 436 { 437 uint32 offset = pos % fBlockSize; 438 pos /= fBlockSize; 439 440 uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize; 441 int32 totalBytesRead = 0; 442 443 TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n", 444 blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID)); 445 446 uint32 scratchSize = 24 * 1024 / fBlockSize; 447 // maximum value allowed by Phoenix BIOS is 0x7f 448 449 while (blocksLeft > 0) { 450 uint32 blocksRead = blocksLeft; 451 if (blocksRead > scratchSize) 452 blocksRead = scratchSize; 453 454 if (fLBA) { 455 struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch; 456 memset(packet, 0, sizeof(disk_address_packet)); 457 458 packet->size = sizeof(disk_address_packet); 459 packet->number_of_blocks = blocksRead; 460 packet->buffer = kExtraSegmentScratch; 461 packet->lba = pos; 462 463 struct bios_regs regs; 464 regs.eax = BIOS_EXT_READ; 465 regs.edx = fDriveID; 466 regs.esi = (addr_t)packet - kDataSegmentBase; 467 call_bios(0x13, ®s); 468 469 if (regs.flags & CARRY_FLAG) 470 goto chs_read; 471 } else { 472 chs_read: 473 // Old style CHS read routine 474 475 // We can only read up to 64 kB this way, but since scratchSize 476 // is actually lower than this value, we don't have to take care 477 // of this here. 478 479 uint32 sector = pos % fParameters.sectors_per_track + 1; 480 // sectors start countint at 1 (unlike head and cylinder) 481 uint32 head = pos / fParameters.sectors_per_track; 482 uint32 cylinder = head / fParameters.heads; 483 head %= fParameters.heads; 484 485 if (cylinder >= fParameters.cylinders) 486 return B_BAD_VALUE; 487 488 // try to read from the device more than once, just to make sure it'll work 489 struct bios_regs regs; 490 int32 tries = 3; 491 492 while (tries-- > 0) { 493 regs.eax = BIOS_READ | blocksRead; 494 regs.edx = fDriveID | (head << 8); 495 regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8); 496 regs.es = 0; 497 regs.ebx = kExtraSegmentScratch; 498 call_bios(0x13, ®s); 499 500 if ((regs.flags & CARRY_FLAG) == 0) 501 break; 502 503 if (tries < 2) { 504 // reset disk system 505 regs.eax = BIOS_RESET_DISK_SYSTEM; 506 regs.edx = fDriveID; 507 call_bios(0x13, ®s); 508 } 509 510 // wait a bit between the retries (1/20 sec) 511 spin(50000); 512 } 513 514 if (regs.flags & CARRY_FLAG) { 515 dprintf("reading %ld bytes from drive %u failed at %Ld\n", 516 blocksRead, fDriveID, pos); 517 return B_ERROR; 518 } 519 } 520 521 uint32 bytesRead = fBlockSize * blocksRead - offset; 522 // copy no more than bufferSize bytes 523 if (bytesRead > bufferSize) 524 bytesRead = bufferSize; 525 526 memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead); 527 pos += blocksRead; 528 offset = 0; 529 blocksLeft -= blocksRead; 530 bufferSize -= bytesRead; 531 buffer = (void *)((addr_t)buffer + bytesRead); 532 totalBytesRead += bytesRead; 533 } 534 535 return totalBytesRead; 536 } 537 538 539 ssize_t 540 BIOSDrive::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize) 541 { 542 // we don't have to know how to write 543 return B_NOT_ALLOWED; 544 } 545 546 547 off_t 548 BIOSDrive::Size() const 549 { 550 return fSize; 551 } 552 553 554 // #pragma mark - 555 556 557 status_t 558 platform_get_boot_device(struct stage2_args *args, Node **_device) 559 { 560 TRACE(("boot drive ID: %x\n", gBootDriveID)); 561 562 BIOSDrive *drive = new BIOSDrive(gBootDriveID); 563 if (drive->InitCheck() != B_OK) { 564 dprintf("no boot drive!\n"); 565 return B_ERROR; 566 } 567 568 TRACE(("drive size: %Ld bytes\n", drive->Size())); 569 gKernelArgs.boot_disk.booted_from_image = gBootedFromImage; 570 571 *_device = drive; 572 return B_OK; 573 } 574 575 576 status_t 577 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice, 578 NodeList *list, boot::Partition **_partition) 579 { 580 BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice); 581 off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize(); 582 583 dprintf("boot partition offset: %Ld\n", offset); 584 585 NodeIterator iterator = list->GetIterator(); 586 boot::Partition *partition = NULL; 587 while ((partition = (boot::Partition *)iterator.Next()) != NULL) { 588 TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size)); 589 // search for the partition that contains the partition 590 // offset as reported by the BFS boot block 591 if (offset >= partition->offset 592 && offset < partition->offset + partition->size) { 593 *_partition = partition; 594 return B_OK; 595 } 596 } 597 598 return B_ENTRY_NOT_FOUND; 599 } 600 601 602 status_t 603 platform_add_block_devices(stage2_args *args, NodeList *devicesList) 604 { 605 uint8 driveCount; 606 if (get_number_of_drives(&driveCount) != B_OK) 607 return B_ERROR; 608 609 dprintf("number of drives: %d\n", driveCount); 610 611 for (int32 i = 0; i < driveCount; i++) { 612 uint8 driveID = i + 0x80; 613 if (driveID == gBootDriveID) 614 continue; 615 616 BIOSDrive *drive = new BIOSDrive(driveID); 617 if (drive->InitCheck() != B_OK) { 618 dprintf("could not add drive %u\n", driveID); 619 delete drive; 620 continue; 621 } 622 623 devicesList->Add(drive); 624 } 625 626 return B_OK; 627 } 628 629 630 status_t 631 platform_register_boot_device(Node *device) 632 { 633 disk_identifier &disk = gKernelArgs.boot_disk.identifier; 634 BIOSDrive *drive = (BIOSDrive *)device; 635 636 gKernelArgs.platform_args.boot_drive_number = gBootDriveID; 637 check_cd_boot(drive); 638 639 if (drive->HasParameters()) { 640 const drive_parameters ¶meters = drive->Parameters(); 641 642 // try all drive_parameters versions, beginning from the most informative 643 644 if (fill_disk_identifier_v3(disk, parameters) == B_OK) 645 return B_OK; 646 647 if (fill_disk_identifier_v2(disk, parameters) == B_OK) 648 return B_OK; 649 650 // no interesting information, we have to fall back to the default 651 // unknown interface/device type identifier 652 } 653 654 // ToDo: register all other BIOS drives as well! 655 656 // not yet implemented correctly!!! 657 658 dprintf("legacy drive identification\n"); 659 660 disk.bus_type = UNKNOWN_BUS; 661 disk.device_type = UNKNOWN_DEVICE; 662 disk.device.unknown.size = drive->Size(); 663 664 return B_OK; 665 } 666 667