2 * Copyright 2007-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
4 * Distributed under the terms of the MIT License.
6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
19 #include <boot/kernel_args.h>
20 #include <directories.h>
21 #include <disk_device_manager/KDiskDevice.h>
22 #include <disk_device_manager/KDiskDeviceManager.h>
23 #include <disk_device_manager/KPartitionVisitor.h>
24 #include <DiskDeviceTypes.h>
25 #include <file_cache.h>
29 #include <util/KMessage.h>
30 #include <util/Stack.h>
33 #include "vfs_net_boot.h"
38 # define TRACE(x) dprintf x
44 typedef Stack
<KPartition
*> PartitionStack
;
49 } sPredefinedLinks
[] = {
50 { kGlobalSystemDirectory
, kSystemDirectory
},
51 { kGlobalBinDirectory
, kSystemBinDirectory
},
52 { kGlobalEtcDirectory
, kSystemEtcDirectory
},
53 { kGlobalTempDirectory
, kSystemTempDirectory
},
54 { kGlobalVarDirectory
, kSystemVarDirectory
},
55 { kGlobalPackageLinksDirectory
, kSystemPackageLinksDirectory
},
59 // This can be used by other code to see if there is a boot file system already
60 dev_t gBootDevice
= -1;
61 bool gReadOnlyBootDevice
= false;
64 /*! No image was chosen - prefer disks with names like "Haiku", or "System"
67 compare_image_boot(const void* _a
, const void* _b
)
69 KPartition
* a
= *(KPartition
**)_a
;
70 KPartition
* b
= *(KPartition
**)_b
;
72 if (a
->ContentName() != NULL
) {
73 if (b
->ContentName() == NULL
)
75 } else if (b
->ContentName() != NULL
) {
80 int compare
= strcasecmp(a
->ContentName(), b
->ContentName());
84 if (!strcasecmp(a
->ContentName(), "Haiku"))
86 if (!strcasecmp(b
->ContentName(), "Haiku"))
88 if (!strncmp(a
->ContentName(), "System", 6))
90 if (!strncmp(b
->ContentName(), "System", 6))
97 /*! The system was booted from CD - prefer CDs over other entries. If there
98 is no CD, fall back to the standard mechanism (as implemented by
102 compare_cd_boot(const void* _a
, const void* _b
)
104 KPartition
* a
= *(KPartition
**)_a
;
105 KPartition
* b
= *(KPartition
**)_b
;
107 bool aIsCD
= a
->Type() != NULL
108 && !strcmp(a
->Type(), kPartitionTypeDataSession
);
109 bool bIsCD
= b
->Type() != NULL
110 && !strcmp(b
->Type(), kPartitionTypeDataSession
);
112 int compare
= (int)aIsCD
- (int)bIsCD
;
116 return compare_image_boot(_a
, _b
);
120 /*! Computes a check sum for the specified block.
121 The check sum is the sum of all data in that block interpreted as an
122 array of uint32 values.
123 Note, this must use the same method as the one used in
124 boot/platform/bios_ia32/devices.cpp (or similar solutions).
127 compute_check_sum(KDiskDevice
* device
, off_t offset
)
130 ssize_t bytesRead
= read_pos(device
->FD(), offset
, buffer
, sizeof(buffer
));
131 if (bytesRead
< B_OK
)
134 if (bytesRead
< (ssize_t
)sizeof(buffer
))
135 memset(buffer
+ bytesRead
, 0, sizeof(buffer
) - bytesRead
);
137 uint32
* array
= (uint32
*)buffer
;
141 i
< (bytesRead
+ sizeof(uint32
) - 1) / sizeof(uint32
); i
++) {
149 // #pragma mark - BootMethod
152 BootMethod::BootMethod(const KMessage
& bootVolume
, int32 method
)
154 fBootVolume(bootVolume
),
160 BootMethod::~BootMethod()
172 // #pragma mark - DiskBootMethod
175 class DiskBootMethod
: public BootMethod
{
177 DiskBootMethod(const KMessage
& bootVolume
, int32 method
)
178 : BootMethod(bootVolume
, method
)
182 virtual bool IsBootDevice(KDiskDevice
* device
, bool strict
);
183 virtual bool IsBootPartition(KPartition
* partition
, bool& foundForSure
);
184 virtual void SortPartitions(KPartition
** partitions
, int32 count
);
189 DiskBootMethod::IsBootDevice(KDiskDevice
* device
, bool strict
)
191 disk_identifier
* disk
;
192 int32 diskIdentifierSize
;
193 if (fBootVolume
.FindData(BOOT_VOLUME_DISK_IDENTIFIER
, B_RAW_TYPE
,
194 (const void**)&disk
, &diskIdentifierSize
) != B_OK
) {
195 dprintf("DiskBootMethod::IsBootDevice(): no disk identifier!\n");
199 TRACE(("boot device: bus %" B_PRId32
", device %" B_PRId32
"\n",
200 disk
->bus_type
, disk
->device_type
));
202 // Assume that CD boots only happen off removable media.
203 if (fMethod
== BOOT_METHOD_CD
&& !device
->IsRemovable())
206 switch (disk
->bus_type
) {
209 // TODO: implement this! (and then enable this feature in the boot
211 // (we need a way to get the device_node of a device, then)
215 // nothing to do here
219 switch (disk
->device_type
) {
221 // test if the size of the device matches
222 // (the BIOS might have given us the wrong value here, though)
223 if (strict
&& device
->Size() != disk
->device
.unknown
.size
)
226 // Skip the check sum test for CDs, since we didn't read anything
227 // useful from the disk in the boot loader.
228 if (fMethod
== BOOT_METHOD_CD
)
231 // check if the check sums match, too
232 for (int32 i
= 0; i
< NUM_DISK_CHECK_SUMS
; i
++) {
233 if (disk
->device
.unknown
.check_sums
[i
].offset
== -1)
236 if (compute_check_sum(device
,
237 disk
->device
.unknown
.check_sums
[i
].offset
)
238 != disk
->device
.unknown
.check_sums
[i
].sum
) {
248 case FIREWIRE_DEVICE
:
250 // TODO: implement me!
259 DiskBootMethod::IsBootPartition(KPartition
* partition
, bool& foundForSure
)
261 off_t bootPartitionOffset
= fBootVolume
.GetInt64(
262 BOOT_VOLUME_PARTITION_OFFSET
, 0);
264 if (!fBootVolume
.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE
, false)) {
265 // the simple case: we can just boot from the selected boot
267 if (partition
->Offset() == bootPartitionOffset
) {
268 dprintf("Identified boot partition by partition offset.\n");
273 // For now, unless we can positively identify an anyboot CD, we will
274 // just collect all BFS/ISO9660 volumes.
276 if (fMethod
== BOOT_METHOD_CD
) {
277 // Check for the boot partition of an anyboot CD. We identify it as
278 // such, if it is the only primary partition on the CD, has type
279 // BFS, and the boot partition offset is 0.
280 KDiskDevice
* device
= partition
->Device();
281 if (IsBootDevice(device
, false) && bootPartitionOffset
== 0
282 && partition
->Parent() == device
&& device
->CountChildren() == 1
283 && device
->ContentType() != NULL
284 && strcmp(device
->ContentType(), kPartitionTypeIntel
) == 0
285 && partition
->ContentType() != NULL
286 && strcmp(partition
->ContentType(), kPartitionTypeBFS
) == 0) {
287 dprintf("Identified anyboot CD.\n");
292 // Ignore non-session partitions, if a boot partition was selected
294 if (fBootVolume
.GetBool(BOOT_VOLUME_USER_SELECTED
, false)
295 && partition
->Type() != NULL
296 && strcmp(partition
->Type(), kPartitionTypeDataSession
) != 0) {
301 if (partition
->ContentType() != NULL
302 && (strcmp(partition
->ContentType(), kPartitionTypeBFS
) == 0
303 || strcmp(partition
->ContentType(), kPartitionTypeISO9660
) == 0)) {
313 DiskBootMethod::SortPartitions(KPartition
** partitions
, int32 count
)
315 qsort(partitions
, count
, sizeof(KPartition
*),
316 fMethod
== BOOT_METHOD_CD
? compare_cd_boot
: compare_image_boot
);
323 /*! Make the boot partition (and probably others) available.
324 The partitions that are a boot candidate a put into the /a partitions
325 stack. If the user selected a boot device, there is will only be one
326 entry in this stack; if not, the most likely is put up first.
327 The boot code should then just try them one by one.
330 get_boot_partitions(KMessage
& bootVolume
, PartitionStack
& partitions
)
332 dprintf("get_boot_partitions(): boot volume message:\n");
333 bootVolume
.Dump(&dprintf
);
335 // create boot method
336 int32 bootMethodType
= bootVolume
.GetInt32(BOOT_METHOD
, BOOT_METHOD_DEFAULT
);
337 dprintf("get_boot_partitions(): boot method type: %" B_PRId32
"\n",
340 BootMethod
* bootMethod
= NULL
;
341 switch (bootMethodType
) {
342 case BOOT_METHOD_NET
:
343 bootMethod
= new(nothrow
) NetBootMethod(bootVolume
, bootMethodType
);
346 case BOOT_METHOD_HARD_DISK
:
349 bootMethod
= new(nothrow
) DiskBootMethod(bootVolume
,
354 status_t status
= bootMethod
!= NULL
? bootMethod
->Init() : B_NO_MEMORY
;
358 KDiskDeviceManager::CreateDefault();
359 KDiskDeviceManager
*manager
= KDiskDeviceManager::Default();
361 status
= manager
->InitialDeviceScan();
362 if (status
!= B_OK
) {
363 dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n",
368 if (1 /* dump devices and partitions */) {
371 while ((device
= manager
->NextDevice(&cookie
)) != NULL
) {
372 device
->Dump(true, 0);
376 struct BootPartitionVisitor
: KPartitionVisitor
{
377 BootPartitionVisitor(BootMethod
* bootMethod
, PartitionStack
&stack
)
378 : fPartitions(stack
),
379 fBootMethod(bootMethod
)
383 virtual bool VisitPre(KPartition
*partition
)
385 if (!partition
->ContainsFileSystem())
388 bool foundForSure
= false;
389 if (fBootMethod
->IsBootPartition(partition
, foundForSure
))
390 fPartitions
.Push(partition
);
392 // if found for sure, we can terminate the search
397 PartitionStack
&fPartitions
;
398 BootMethod
* fBootMethod
;
399 } visitor(bootMethod
, partitions
);
406 while ((device
= manager
->NextDevice(&cookie
)) != NULL
) {
407 if (!bootMethod
->IsBootDevice(device
, strict
))
410 if (device
->VisitEachDescendant(&visitor
) != NULL
)
414 if (!partitions
.IsEmpty() || !strict
)
417 // we couldn't find any potential boot devices, try again less strict
421 // sort partition list (e.g.. when booting from CD, CDs should come first in
423 if (!bootVolume
.GetBool(BOOT_VOLUME_USER_SELECTED
, false))
424 bootMethod
->SortPartitions(partitions
.Array(), partitions
.CountItems());
434 vfs_bootstrap_file_systems(void)
438 // bootstrap the root filesystem
439 status
= _kern_mount("/", NULL
, "rootfs", 0, NULL
, 0);
441 panic("error mounting rootfs!\n");
443 _kern_setcwd(-1, "/");
445 // bootstrap the devfs
446 _kern_create_dir(-1, "/dev", 0755);
447 status
= _kern_mount("/dev", NULL
, "devfs", 0, NULL
, 0);
449 panic("error mounting devfs\n");
451 // create directory for the boot volume
452 _kern_create_dir(-1, "/boot", 0755);
454 // create some standard links on the rootfs
456 for (int32 i
= 0; sPredefinedLinks
[i
].path
!= NULL
; i
++) {
457 _kern_create_symlink(-1, sPredefinedLinks
[i
].path
,
458 sPredefinedLinks
[i
].target
, 0777);
459 // we don't care if it will succeed or not
467 vfs_mount_boot_file_system(kernel_args
* args
)
470 bootVolume
.SetTo(args
->boot_volume
, args
->boot_volume_size
);
472 PartitionStack partitions
;
473 status_t status
= get_boot_partitions(bootVolume
, partitions
);
475 panic("get_boot_partitions failed!");
477 if (partitions
.IsEmpty()) {
478 panic("did not find any boot partitions!");
481 dev_t bootDevice
= -1;
483 KPartition
* bootPartition
;
484 while (partitions
.Pop(&bootPartition
)) {
486 if (bootPartition
->GetPath(&path
) != B_OK
)
487 panic("could not get boot device!\n");
489 const char* fsName
= NULL
;
490 bool readOnly
= false;
491 if (strcmp(bootPartition
->ContentType(), kPartitionTypeISO9660
) == 0) {
492 fsName
= "iso9660:write_overlay:attribute_overlay";
494 } else if (bootPartition
->IsReadOnly()
495 && strcmp(bootPartition
->ContentType(), kPartitionTypeBFS
) == 0) {
496 fsName
= "bfs:write_overlay";
500 TRACE(("trying to mount boot partition: %s\n", path
.Path()));
502 bootDevice
= _kern_mount("/boot", path
.Path(), fsName
, 0, NULL
, 0);
503 if (bootDevice
>= 0) {
504 dprintf("Mounted boot partition: %s\n", path
.Path());
505 gReadOnlyBootDevice
= readOnly
;
510 if (bootDevice
< B_OK
)
511 panic("could not mount boot device!\n");
513 // create link for the name of the boot device
516 if (_kern_read_fs_info(bootDevice
, &info
) == B_OK
) {
517 char path
[B_FILE_NAME_LENGTH
+ 1];
518 snprintf(path
, sizeof(path
), "/%s", info
.volume_name
);
520 _kern_create_symlink(-1, path
, "/boot", 0777);
523 // If we're booting off a packaged system, mount packagefs.
525 if (bootVolume
.GetBool(BOOT_VOLUME_PACKAGED
, false)
526 || (bootVolume
.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE
, false)
527 && lstat(kSystemPackagesDirectory
, &st
) == 0)) {
528 static const char* const kPackageFSName
= "packagefs";
531 strlcpy(arguments
, "packages /boot/system/packages; type system",
533 if (const char* stateName
534 = bootVolume
.GetString(BOOT_VOLUME_PACKAGES_STATE
, NULL
)) {
535 strlcat(arguments
, "; state ", sizeof(arguments
));
536 strlcat(arguments
, stateName
, sizeof(arguments
));
539 dev_t packageMount
= _kern_mount("/boot/system", NULL
, kPackageFSName
,
540 0, arguments
, 0 /* unused argument length */);
541 if (packageMount
< 0) {
542 panic("Failed to mount system packagefs: %s",
543 strerror(packageMount
));
546 packageMount
= _kern_mount("/boot/home/config", NULL
, kPackageFSName
, 0,
547 "packages /boot/home/config/packages; type home",
548 0 /* unused argument length */);
549 if (packageMount
< 0) {
550 dprintf("Failed to mount home packagefs: %s\n",
551 strerror(packageMount
));
555 // Now that packagefs is mounted, the boot volume is really ready.
556 gBootDevice
= bootDevice
;
558 // Do post-boot-volume module initialization. The module code wants to know
559 // whether the module images the boot loader has pre-loaded are the same as
560 // on the boot volume. That is the case when booting from hard disk or CD,
561 // but not via network.
562 int32 bootMethodType
= bootVolume
.GetInt32(BOOT_METHOD
, BOOT_METHOD_DEFAULT
);
563 bool bootingFromBootLoaderVolume
= bootMethodType
== BOOT_METHOD_HARD_DISK
564 || bootMethodType
== BOOT_METHOD_CD
;
565 module_init_post_boot_device(bootingFromBootLoaderVolume
);
567 file_cache_init_post_boot_device();
569 // search for other disk systems
570 KDiskDeviceManager
*manager
= KDiskDeviceManager::Default();
571 manager
->RescanDiskSystems();
572 manager
->StartMonitoring();