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