1 /* 2 * Copyright 2003-2006, 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_EXT_WRITE 0x4300 37 #define BIOS_GET_EXT_DRIVE_PARAMETERS 0x4800 38 #define BIOS_BOOT_CD_GET_STATUS 0x4b01 39 40 struct real_addr { 41 uint16 offset; 42 uint16 segment; 43 }; 44 45 struct disk_address_packet { 46 uint8 size; 47 uint8 reserved; 48 uint16 number_of_blocks; 49 uint32 buffer; 50 uint64 lba; 51 uint64 flat_buffer; 52 }; 53 54 static const uint16 kParametersSizeVersion1 = 0x1a; 55 static const uint16 kParametersSizeVersion2 = 0x1e; 56 static const uint16 kParametersSizeVersion3 = 0x42; 57 58 static const uint16 kDevicePathSignature = 0xbedd; 59 60 struct drive_parameters { 61 uint16 parameters_size; 62 uint16 flags; 63 uint32 cylinders; 64 uint32 heads; 65 uint32 sectors_per_track; 66 uint64 sectors; 67 uint16 bytes_per_sector; 68 /* edd 2.0 */ 69 real_addr device_table; 70 /* edd 3.0 */ 71 uint16 device_path_signature; 72 uint8 device_path_size; 73 uint8 reserved1[3]; 74 char host_bus[4]; 75 char interface_type[8]; 76 union { 77 struct { 78 uint16 base_address; 79 } legacy; 80 struct { 81 uint8 bus; 82 uint8 slot; 83 uint8 function; 84 } pci; 85 uint8 reserved[8]; 86 } interface; 87 union { 88 struct { 89 uint8 slave; 90 } ata; 91 struct { 92 uint8 slave; 93 uint8 logical_unit; 94 } atapi; 95 struct { 96 uint8 logical_unit; 97 } scsi; 98 struct { 99 uint8 tbd; 100 } usb; 101 struct { 102 uint64 guid; 103 } firewire; 104 struct { 105 uint64 wwd; 106 } fibre; 107 } device; 108 uint8 reserved2; 109 uint8 checksum; 110 } _PACKED; 111 112 struct device_table { 113 uint16 base_address; 114 uint16 control_port_address; 115 uint8 _reserved1 : 4; 116 uint8 is_slave : 1; 117 uint8 _reserved2 : 1; 118 uint8 lba_enabled : 1; 119 } _PACKED; 120 121 struct specification_packet { 122 uint8 size; 123 uint8 media_type; 124 uint8 drive_number; 125 uint8 controller_index; 126 uint32 start_emulation; 127 uint16 device_specification; 128 uint8 _more_[9]; 129 } _PACKED; 130 131 class BIOSDrive : public Node { 132 public: 133 BIOSDrive(uint8 driveID); 134 virtual ~BIOSDrive(); 135 136 status_t InitCheck() const; 137 138 virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize); 139 virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize); 140 141 virtual off_t Size() const; 142 143 uint32 BlockSize() const { return fBlockSize; } 144 145 status_t FillIdentifier(); 146 147 bool HasParameters() const { return fHasParameters; } 148 const drive_parameters &Parameters() const { return fParameters; } 149 150 disk_identifier &Identifier() { return fIdentifier; } 151 uint8 DriveID() const { return fDriveID; } 152 153 protected: 154 uint8 fDriveID; 155 bool fLBA; 156 uint64 fSize; 157 uint32 fBlockSize; 158 bool fHasParameters; 159 drive_parameters fParameters; 160 disk_identifier fIdentifier; 161 }; 162 163 164 static bool sBlockDevicesAdded = false; 165 166 167 static void 168 check_cd_boot(BIOSDrive *drive) 169 { 170 gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK); 171 172 if (drive->DriveID() != 0) 173 return; 174 175 struct bios_regs regs; 176 regs.eax = BIOS_BOOT_CD_GET_STATUS; 177 regs.edx = 0; 178 regs.esi = kDataSegmentScratch; 179 call_bios(0x13, ®s); 180 181 if ((regs.flags & CARRY_FLAG) != 0) 182 return; 183 184 // we obviously were booted from CD! 185 186 specification_packet *packet = (specification_packet *)kDataSegmentScratch; 187 if (packet->media_type != 0) 188 gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_CD); 189 190 #if 0 191 dprintf("got CD boot spec:\n"); 192 dprintf(" size: %#x\n", packet->size); 193 dprintf(" media type: %u\n", packet->media_type); 194 dprintf(" drive_number: %u\n", packet->drive_number); 195 dprintf(" controller index: %u\n", packet->controller_index); 196 dprintf(" start emulation: %lu\n", packet->start_emulation); 197 dprintf(" device_specification: %u\n", packet->device_specification); 198 #endif 199 } 200 201 202 static bool 203 are_extensions_available(uint8 drive) 204 { 205 struct bios_regs regs; 206 regs.eax = BIOS_IS_EXT_PRESENT; 207 regs.ebx = 0x55aa; 208 regs.edx = drive; 209 call_bios(0x13, ®s); 210 211 TRACE(("checking extensions: carry: %u; ebx: 0x%08lx; ecx: 0x%08lx\n", 212 regs.flags & CARRY_FLAG, regs.ebx, regs.ecx)); 213 return (regs.flags & CARRY_FLAG) == 0 && regs.ebx == 0xaa55 214 && (regs.ecx & 0x01 /* supports device access using packet */) != 0; 215 } 216 217 218 static status_t 219 get_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters) 220 { 221 drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch; 222 223 memset(parameter, 0, sizeof(drive_parameters)); 224 parameter->parameters_size = sizeof(drive_parameters); 225 226 struct bios_regs regs; 227 regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS; 228 regs.edx = drive; 229 regs.esi = (addr_t)parameter - kDataSegmentBase; 230 call_bios(0x13, ®s); 231 232 // filter out faulty BIOS return codes 233 if ((regs.flags & CARRY_FLAG) != 0 234 || parameter->sectors == 0) 235 return B_ERROR; 236 237 memcpy(targetParameters, parameter, sizeof(drive_parameters)); 238 return B_OK; 239 } 240 241 242 static status_t 243 get_drive_parameters(uint8 drive, drive_parameters *parameters) 244 { 245 struct bios_regs regs; 246 regs.eax = BIOS_GET_DRIVE_PARAMETERS; 247 regs.edx = drive; 248 regs.es = 0; 249 regs.edi = 0; // guard against faulty BIOS, see Ralf Brown's interrupt list 250 call_bios(0x13, ®s); 251 252 if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0) 253 return B_ERROR; 254 255 // fill drive_parameters structure with useful values 256 parameters->parameters_size = kParametersSizeVersion1; 257 parameters->flags = 0; 258 parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1; 259 parameters->heads = ((regs.edx >> 8) & 0xff) + 1; 260 // heads and cylinders start counting from 0 261 parameters->sectors_per_track = regs.ecx & 0x3f; 262 parameters->sectors = parameters->cylinders * parameters->heads 263 * parameters->sectors_per_track; 264 parameters->bytes_per_sector = 512; 265 266 return B_OK; 267 } 268 269 270 static status_t 271 get_number_of_drives(uint8 *_count) 272 { 273 struct bios_regs regs; 274 regs.eax = BIOS_GET_DRIVE_PARAMETERS; 275 regs.edx = 0x80; 276 regs.es = 0; 277 regs.edi = 0; 278 call_bios(0x13, ®s); 279 280 if (regs.flags & CARRY_FLAG) 281 return B_ERROR; 282 283 *_count = regs.edx & 0xff; 284 return B_OK; 285 } 286 287 288 /** parse EDD 3.0 drive path information */ 289 290 static status_t 291 fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters ¶meters) 292 { 293 if (parameters.parameters_size < kParametersSizeVersion3 294 || parameters.device_path_signature != kDevicePathSignature) 295 return B_BAD_TYPE; 296 297 // parse host bus 298 299 if (!strncmp(parameters.host_bus, "PCI", 3)) { 300 disk.bus_type = PCI_BUS; 301 302 disk.bus.pci.bus = parameters.interface.pci.bus; 303 disk.bus.pci.slot = parameters.interface.pci.slot; 304 disk.bus.pci.function = parameters.interface.pci.function; 305 } else if (!strncmp(parameters.host_bus, "ISA", 3)) { 306 disk.bus_type = LEGACY_BUS; 307 308 disk.bus.legacy.base_address = parameters.interface.legacy.base_address; 309 dprintf("legacy base address %x\n", disk.bus.legacy.base_address); 310 } else { 311 dprintf("unknown host bus \"%s\"\n", parameters.host_bus); 312 return B_BAD_DATA; 313 } 314 315 // parse interface 316 317 if (!strncmp(parameters.interface_type, "ATA", 3)) { 318 disk.device_type = ATA_DEVICE; 319 disk.device.ata.master = !parameters.device.ata.slave; 320 dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave"); 321 } else if (!strncmp(parameters.interface_type, "ATAPI", 3)) { 322 disk.device_type = ATAPI_DEVICE; 323 disk.device.atapi.master = !parameters.device.ata.slave; 324 disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit; 325 } else if (!strncmp(parameters.interface_type, "SCSI", 3)) { 326 disk.device_type = SCSI_DEVICE; 327 disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit; 328 } else if (!strncmp(parameters.interface_type, "USB", 3)) { 329 disk.device_type = USB_DEVICE; 330 disk.device.usb.tbd = parameters.device.usb.tbd; 331 } else if (!strncmp(parameters.interface_type, "1394", 3)) { 332 disk.device_type = FIREWIRE_DEVICE; 333 disk.device.firewire.guid = parameters.device.firewire.guid; 334 } else if (!strncmp(parameters.interface_type, "FIBRE", 3)) { 335 disk.device_type = FIBRE_DEVICE; 336 disk.device.fibre.wwd = parameters.device.fibre.wwd; 337 } else { 338 dprintf("unknown interface type \"%s\"\n", parameters.interface_type); 339 return B_BAD_DATA; 340 } 341 342 return B_OK; 343 } 344 345 346 /** EDD 2.0 drive table information */ 347 348 static status_t 349 fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters ¶meters) 350 { 351 if (parameters.device_table.segment == 0xffff 352 && parameters.device_table.offset == 0xffff) 353 return B_BAD_TYPE; 354 355 device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment, 356 parameters.device_table.offset); 357 358 disk.bus_type = LEGACY_BUS; 359 disk.bus.legacy.base_address = table->base_address; 360 361 disk.device_type = ATA_DEVICE; 362 disk.device.ata.master = !table->is_slave; 363 364 return B_OK; 365 } 366 367 368 static off_t 369 get_next_check_sum_offset(int32 index, off_t maxSize) 370 { 371 // The boot block often contains the disk superblock, and should be 372 // unique enough for most cases 373 if (index < 2) 374 return index * 512; 375 376 // Try some data in the first part of the drive 377 if (index < 4) 378 return (maxSize >> 10) + index * 2048; 379 380 // Some random value might do 381 return ((system_time() + index) % (maxSize >> 9)) * 512; 382 } 383 384 385 /** Computes a check sum for the specified block. 386 * The check sum is the sum of all data in that block interpreted as an 387 * array of uint32 values. 388 * Note, this must use the same method as the one used in kernel/fs/vfs_boot.cpp. 389 */ 390 391 static uint32 392 compute_check_sum(BIOSDrive *drive, off_t offset) 393 { 394 char buffer[512]; 395 ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer)); 396 if (bytesRead < B_OK) 397 return 0; 398 399 if (bytesRead < (ssize_t)sizeof(buffer)) 400 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 401 402 uint32 *array = (uint32 *)buffer; 403 uint32 sum = 0; 404 405 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) { 406 sum += array[i]; 407 } 408 409 return sum; 410 } 411 412 /** Checks if the specified drive is usable for reading. 413 */ 414 415 static bool 416 is_drive_readable(BIOSDrive *drive) 417 { 418 char buffer; 419 return drive->ReadAt(NULL, 0, &buffer, sizeof(buffer)) > 0; 420 } 421 422 423 static void 424 find_unique_check_sums(NodeList *devices) 425 { 426 NodeIterator iterator = devices->GetIterator(); 427 Node *device; 428 int32 index = 0; 429 off_t minSize = 0; 430 const int32 kMaxTries = 200; 431 432 while (index < kMaxTries) { 433 bool clash = false; 434 435 iterator.Rewind(); 436 437 while ((device = iterator.Next()) != NULL) { 438 BIOSDrive *drive = (BIOSDrive *)device; 439 #if 0 440 // there is no RTTI in the boot loader... 441 BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device); 442 if (drive == NULL) 443 continue; 444 #endif 445 446 // TODO: currently, we assume that the BIOS provided us with unique 447 // disk identifiers... hopefully this is a good idea 448 if (drive->Identifier().device_type != UNKNOWN_DEVICE) 449 continue; 450 451 if (minSize == 0 || drive->Size() < minSize) 452 minSize = drive->Size(); 453 454 // check for clashes 455 456 NodeIterator compareIterator = devices->GetIterator(); 457 while ((device = compareIterator.Next()) != NULL) { 458 BIOSDrive *compareDrive = (BIOSDrive *)device; 459 460 if (compareDrive == drive 461 || compareDrive->Identifier().device_type != UNKNOWN_DEVICE) 462 continue; 463 464 // TODO: Until we can actually get and compare *all* fields of the disk 465 // identifier in the kernel, we cannot compare the whole structure (we also 466 // should be more careful zeroing the structure before we fill it). 467 #if 0 468 if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(), 469 sizeof(disk_identifier))) { 470 clash = true; 471 break; 472 } 473 #else 474 const disk_identifier& ourId = drive->Identifier(); 475 const disk_identifier& otherId = compareDrive->Identifier(); 476 if (memcmp(&ourId.device.unknown.check_sums, 477 &otherId.device.unknown.check_sums, 478 sizeof(ourId.device.unknown.check_sums)) == 0) { 479 clash = true; 480 } 481 #endif 482 } 483 484 if (clash) 485 break; 486 } 487 488 if (!clash) { 489 // our work here is done. 490 return; 491 } 492 493 // add a new block to the check sums 494 495 off_t offset = get_next_check_sum_offset(index, minSize); 496 int32 i = index % NUM_DISK_CHECK_SUMS; 497 iterator.Rewind(); 498 499 while ((device = iterator.Next()) != NULL) { 500 BIOSDrive *drive = (BIOSDrive *)device; 501 502 disk_identifier& disk = drive->Identifier(); 503 disk.device.unknown.check_sums[i].offset = offset; 504 disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset); 505 506 TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset, 507 disk.device.unknown.check_sums[i].sum)); 508 } 509 510 index++; 511 } 512 513 // If we get here, we couldn't find a way to differentiate all disks from each other. 514 // It's very likely that one disk is an exact copy of the other, so there is nothing 515 // we could do, anyway. 516 517 dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n"); 518 } 519 520 521 static status_t 522 add_block_devices(NodeList *devicesList, bool identifierMissing) 523 { 524 if (sBlockDevicesAdded) 525 return B_OK; 526 527 uint8 driveCount; 528 if (get_number_of_drives(&driveCount) != B_OK) 529 return B_ERROR; 530 531 dprintf("number of drives: %d\n", driveCount); 532 533 for (int32 i = 0; i < driveCount; i++) { 534 uint8 driveID = i + 0x80; 535 if (driveID == gBootDriveID) 536 continue; 537 538 BIOSDrive *drive = new(nothrow) BIOSDrive(driveID); 539 if (drive->InitCheck() != B_OK) { 540 dprintf("could not add drive %u\n", driveID); 541 delete drive; 542 continue; 543 } 544 545 // Only add usable drives 546 if (is_drive_readable(drive)) 547 devicesList->Add(drive); 548 else { 549 dprintf("could not read from drive %" B_PRIu8 ", not adding\n", driveID); 550 delete drive; 551 continue; 552 } 553 554 if (drive->FillIdentifier() != B_OK) 555 identifierMissing = true; 556 } 557 558 if (identifierMissing) { 559 // we cannot distinguish between all drives by identifier, we need 560 // compute checksums for them 561 find_unique_check_sums(devicesList); 562 } 563 564 sBlockDevicesAdded = true; 565 return B_OK; 566 } 567 568 569 // #pragma mark - 570 571 572 BIOSDrive::BIOSDrive(uint8 driveID) 573 : 574 fDriveID(driveID), 575 fSize(0) 576 { 577 TRACE(("drive ID %u\n", driveID)); 578 579 if (driveID < 0x80 || !are_extensions_available(driveID) 580 || get_ext_drive_parameters(driveID, &fParameters) != B_OK) { 581 // old style CHS support 582 583 if (get_drive_parameters(driveID, &fParameters) != B_OK) { 584 dprintf("getting drive parameters for: %u failed!\n", fDriveID); 585 return; 586 } 587 588 TRACE((" cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n", 589 fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track, 590 fParameters.bytes_per_sector)); 591 TRACE((" total sectors: %Ld\n", fParameters.sectors)); 592 593 fBlockSize = 512; 594 fSize = fParameters.sectors * fBlockSize; 595 fLBA = false; 596 fHasParameters = false; 597 } else { 598 TRACE(("size: %x\n", fParameters.parameters_size)); 599 TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature)); 600 TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus, 601 fParameters.interface_type)); 602 TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n", 603 fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track, 604 fParameters.bytes_per_sector)); 605 TRACE(("total sectors: %Ld\n", fParameters.sectors)); 606 607 fBlockSize = fParameters.bytes_per_sector; 608 fSize = fParameters.sectors * fBlockSize; 609 fLBA = true; 610 fHasParameters = true; 611 } 612 } 613 614 615 BIOSDrive::~BIOSDrive() 616 { 617 } 618 619 620 status_t 621 BIOSDrive::InitCheck() const 622 { 623 return fSize > 0 ? B_OK : B_ERROR; 624 } 625 626 627 ssize_t 628 BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) 629 { 630 uint32 offset = pos % fBlockSize; 631 pos /= fBlockSize; 632 633 uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize; 634 int32 totalBytesRead = 0; 635 636 //TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n", 637 // blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID)); 638 639 uint32 scratchSize = 24 * 1024 / fBlockSize; 640 // maximum value allowed by Phoenix BIOS is 0x7f 641 642 while (blocksLeft > 0) { 643 uint32 blocksRead = blocksLeft; 644 if (blocksRead > scratchSize) 645 blocksRead = scratchSize; 646 647 if (fLBA) { 648 struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch; 649 memset(packet, 0, sizeof(disk_address_packet)); 650 651 packet->size = sizeof(disk_address_packet); 652 packet->number_of_blocks = blocksRead; 653 packet->buffer = kExtraSegmentScratch; 654 packet->lba = pos; 655 656 struct bios_regs regs; 657 regs.eax = BIOS_EXT_READ; 658 regs.edx = fDriveID; 659 regs.esi = (addr_t)packet - kDataSegmentBase; 660 call_bios(0x13, ®s); 661 662 if (regs.flags & CARRY_FLAG) 663 goto chs_read; 664 } else { 665 chs_read: 666 // Old style CHS read routine 667 668 // We can only read up to 64 kB this way, but since scratchSize 669 // is actually lower than this value, we don't have to take care 670 // of this here. 671 672 uint32 sector = pos % fParameters.sectors_per_track + 1; 673 // sectors start countint at 1 (unlike head and cylinder) 674 uint32 head = pos / fParameters.sectors_per_track; 675 uint32 cylinder = head / fParameters.heads; 676 head %= fParameters.heads; 677 678 if (cylinder >= fParameters.cylinders) { 679 TRACE(("cylinder value %lu bigger than available %lu\n", 680 cylinder, fParameters.cylinders)); 681 return B_BAD_VALUE; 682 } 683 684 // try to read from the device more than once, just to make sure it'll work 685 struct bios_regs regs; 686 int32 tries = 3; 687 bool readWorked = false; 688 689 while (tries-- > 0) { 690 regs.eax = BIOS_READ | blocksRead; 691 regs.edx = fDriveID | (head << 8); 692 regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8); 693 regs.es = 0; 694 regs.ebx = kExtraSegmentScratch; 695 call_bios(0x13, ®s); 696 697 if ((regs.flags & CARRY_FLAG) == 0) { 698 readWorked = true; 699 break; 700 } 701 702 TRACE(("read failed\n")); 703 704 if (tries < 2) { 705 // reset disk system 706 TRACE(("reset disk system\n")); 707 regs.eax = BIOS_RESET_DISK_SYSTEM; 708 regs.edx = fDriveID; 709 call_bios(0x13, ®s); 710 } 711 712 // wait a bit between the retries (1/20 sec) 713 spin(50000); 714 } 715 716 if (!readWorked) { 717 dprintf("reading %ld bytes from drive %u failed at %Ld\n", 718 blocksRead, fDriveID, pos); 719 return B_ERROR; 720 } 721 } 722 723 uint32 bytesRead = fBlockSize * blocksRead - offset; 724 // copy no more than bufferSize bytes 725 if (bytesRead > bufferSize) 726 bytesRead = bufferSize; 727 728 memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead); 729 pos += blocksRead; 730 offset = 0; 731 blocksLeft -= blocksRead; 732 bufferSize -= bytesRead; 733 buffer = (void *)((addr_t)buffer + bytesRead); 734 totalBytesRead += bytesRead; 735 } 736 737 return totalBytesRead; 738 } 739 740 741 ssize_t 742 BIOSDrive::WriteAt(void* cookie, off_t pos, const void* buffer, 743 size_t bufferSize) 744 { 745 // we support only LBA addressing 746 if (!fLBA) { 747 dprintf("BIOSDrive::WriteAt(): CHS addressing not supported\n"); 748 return B_UNSUPPORTED; 749 } 750 751 // we support only block-aligned writes 752 if (pos % fBlockSize != 0 || bufferSize % fBlockSize != 0) { 753 dprintf("BIOSDrive::WriteAt(pos: %" B_PRIdOFF ", size: %" B_PRIuSIZE 754 "): Block-unaligned write not supported.\n", pos, bufferSize); 755 return B_UNSUPPORTED; 756 } 757 758 pos /= fBlockSize; 759 760 uint32 blocksLeft = bufferSize / fBlockSize; 761 int32 totalBytesWritten = 0; 762 763 uint32 scratchSize = 24 * 1024 / fBlockSize; 764 // maximum value allowed by Phoenix BIOS is 0x7f 765 766 while (blocksLeft > 0) { 767 uint32 blocksToWrite = blocksLeft; 768 if (blocksToWrite > scratchSize) 769 blocksToWrite = scratchSize; 770 771 uint32 bytesToWrite = blocksToWrite * fBlockSize; 772 773 memcpy((void*)kExtraSegmentScratch, buffer, bytesToWrite); 774 775 struct disk_address_packet* packet 776 = (disk_address_packet*)kDataSegmentScratch; 777 memset(packet, 0, sizeof(disk_address_packet)); 778 779 packet->size = sizeof(disk_address_packet); 780 packet->number_of_blocks = blocksToWrite; 781 packet->buffer = kExtraSegmentScratch; 782 packet->lba = pos; 783 784 struct bios_regs regs; 785 regs.eax = BIOS_EXT_WRITE; // al = 0x00 -- no write verify 786 regs.edx = fDriveID; 787 regs.esi = (addr_t)packet - kDataSegmentBase; 788 call_bios(0x13, ®s); 789 790 if (regs.flags & CARRY_FLAG) 791 return B_ERROR; 792 793 pos += blocksToWrite; 794 blocksLeft -= blocksToWrite; 795 bufferSize -= bytesToWrite; 796 buffer = (void*)((addr_t)buffer + bytesToWrite); 797 totalBytesWritten += bytesToWrite; 798 } 799 800 return totalBytesWritten; 801 } 802 803 804 off_t 805 BIOSDrive::Size() const 806 { 807 return fSize; 808 } 809 810 811 status_t 812 BIOSDrive::FillIdentifier() 813 { 814 if (HasParameters()) { 815 // try all drive_parameters versions, beginning from the most informative 816 817 #if 0 818 if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK) 819 return B_OK; 820 821 if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK) 822 return B_OK; 823 #else 824 // TODO: the above version is the correct one - it's currently 825 // disabled, as the kernel boot code only supports the 826 // UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot 827 // device. 828 if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK) 829 fill_disk_identifier_v2(fIdentifier, fParameters); 830 831 #endif 832 833 // no interesting information, we have to fall back to the default 834 // unknown interface/device type identifier 835 } 836 837 fIdentifier.bus_type = UNKNOWN_BUS; 838 fIdentifier.device_type = UNKNOWN_DEVICE; 839 fIdentifier.device.unknown.size = Size(); 840 841 for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) { 842 fIdentifier.device.unknown.check_sums[i].offset = -1; 843 fIdentifier.device.unknown.check_sums[i].sum = 0; 844 } 845 846 return B_ERROR; 847 } 848 849 850 // #pragma mark - 851 852 853 status_t 854 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) 855 { 856 TRACE(("boot drive ID: %x\n", gBootDriveID)); 857 858 BIOSDrive *drive = new(nothrow) BIOSDrive(gBootDriveID); 859 if (drive->InitCheck() != B_OK) { 860 dprintf("no boot drive!\n"); 861 delete drive; 862 return B_ERROR; 863 } 864 865 devicesList->Add(drive); 866 867 if (drive->FillIdentifier() != B_OK) { 868 // We need to add all block devices to give the kernel the possibility 869 // to find the right boot volume 870 add_block_devices(devicesList, true); 871 } 872 873 TRACE(("boot drive size: %Ld bytes\n", drive->Size())); 874 gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, gBootedFromImage); 875 876 return B_OK; 877 } 878 879 880 status_t 881 platform_get_boot_partitions(struct stage2_args *args, Node *bootDevice, 882 NodeList *list, NodeList *bootList) 883 { 884 BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice); 885 off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize(); 886 887 dprintf("boot partition offset: %Ld\n", offset); 888 889 NodeIterator iterator = list->GetIterator(); 890 boot::Partition *partition = NULL; 891 while ((partition = (boot::Partition *)iterator.Next()) != NULL) { 892 TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size)); 893 // search for the partition that contains the partition 894 // offset as reported by the BFS boot block 895 if (offset >= partition->offset 896 && offset < partition->offset + partition->size) { 897 bootList->Insert(partition); 898 return B_OK; 899 } 900 } 901 902 return B_ENTRY_NOT_FOUND; 903 } 904 905 906 status_t 907 platform_add_block_devices(stage2_args *args, NodeList *devicesList) 908 { 909 return add_block_devices(devicesList, false); 910 } 911 912 913 status_t 914 platform_register_boot_device(Node *device) 915 { 916 BIOSDrive *drive = (BIOSDrive *)device; 917 918 check_cd_boot(drive); 919 920 gBootVolume.SetInt64("boot drive number", drive->DriveID()); 921 gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, 922 &drive->Identifier(), sizeof(disk_identifier)); 923 924 return B_OK; 925 } 926 927