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
BlockSize() const142 uint32 BlockSize() const { return fBlockSize; }
143
144 status_t FillIdentifier();
145
HasParameters() const146 bool HasParameters() const { return fHasParameters; }
Parameters() const147 const drive_parameters &Parameters() const { return fParameters; }
148
Identifier()149 disk_identifier &Identifier() { return fIdentifier; }
DriveID() const150 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
check_cd_boot(BIOSDrive * drive)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
are_extensions_available(uint8 drive)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
get_ext_drive_parameters(uint8 drive,drive_parameters * targetParameters)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
get_drive_parameters(uint8 drive,drive_parameters * parameters)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
get_number_of_drives(uint8 * _count)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
fill_disk_identifier_v3(disk_identifier & disk,const drive_parameters & parameters)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
fill_disk_identifier_v2(disk_identifier & disk,const drive_parameters & parameters)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
get_next_check_sum_offset(int32 index,off_t maxSize)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
compute_check_sum(BIOSDrive * drive,off_t offset)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
is_drive_readable(BIOSDrive * drive)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
find_unique_check_sums(NodeList * devices)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
add_block_devices(NodeList * devicesList,bool identifierMissing)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
BIOSDrive(uint8 driveID)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
~BIOSDrive()614 BIOSDrive::~BIOSDrive()
615 {
616 }
617
618
619 status_t
InitCheck() const620 BIOSDrive::InitCheck() const
621 {
622 return fSize > 0 ? B_OK : B_ERROR;
623 }
624
625
626 ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)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
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)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
Size() const804 BIOSDrive::Size() const
805 {
806 return fSize;
807 }
808
809
810 status_t
FillIdentifier()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
platform_add_boot_device(struct stage2_args * args,NodeList * devicesList)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
platform_get_boot_partitions(struct stage2_args * args,Node * bootDevice,NodeList * list,NodeList * bootList)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
platform_add_block_devices(stage2_args * args,NodeList * devicesList)906 platform_add_block_devices(stage2_args *args, NodeList *devicesList)
907 {
908 return add_block_devices(devicesList, false);
909 }
910
911
912 status_t
platform_register_boot_device(Node * device)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
platform_cleanup_devices()928 platform_cleanup_devices()
929 {
930 }
931