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