1 /* 2 * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #include <OS.h> 11 #include <fs_info.h> 12 13 #include <disk_device_manager/KDiskDevice.h> 14 #include <disk_device_manager/KDiskDeviceManager.h> 15 #include <disk_device_manager/KPartitionVisitor.h> 16 #include <DiskDeviceTypes.h> 17 18 #include <vfs.h> 19 #include <file_cache.h> 20 #include <KPath.h> 21 #include <syscalls.h> 22 #include <boot/kernel_args.h> 23 #include <util/Stack.h> 24 25 #include <stdio.h> 26 27 28 //#define TRACE_VFS 29 #ifdef TRACE_VFS 30 # define TRACE(x) dprintf x 31 #else 32 # define TRACE(x) ; 33 #endif 34 35 36 typedef Stack<KPartition *> PartitionStack; 37 38 static struct { 39 const char *path; 40 const char *target; 41 } sPredefinedLinks[] = { 42 {"/system", "/boot/beos/system"}, 43 {"/bin", "/boot/beos/bin"}, 44 {"/etc", "/boot/beos/etc"}, 45 {"/var", "/boot/var"}, 46 {"/tmp", "/boot/var/tmp"}, 47 {NULL} 48 }; 49 50 // This can be used by other code to see if there is a boot file system already 51 dev_t gBootDevice = -1; 52 53 54 /** No image was chosen - prefer disks with names like "Haiku", or "System" 55 */ 56 57 static int 58 compare_image_boot(const void *_a, const void *_b) 59 { 60 KPartition *a = *(KPartition **)_a; 61 KPartition *b = *(KPartition **)_b; 62 63 if (a->ContentName() != NULL) { 64 if (b->ContentName() == NULL) 65 return 1; 66 } else if (b->ContentName() != NULL) { 67 return -1; 68 } else 69 return 0; 70 71 int compare = strcmp(a->ContentName(), b->ContentName()); 72 if (!compare) 73 return 0; 74 75 if (!strcasecmp(a->ContentName(), "Haiku")) 76 return 1; 77 if (!strcasecmp(b->ContentName(), "Haiku")) 78 return -1; 79 if (!strncmp(a->ContentName(), "System", 6)) 80 return 1; 81 if (!strncmp(b->ContentName(), "System", 6)) 82 return -1; 83 84 return compare; 85 } 86 87 88 /** The system was booted from CD - prefer CDs over other entries. If there 89 * is no CD, fall back to the standard mechanism (as implemented by 90 * compare_image_boot(). 91 */ 92 93 static int 94 compare_cd_boot(const void *_a, const void *_b) 95 { 96 KPartition *a = *(KPartition **)_a; 97 KPartition *b = *(KPartition **)_b; 98 99 bool aIsCD = a->Type() != NULL && !strcmp(a->Type(), kPartitionTypeDataSession); 100 bool bIsCD = b->Type() != NULL && !strcmp(b->Type(), kPartitionTypeDataSession); 101 102 int compare = (int)aIsCD - (int)bIsCD; 103 if (compare != 0) 104 return compare; 105 106 return compare_image_boot(_a, _b); 107 } 108 109 110 /** Computes a check sum for the specified block. 111 * The check sum is the sum of all data in that block interpreted as an 112 * array of uint32 values. 113 * Note, this must use the same method as the one used in 114 * boot/platform/bios_ia32/devices.cpp (or similar solutions). 115 */ 116 117 static uint32 118 compute_check_sum(KDiskDevice *device, off_t offset) 119 { 120 char buffer[512]; 121 ssize_t bytesRead = read_pos(device->FD(), offset, buffer, sizeof(buffer)); 122 if (bytesRead < B_OK) 123 return 0; 124 125 if (bytesRead < (ssize_t)sizeof(buffer)) 126 memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead); 127 128 uint32 *array = (uint32 *)buffer; 129 uint32 sum = 0; 130 131 for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) { 132 sum += array[i]; 133 } 134 135 return sum; 136 } 137 138 139 /** Checks if the device matches the boot device as specified by the 140 * boot loader. 141 */ 142 143 static bool 144 is_boot_device(kernel_args *args, KDiskDevice *device, bool strict) 145 { 146 disk_identifier &disk = args->boot_disk.identifier; 147 148 TRACE(("boot device: bus %ld, device %ld\n", disk.bus_type, 149 disk.device_type)); 150 151 switch (disk.bus_type) { 152 case PCI_BUS: 153 case LEGACY_BUS: 154 // TODO: implement this! (and then enable this feature in the boot loader) 155 // (we need a way to get the device_node of a device, then) 156 break; 157 158 case UNKNOWN_BUS: 159 // nothing to do here 160 break; 161 } 162 163 switch (disk.device_type) { 164 case UNKNOWN_DEVICE: 165 // test if the size of the device matches 166 // (the BIOS might have given us the wrong value here, though) 167 if (strict && device->Size() != disk.device.unknown.size) 168 return false; 169 170 // check if the check sums match, too 171 for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) { 172 if (disk.device.unknown.check_sums[i].offset == -1) 173 continue; 174 175 if (compute_check_sum(device, disk.device.unknown.check_sums[i].offset) 176 != disk.device.unknown.check_sums[i].sum) 177 return false; 178 } 179 break; 180 181 case ATA_DEVICE: 182 case ATAPI_DEVICE: 183 case SCSI_DEVICE: 184 case USB_DEVICE: 185 case FIREWIRE_DEVICE: 186 case FIBRE_DEVICE: 187 // TODO: implement me! 188 break; 189 } 190 191 return true; 192 } 193 194 195 /** Make the boot partition (and probably others) available. 196 * The partitions that are a boot candidate a put into the /a partitions 197 * stack. If the user selected a boot device, there is will only be one 198 * entry in this stack; if not, the most likely is put up first. 199 * The boot code should then just try them one by one. 200 */ 201 202 static status_t 203 get_boot_partitions(kernel_args *args, PartitionStack &partitions) 204 { 205 KDiskDeviceManager::CreateDefault(); 206 KDiskDeviceManager *manager = KDiskDeviceManager::Default(); 207 208 status_t status = manager->InitialDeviceScan(); 209 if (status != B_OK) { 210 dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n", strerror(status)); 211 return status; 212 } 213 214 struct BootPartitionVisitor : KPartitionVisitor { 215 BootPartitionVisitor(kernel_args &args, PartitionStack &stack) 216 : fArgs(args), fPartitions(stack) {} 217 218 virtual bool VisitPre(KPartition *partition) 219 { 220 if (!partition->ContainsFileSystem()) 221 return false; 222 223 if (!fArgs.boot_disk.booted_from_image) { 224 // the simple case: we can just boot from the selected boot device 225 if (partition->Offset() == fArgs.boot_disk.partition_offset) { 226 fPartitions.Push(partition); 227 return true; 228 } 229 } else { 230 // for now, we will just collect all BFS volumes 231 if (fArgs.boot_disk.cd && fArgs.boot_disk.user_selected 232 && partition->Type() != NULL 233 && strcmp(partition->Type(), kPartitionTypeDataSession)) 234 return false; 235 236 if (partition->ContentType() != NULL 237 && !strcmp(partition->ContentType(), "Be File System")) 238 fPartitions.Push(partition); 239 } 240 return false; 241 } 242 private: 243 kernel_args &fArgs; 244 PartitionStack &fPartitions; 245 } visitor(*args, partitions); 246 247 bool strict = true; 248 249 while (true) { 250 KDiskDevice *device; 251 int32 cookie = 0; 252 while ((device = manager->NextDevice(&cookie)) != NULL) { 253 if (!is_boot_device(args, device, strict)) 254 continue; 255 256 if (device->VisitEachDescendant(&visitor) != NULL) 257 break; 258 } 259 260 if (!partitions.IsEmpty() || !strict) 261 break; 262 263 // we couldn't find any potential boot devices, try again less strict 264 strict = false; 265 } 266 267 if (!args->boot_disk.user_selected) { 268 // sort partition list (ie. when booting from CD, CDs should come first in the list) 269 qsort(partitions.Array(), partitions.CountItems(), sizeof(KPartition *), 270 args->boot_disk.cd ? compare_cd_boot : compare_image_boot); 271 } 272 273 return B_OK; 274 } 275 276 277 // #pragma mark - 278 279 280 status_t 281 vfs_bootstrap_file_systems(void) 282 { 283 status_t status; 284 285 // bootstrap the root filesystem 286 status = _kern_mount("/", NULL, "rootfs", 0, NULL, 0); 287 if (status < B_OK) 288 panic("error mounting rootfs!\n"); 289 290 _kern_setcwd(-1, "/"); 291 292 // bootstrap the devfs 293 _kern_create_dir(-1, "/dev", 0755); 294 status = _kern_mount("/dev", NULL, "devfs", 0, NULL, 0); 295 if (status < B_OK) 296 panic("error mounting devfs\n"); 297 298 // bootstrap the pipefs 299 _kern_create_dir(-1, "/pipe", 0755); 300 status = _kern_mount("/pipe", NULL, "pipefs", 0, NULL, 0); 301 if (status < B_OK) 302 panic("error mounting pipefs\n"); 303 304 // create directory for the boot volume 305 _kern_create_dir(-1, "/boot", 0755); 306 307 // create some standard links on the rootfs 308 309 for (int32 i = 0; sPredefinedLinks[i].path != NULL; i++) { 310 _kern_create_symlink(-1, sPredefinedLinks[i].path, 311 sPredefinedLinks[i].target, 0); 312 // we don't care if it will succeed or not 313 } 314 315 return B_OK; 316 } 317 318 319 status_t 320 vfs_mount_boot_file_system(kernel_args *args) 321 { 322 PartitionStack partitions; 323 status_t status = get_boot_partitions(args, partitions); 324 if (status < B_OK) { 325 panic("Did not get any boot partitions!"); 326 return status; 327 } 328 329 KPartition *bootPartition; 330 while (partitions.Pop(&bootPartition)) { 331 KPath path; 332 if (bootPartition->GetPath(&path) != B_OK) 333 panic("could not get boot device!\n"); 334 335 gBootDevice = _kern_mount("/boot", path.Path(), NULL, 0, NULL, 0); 336 if (gBootDevice >= B_OK) 337 break; 338 } 339 340 if (gBootDevice < B_OK) 341 panic("could not mount boot device!\n"); 342 343 // create link for the name of the boot device 344 345 fs_info info; 346 if (_kern_read_fs_info(gBootDevice, &info) == B_OK) { 347 char path[B_FILE_NAME_LENGTH + 1]; 348 snprintf(path, sizeof(path), "/%s", info.volume_name); 349 350 _kern_create_symlink(-1, path, "/boot", 0); 351 } 352 353 file_cache_init_post_boot_device(); 354 return B_OK; 355 } 356 357