xref: /haiku/src/system/boot/loader/loader.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
1 /*
2  * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "loader.h"
8 #include "elf.h"
9 #include "RootFileSystem.h"
10 
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>
19 
20 #include <unistd.h>
21 #include <string.h>
22 
23 #ifndef BOOT_ARCH
24 #	error BOOT_ARCH has to be defined to differentiate the kernel per platform
25 #endif
26 
27 #define KERNEL_IMAGE			"kernel_" BOOT_ARCH
28 #define KERNEL_PATH				"system/" KERNEL_IMAGE
29 
30 #ifdef ALTERNATE_BOOT_ARCH
31 # define ALTERNATE_KERNEL_IMAGE	"kernel_" ALTERNATE_BOOT_ARCH
32 # define ALTERNATE_KERNEL_PATH	"system/" ALTERNATE_KERNEL_IMAGE
33 #endif
34 
35 
36 static const char *sKernelPaths[][2] = {
37 	{ KERNEL_PATH, KERNEL_IMAGE },
38 #ifdef ALTERNATE_BOOT_ARCH
39 	{ ALTERNATE_KERNEL_PATH, ALTERNATE_KERNEL_IMAGE },
40 #endif
41 	{ NULL, NULL },
42 };
43 
44 static const char *sAddonPaths[] = {
45 	kVolumeLocalSystemKernelAddonsDirectory,
46 	kVolumeLocalCommonKernelAddonsDirectory,
47 	kVolumeLocalUserKernelAddonsDirectory,
48 	NULL
49 };
50 
51 
52 static int
53 find_kernel(Directory *volume, const char **name = NULL)
54 {
55 	for (int32 i = 0; sKernelPaths[i][0] != NULL; i++) {
56 		int fd = open_from(volume, sKernelPaths[i][0], O_RDONLY);
57 		if (fd >= 0) {
58 			if (name)
59 				*name = sKernelPaths[i][1];
60 
61 			return fd;
62 		}
63 	}
64 
65 	return B_ENTRY_NOT_FOUND;
66 }
67 
68 bool
69 is_bootable(Directory *volume)
70 {
71 	if (volume->IsEmpty())
72 		return false;
73 
74 	// check for the existance of a kernel (for our platform)
75 	int fd = find_kernel(volume);
76 	if (fd < B_OK)
77 		return false;
78 
79 	close(fd);
80 
81 	return true;
82 }
83 
84 
85 status_t
86 load_kernel(stage2_args *args, Directory *volume)
87 {
88 	const char *name;
89 	int fd = find_kernel(volume, &name);
90 	if (fd < B_OK)
91 		return fd;
92 
93 	dprintf("load kernel %s...\n", name);
94 
95 	elf_init();
96 	preloaded_image *image;
97 	status_t status = elf_load_image(fd, &image);
98 
99 	close(fd);
100 
101 	if (status < B_OK) {
102 		dprintf("loading kernel failed: %lx!\n", status);
103 		return status;
104 	}
105 
106 	gKernelArgs.kernel_image = image;
107 
108 	status = elf_relocate_image(gKernelArgs.kernel_image);
109 	if (status < B_OK) {
110 		dprintf("relocating kernel failed: %lx!\n", status);
111 		return status;
112 	}
113 
114 	gKernelArgs.kernel_image->name = kernel_args_strdup(name);
115 
116 	return B_OK;
117 }
118 
119 
120 static status_t
121 load_modules_from(Directory *volume, const char *path)
122 {
123 	// we don't have readdir() & co. (yet?)...
124 
125 	int fd = open_from(volume, path, O_RDONLY);
126 	if (fd < B_OK)
127 		return fd;
128 
129 	Directory *modules = (Directory *)get_node_from(fd);
130 	if (modules == NULL)
131 		return B_ENTRY_NOT_FOUND;
132 
133 	void *cookie;
134 	if (modules->Open(&cookie, O_RDONLY) == B_OK) {
135 		char name[B_FILE_NAME_LENGTH];
136 		while (modules->GetNextEntry(cookie, name, sizeof(name)) == B_OK) {
137 			if (!strcmp(name, ".") || !strcmp(name, ".."))
138 				continue;
139 
140 			status_t status = elf_load_image(modules, name);
141 			if (status != B_OK)
142 				dprintf("Could not load \"%s\" error %ld\n", name, status);
143 		}
144 
145 		modules->Close(cookie);
146 	}
147 
148 	return B_OK;
149 }
150 
151 
152 /** Loads a module by module name. This basically works in the same
153  *	way as the kernel module loader; it will cut off the last part
154  *	of the module name until it could find a module and loads it.
155  *	It tests both, kernel and user module directories.
156  */
157 
158 static status_t
159 load_module(Directory *volume, const char *name)
160 {
161 	char moduleName[B_FILE_NAME_LENGTH];
162 	if (strlcpy(moduleName, name, sizeof(moduleName)) > sizeof(moduleName))
163 		return B_NAME_TOO_LONG;
164 
165 	for (int32 i = 0; sAddonPaths[i]; i++) {
166 		// get base path
167 		int baseFD = open_from(volume, sAddonPaths[i], O_RDONLY);
168 		if (baseFD < B_OK)
169 			continue;
170 
171 		Directory *base = (Directory *)get_node_from(baseFD);
172 		if (base == NULL) {
173 			close(baseFD);
174 			continue;
175 		}
176 
177 		while (true) {
178 			int fd = open_from(base, moduleName, O_RDONLY);
179 			if (fd >= B_OK) {
180 				struct stat stat;
181 				if (fstat(fd, &stat) != 0 || !S_ISREG(stat.st_mode))
182 					return B_BAD_VALUE;
183 
184 				status_t status = elf_load_image(base, moduleName);
185 
186 				close(fd);
187 				close(baseFD);
188 				return status;
189 			}
190 
191 			// cut off last name element (or stop trying if there are no more)
192 
193 			char *last = strrchr(moduleName, '/');
194 			if (last != NULL)
195 				last[0] = '\0';
196 			else
197 				break;
198 		}
199 
200 		close(baseFD);
201 	}
202 
203 	return B_OK;
204 }
205 
206 
207 status_t
208 load_modules(stage2_args *args, Directory *volume)
209 {
210 	int32 failed = 0;
211 
212 	// ToDo: this should be mostly replaced by a hardware oriented detection mechanism
213 
214 	int32 i = 0;
215 	for (; sAddonPaths[i]; i++) {
216 		char path[B_FILE_NAME_LENGTH];
217 		snprintf(path, sizeof(path), "%s/boot", sAddonPaths[i]);
218 
219 		if (load_modules_from(volume, path) != B_OK)
220 			failed++;
221 	}
222 
223 	if (failed == i) {
224 		// couldn't load any boot modules
225 		// fall back to load all modules (currently needed by the boot floppy)
226 		const char *paths[] = { "bus_managers", "busses/ide", "busses/scsi",
227 			"generic", "partitioning_systems", "drivers/bin", NULL};
228 
229 		for (int32 i = 0; paths[i]; i++) {
230 			char path[B_FILE_NAME_LENGTH];
231 			snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], paths[i]);
232 			load_modules_from(volume, path);
233 		}
234 	}
235 
236 	// and now load all partitioning and file system modules
237 	// needed to identify the boot volume
238 
239 	if (!gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) {
240 		// iterate over the mounted volumes and load their file system
241 		Partition *partition;
242 		if (gRoot->GetPartitionFor(volume, &partition) == B_OK) {
243 			while (partition != NULL) {
244 				load_module(volume, partition->ModuleName());
245 				partition = partition->Parent();
246 			}
247 		}
248 	} else {
249 		// The boot image should only contain the file system
250 		// needed to boot the system, so we just load it.
251 		// ToDo: this is separate from the fall back from above
252 		//	as this piece will survive a more intelligent module
253 		//	loading approach...
254 		char path[B_FILE_NAME_LENGTH];
255 		snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], "file_systems");
256 		load_modules_from(volume, path);
257 	}
258 
259 	return B_OK;
260 }
261 
262