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