xref: /haiku/src/system/boot/loader/loader.cpp (revision 2b76973fa2401f7a5edf68e6470f3d3210cbcff3)
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 
24 #ifndef BOOT_ARCH
25 #	error BOOT_ARCH has to be defined to differentiate the kernel per platform
26 #endif
27 
28 #define SYSTEM_DIRECTORY_PREFIX	"system/"
29 #define KERNEL_IMAGE	"kernel_" BOOT_ARCH
30 #define KERNEL_PATH		SYSTEM_DIRECTORY_PREFIX KERNEL_IMAGE
31 
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
36 
37 
38 static const char* const kSystemDirectoryPrefix = SYSTEM_DIRECTORY_PREFIX;
39 
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 },
46 };
47 
48 static const char *sAddonPaths[] = {
49 	kVolumeLocalSystemKernelAddonsDirectory,
50 	kVolumeLocalCommonNonpackagedKernelAddonsDirectory,
51 	kVolumeLocalCommonKernelAddonsDirectory,
52 	kVolumeLocalUserNonpackagedKernelAddonsDirectory,
53 	kVolumeLocalUserKernelAddonsDirectory,
54 	NULL
55 };
56 
57 
58 static int
59 open_maybe_packaged(BootVolume& volume, const char* path, int openMode)
60 {
61 	if (strncmp(path, kSystemDirectoryPrefix, strlen(kSystemDirectoryPrefix))
62 			== 0) {
63 		path += strlen(kSystemDirectoryPrefix);
64 		return open_from(volume.SystemDirectory(), path, openMode);
65 	}
66 
67 	return open_from(volume.RootDirectory(), path, openMode);
68 }
69 
70 
71 static int
72 find_kernel(BootVolume& volume, const char** name = NULL)
73 {
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];
79 
80 			return fd;
81 		}
82 	}
83 
84 	return B_ENTRY_NOT_FOUND;
85 }
86 
87 
88 bool
89 is_bootable(Directory *volume)
90 {
91 	if (volume->IsEmpty())
92 		return false;
93 
94 	BootVolume bootVolume;
95 	if (bootVolume.SetTo(volume) != B_OK)
96 		return false;
97 
98 	// check for the existance of a kernel (for our platform)
99 	int fd = find_kernel(bootVolume);
100 	if (fd < 0)
101 		return false;
102 
103 	close(fd);
104 
105 	return true;
106 }
107 
108 
109 status_t
110 load_kernel(stage2_args* args, BootVolume& volume)
111 {
112 	const char *name;
113 	int fd = find_kernel(volume, &name);
114 	if (fd < B_OK)
115 		return fd;
116 
117 	dprintf("load kernel %s...\n", name);
118 
119 	elf_init();
120 	preloaded_image *image;
121 	status_t status = elf_load_image(fd, &image);
122 
123 	close(fd);
124 
125 	if (status < B_OK) {
126 		dprintf("loading kernel failed: %lx!\n", status);
127 		return status;
128 	}
129 
130 	gKernelArgs.kernel_image = image;
131 
132 	status = elf_relocate_image(gKernelArgs.kernel_image);
133 	if (status < B_OK) {
134 		dprintf("relocating kernel failed: %lx!\n", status);
135 		return status;
136 	}
137 
138 	gKernelArgs.kernel_image->name = kernel_args_strdup(name);
139 
140 	return B_OK;
141 }
142 
143 
144 static status_t
145 load_modules_from(BootVolume& volume, const char* path)
146 {
147 	// we don't have readdir() & co. (yet?)...
148 
149 	int fd = open_maybe_packaged(volume, path, O_RDONLY);
150 	if (fd < B_OK)
151 		return fd;
152 
153 	Directory *modules = (Directory *)get_node_from(fd);
154 	if (modules == NULL)
155 		return B_ENTRY_NOT_FOUND;
156 
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;
163 
164 			status_t status = elf_load_image(modules, name);
165 			if (status != B_OK)
166 				dprintf("Could not load \"%s\" error %ld\n", name, status);
167 		}
168 
169 		modules->Close(cookie);
170 	}
171 
172 	return B_OK;
173 }
174 
175 
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.
180  */
181 
182 static status_t
183 load_module(BootVolume& volume, const char* name)
184 {
185 	char moduleName[B_FILE_NAME_LENGTH];
186 	if (strlcpy(moduleName, name, sizeof(moduleName)) > sizeof(moduleName))
187 		return B_NAME_TOO_LONG;
188 
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;
194 
195 		Directory *base = (Directory *)get_node_from(baseFD);
196 		if (base == NULL) {
197 			close(baseFD);
198 			continue;
199 		}
200 
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;
207 
208 				status_t status = elf_load_image(base, moduleName);
209 
210 				close(fd);
211 				close(baseFD);
212 				return status;
213 			}
214 
215 			// cut off last name element (or stop trying if there are no more)
216 
217 			char *last = strrchr(moduleName, '/');
218 			if (last != NULL)
219 				last[0] = '\0';
220 			else
221 				break;
222 		}
223 
224 		close(baseFD);
225 	}
226 
227 	return B_OK;
228 }
229 
230 
231 status_t
232 load_modules(stage2_args* args, BootVolume& volume)
233 {
234 	int32 failed = 0;
235 
236 	// ToDo: this should be mostly replaced by a hardware oriented detection mechanism
237 
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]);
242 
243 		if (load_modules_from(volume, path) != B_OK)
244 			failed++;
245 	}
246 
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};
252 
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);
257 		}
258 	}
259 
260 	// and now load all partitioning and file system modules
261 	// needed to identify the boot volume
262 
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();
271 			}
272 		}
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);
282 	}
283 
284 	return B_OK;
285 }
286 
287