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