headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / loader.cpp
blob2651468c32fe4a0c103580792630210ba1df237a
1 /*
2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "loader.h"
8 #include "elf.h"
9 #include "RootFileSystem.h"
11 #include <directories.h>
12 #include <OS.h>
13 #include <util/list.h>
14 #include <boot/stage2.h>
15 #include <boot/vfs.h>
16 #include <boot/platform.h>
17 #include <boot/stdio.h>
18 #include <boot/partitions.h>
20 #include <unistd.h>
21 #include <string.h>
24 #ifndef BOOT_ARCH
25 # error BOOT_ARCH has to be defined to differentiate the kernel per platform
26 #endif
28 #define SYSTEM_DIRECTORY_PREFIX "system/"
29 #define KERNEL_IMAGE "kernel_" BOOT_ARCH
30 #define KERNEL_PATH SYSTEM_DIRECTORY_PREFIX KERNEL_IMAGE
32 #ifdef ALTERNATE_BOOT_ARCH
33 # define ALTERNATE_KERNEL_IMAGE "kernel_" ALTERNATE_BOOT_ARCH
34 # define ALTERNATE_KERNEL_PATH "system/" ALTERNATE_KERNEL_IMAGE
35 #endif
38 static const char* const kSystemDirectoryPrefix = SYSTEM_DIRECTORY_PREFIX;
40 static const char *sKernelPaths[][2] = {
41 { KERNEL_PATH, KERNEL_IMAGE },
42 #ifdef ALTERNATE_BOOT_ARCH
43 { ALTERNATE_KERNEL_PATH, ALTERNATE_KERNEL_IMAGE },
44 #endif
45 { NULL, NULL },
48 static const char *sAddonPaths[] = {
49 kVolumeLocalSystemKernelAddonsDirectory,
50 kVolumeLocalCommonNonpackagedKernelAddonsDirectory,
51 kVolumeLocalCommonKernelAddonsDirectory,
52 kVolumeLocalUserNonpackagedKernelAddonsDirectory,
53 kVolumeLocalUserKernelAddonsDirectory,
54 NULL
58 static int
59 open_maybe_packaged(BootVolume& volume, const char* path, int openMode)
61 if (strncmp(path, kSystemDirectoryPrefix, strlen(kSystemDirectoryPrefix))
62 == 0) {
63 path += strlen(kSystemDirectoryPrefix);
64 return open_from(volume.SystemDirectory(), path, openMode);
67 return open_from(volume.RootDirectory(), path, openMode);
71 static int
72 find_kernel(BootVolume& volume, const char** name = NULL)
74 for (int32 i = 0; sKernelPaths[i][0] != NULL; i++) {
75 int fd = open_maybe_packaged(volume, sKernelPaths[i][0], O_RDONLY);
76 if (fd >= 0) {
77 if (name)
78 *name = sKernelPaths[i][1];
80 return fd;
84 return B_ENTRY_NOT_FOUND;
88 bool
89 is_bootable(Directory *volume)
91 if (volume->IsEmpty())
92 return false;
94 BootVolume bootVolume;
95 if (bootVolume.SetTo(volume) != B_OK)
96 return false;
98 // check for the existance of a kernel (for our platform)
99 int fd = find_kernel(bootVolume);
100 if (fd < 0)
101 return false;
103 close(fd);
105 return true;
109 status_t
110 load_kernel(stage2_args* args, BootVolume& volume)
112 const char *name;
113 int fd = find_kernel(volume, &name);
114 if (fd < B_OK)
115 return fd;
117 dprintf("load kernel %s...\n", name);
119 elf_init();
120 preloaded_image *image;
121 status_t status = elf_load_image(fd, &image);
123 close(fd);
125 if (status < B_OK) {
126 dprintf("loading kernel failed: %" B_PRIx32 "!\n", status);
127 return status;
130 gKernelArgs.kernel_image = image;
132 status = elf_relocate_image(gKernelArgs.kernel_image);
133 if (status < B_OK) {
134 dprintf("relocating kernel failed: %" B_PRIx32 "!\n", status);
135 return status;
138 gKernelArgs.kernel_image->name = kernel_args_strdup(name);
140 return B_OK;
144 static status_t
145 load_modules_from(BootVolume& volume, const char* path)
147 // we don't have readdir() & co. (yet?)...
149 int fd = open_maybe_packaged(volume, path, O_RDONLY);
150 if (fd < B_OK)
151 return fd;
153 Directory *modules = (Directory *)get_node_from(fd);
154 if (modules == NULL)
155 return B_ENTRY_NOT_FOUND;
157 void *cookie;
158 if (modules->Open(&cookie, O_RDONLY) == B_OK) {
159 char name[B_FILE_NAME_LENGTH];
160 while (modules->GetNextEntry(cookie, name, sizeof(name)) == B_OK) {
161 if (!strcmp(name, ".") || !strcmp(name, ".."))
162 continue;
164 status_t status = elf_load_image(modules, name);
165 if (status != B_OK)
166 dprintf("Could not load \"%s\" error %" B_PRIx32 "\n", name, status);
169 modules->Close(cookie);
172 return B_OK;
176 /** Loads a module by module name. This basically works in the same
177 * way as the kernel module loader; it will cut off the last part
178 * of the module name until it could find a module and loads it.
179 * It tests both, kernel and user module directories.
182 static status_t
183 load_module(BootVolume& volume, const char* name)
185 char moduleName[B_FILE_NAME_LENGTH];
186 if (strlcpy(moduleName, name, sizeof(moduleName)) > sizeof(moduleName))
187 return B_NAME_TOO_LONG;
189 for (int32 i = 0; sAddonPaths[i]; i++) {
190 // get base path
191 int baseFD = open_maybe_packaged(volume, sAddonPaths[i], O_RDONLY);
192 if (baseFD < B_OK)
193 continue;
195 Directory *base = (Directory *)get_node_from(baseFD);
196 if (base == NULL) {
197 close(baseFD);
198 continue;
201 while (true) {
202 int fd = open_from(base, moduleName, O_RDONLY);
203 if (fd >= B_OK) {
204 struct stat stat;
205 if (fstat(fd, &stat) != 0 || !S_ISREG(stat.st_mode))
206 return B_BAD_VALUE;
208 status_t status = elf_load_image(base, moduleName);
210 close(fd);
211 close(baseFD);
212 return status;
215 // cut off last name element (or stop trying if there are no more)
217 char *last = strrchr(moduleName, '/');
218 if (last != NULL)
219 last[0] = '\0';
220 else
221 break;
224 close(baseFD);
227 return B_OK;
231 status_t
232 load_modules(stage2_args* args, BootVolume& volume)
234 int32 failed = 0;
236 // ToDo: this should be mostly replaced by a hardware oriented detection mechanism
238 int32 i = 0;
239 for (; sAddonPaths[i]; i++) {
240 char path[B_FILE_NAME_LENGTH];
241 snprintf(path, sizeof(path), "%s/boot", sAddonPaths[i]);
243 if (load_modules_from(volume, path) != B_OK)
244 failed++;
247 if (failed == i) {
248 // couldn't load any boot modules
249 // fall back to load all modules (currently needed by the boot floppy)
250 const char *paths[] = { "bus_managers", "busses/ide", "busses/scsi",
251 "generic", "partitioning_systems", "drivers/bin", NULL};
253 for (int32 i = 0; paths[i]; i++) {
254 char path[B_FILE_NAME_LENGTH];
255 snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], paths[i]);
256 load_modules_from(volume, path);
260 // and now load all partitioning and file system modules
261 // needed to identify the boot volume
263 if (!gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) {
264 // iterate over the mounted volumes and load their file system
265 Partition *partition;
266 if (gRoot->GetPartitionFor(volume.RootDirectory(), &partition)
267 == B_OK) {
268 while (partition != NULL) {
269 load_module(volume, partition->ModuleName());
270 partition = partition->Parent();
273 } else {
274 // The boot image should only contain the file system
275 // needed to boot the system, so we just load it.
276 // ToDo: this is separate from the fall back from above
277 // as this piece will survive a more intelligent module
278 // loading approach...
279 char path[B_FILE_NAME_LENGTH];
280 snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], "file_systems");
281 load_modules_from(volume, path);
284 return B_OK;