xref: /titanic_51/usr/src/boot/sys/boot/i386/libi386/linux.c (revision d4f75c945065a64cbb0535077fbbcb67c8301624)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2015 Toomas Soome <tsoome@me.com>
14  */
15 
16 /*
17  * Primitive linux loader, at the moment only intended to load memtest86+.bin.
18  *
19  * Note the linux kernel location conflicts with loader, so we need to
20  * read in to temporary space and relocate on exec, when btx is stopped.
21  */
22 #include <sys/cdefs.h>
23 #include <sys/stat.h>
24 #include <stand.h>
25 #include <machine/metadata.h>
26 #include <machine/pc/bios.h>
27 
28 #include "linux.h"
29 #include "bootstrap.h"
30 #include "libi386.h"
31 #include "btxv86.h"
32 
33 static int linux_loadkernel(char *, u_int64_t, struct preloaded_file **);
34 static int linux_loadinitrd(char *, u_int64_t, struct preloaded_file **);
35 static int linux_exec(struct preloaded_file *);
36 static int linux_execinitrd(struct preloaded_file *);
37 
38 struct file_format linux = { linux_loadkernel, linux_exec };
39 struct file_format linux_initrd = { linux_loadinitrd, linux_execinitrd };
40 
41 uint32_t linux_text_len;
42 uint32_t linux_data_tmp_addr;
43 uint32_t linux_data_real_addr;
44 static size_t max_cmdline_size;
45 
46 static void
47 test_addr(uint64_t addr, uint64_t length, vm_offset_t *result)
48 {
49 	vm_offset_t candidate;
50 
51 	if (addr + length >= 0xa0000)
52 		length = 0xa0000 - addr;
53 
54 	candidate = addr + length - (LINUX_CL_OFFSET + max_cmdline_size);
55 	if (candidate > LINUX_OLD_REAL_MODE_ADDR)
56 		candidate = LINUX_OLD_REAL_MODE_ADDR;
57 	if (candidate < addr)
58 		return;
59 
60 	if (candidate > *result || *result == (vm_offset_t)-1)
61 		*result = candidate;
62 }
63 
64 static vm_offset_t
65 find_real_addr(struct preloaded_file *fp)
66 {
67 	struct bios_smap *smap;
68 	struct file_metadata *md;
69 	int entries, i;
70 	vm_offset_t candidate = -1;
71 
72 	md = file_findmetadata(fp, MODINFOMD_SMAP);
73 	if (md == NULL) {
74 		printf("no memory smap\n");
75 		return (candidate);
76 	}
77 	entries = md->md_size / sizeof (struct bios_smap);
78 	smap = (struct bios_smap *)md->md_data;
79 	for (i = 0; i < entries; i++) {
80 		if (smap[i].type != SMAP_TYPE_MEMORY)
81 			continue;
82 		if (smap[i].base >= 0xa0000)
83 			continue;
84 		test_addr(smap[i].base, smap[i].length, &candidate);
85 	}
86 	return (candidate);
87 }
88 
89 static int
90 linux_loadkernel(char *filename, uint64_t dest, struct preloaded_file **result)
91 {
92 	struct linux_kernel_header lh;
93 	struct preloaded_file *fp;
94 	struct stat sb;
95 	ssize_t n;
96 	int fd, error = 0;
97 	int setup_sects, linux_big;
98 	unsigned long data, text;
99 	vm_offset_t mem;
100 
101 	if (filename == NULL)
102 		return (EFTYPE);
103 
104 	/* is kernel already loaded? */
105 	fp = file_findfile(NULL, NULL);
106 	if (fp != NULL)
107 		return (EFTYPE);
108 
109 	if ((fd = open(filename, O_RDONLY)) == -1)
110 		return (errno);
111 
112 	if (fstat(fd, &sb) != 0) {
113 		printf("stat failed\n");
114 		error = errno;
115 		close(fd);
116 		return (error);
117 	}
118 
119 	n = read(fd, &lh, sizeof (lh));
120 	if (n != sizeof (lh)) {
121 		printf("error reading kernel header\n");
122 		error = EIO;
123 		goto end;
124 	}
125 
126 	if (lh.boot_flag != BOOTSEC_SIGNATURE) {
127 		printf("invalid magic number\n");
128 		error = EFTYPE;
129 		goto end;
130 	}
131 
132 	setup_sects = lh.setup_sects;
133 	linux_big = 0;
134 	max_cmdline_size = 256;
135 
136 	if (setup_sects > LINUX_MAX_SETUP_SECTS) {
137 		printf("too many setup sectors\n");
138 		error = EFTYPE;
139 		goto end;
140 	}
141 
142 	fp = file_alloc();
143 	if (fp == NULL) {
144 		error = ENOMEM;
145 		goto end;
146 	}
147 
148 	bios_addsmapdata(fp);
149 
150 	if (lh.header == LINUX_MAGIC_SIGNATURE && lh.version >= 0x0200) {
151 		linux_big = lh.loadflags & LINUX_FLAG_BIG_KERNEL;
152 		lh.type_of_loader = LINUX_BOOT_LOADER_TYPE;
153 
154 		if (lh.version >= 0x0206)
155 			max_cmdline_size = lh.cmdline_size + 1;
156 
157 		linux_data_real_addr = find_real_addr(fp);
158 		if (linux_data_real_addr == -1) {
159 			printf("failed to detect suitable low memory\n");
160 			file_discard(fp);
161 			error = ENOMEM;
162 			goto end;
163 		}
164 		if (lh.version >= 0x0201) {
165 			lh.heap_end_ptr = LINUX_HEAP_END_OFFSET;
166 			lh.loadflags |= LINUX_FLAG_CAN_USE_HEAP;
167 		}
168 		if (lh.version >= 0x0202) {
169 			lh.cmd_line_ptr = linux_data_real_addr +
170 			    LINUX_CL_OFFSET;
171 		} else {
172 			lh.cl_magic = LINUX_CL_MAGIC;
173 			lh.cl_offset = LINUX_CL_OFFSET;
174 			lh.setup_move_size = LINUX_CL_OFFSET + max_cmdline_size;
175 		}
176 	} else {
177 		/* old kernel */
178 		lh.cl_magic = LINUX_CL_MAGIC;
179 		lh.cl_offset = LINUX_CL_OFFSET;
180 		setup_sects = LINUX_DEFAULT_SETUP_SECTS;
181 		linux_data_real_addr = LINUX_OLD_REAL_MODE_ADDR;
182 	}
183 	if (setup_sects == 0)
184 		setup_sects = LINUX_DEFAULT_SETUP_SECTS;
185 
186 	data = setup_sects << 9;
187 	text = sb.st_size - data - 512;
188 
189 	/* temporary location of real mode part */
190 	linux_data_tmp_addr = LINUX_BZIMAGE_ADDR + text;
191 
192 	if (!linux_big && text > linux_data_real_addr - LINUX_ZIMAGE_ADDR) {
193 		printf("Linux zImage is too big, use bzImage instead\n");
194 		file_discard(fp);
195 		error = EFBIG;
196 		goto end;
197 	}
198 	printf("   [Linux-%s, setup=0x%x, size=0x%x]\n",
199 	    (linux_big ? "bzImage" : "zImage"), data, text);
200 
201 	/* copy real mode part to place */
202 	i386_copyin(&lh, linux_data_tmp_addr, sizeof (lh));
203 	n = data + 512 - sizeof (lh);
204 	if (archsw.arch_readin(fd, linux_data_tmp_addr+sizeof (lh), n) != n) {
205 		printf("failed to read %s\n", filename);
206 		file_discard(fp);
207 		error = errno;
208 		goto end;
209 	}
210 
211 	/* Clear the heap space. */
212 	if (lh.header != LINUX_MAGIC_SIGNATURE || lh.version < 0x0200) {
213 		memset(PTOV(linux_data_tmp_addr + ((setup_sects + 1) << 9)),
214 		    0, (LINUX_MAX_SETUP_SECTS - setup_sects - 1) << 9);
215 	}
216 
217 	mem = LINUX_BZIMAGE_ADDR;
218 
219 	if (archsw.arch_readin(fd, mem, text) != text) {
220 		printf("failed to read %s\n", filename);
221 		file_discard(fp);
222 		error = EIO;
223 		goto end;
224 	}
225 
226 	fp->f_name = strdup(filename);
227 	if (linux_big)
228 		fp->f_type = strdup("Linux bzImage");
229 	else
230 		fp->f_type = strdup("Linux zImage");
231 
232 	/*
233 	 * NOTE: f_addr and f_size is used here as hint for module
234 	 * allocation, as module location will be f_addr + f_size.
235 	 */
236 	fp->f_addr = linux_data_tmp_addr;
237 	fp->f_size = LINUX_SETUP_MOVE_SIZE;
238 	linux_text_len = text;
239 
240 	/*
241 	 * relocater_data is space allocated in relocater_tramp.S
242 	 * There is space for 3 instances + terminating zero in case
243 	 * all 3 entries are used.
244 	 */
245 	if (linux_big == 0) {
246 		relocater_data[0].src = LINUX_BZIMAGE_ADDR;
247 		relocater_data[0].dest = LINUX_ZIMAGE_ADDR;
248 		relocater_data[0].size = text;
249 		relocater_data[1].src = linux_data_tmp_addr;
250 		relocater_data[1].dest = linux_data_real_addr;
251 		relocater_data[1].size = LINUX_SETUP_MOVE_SIZE;
252 		/* make sure the next entry is zeroed */
253 		relocater_data[2].src = 0;
254 		relocater_data[2].dest = 0;
255 		relocater_data[2].size = 0;
256 	} else {
257 		relocater_data[0].src = linux_data_tmp_addr;
258 		relocater_data[0].dest = linux_data_real_addr;
259 		relocater_data[0].size = LINUX_SETUP_MOVE_SIZE;
260 		/* make sure the next entry is zeroed */
261 		relocater_data[1].src = 0;
262 		relocater_data[1].dest = 0;
263 		relocater_data[1].size = 0;
264 	}
265 
266 	*result = fp;
267 	setenv("kernelname", fp->f_name, 1);
268 end:
269 	close(fd);
270 	return (error);
271 }
272 
273 static int
274 linux_exec(struct preloaded_file *fp)
275 {
276 	struct linux_kernel_header *lh = (struct linux_kernel_header *)
277 	    PTOV(linux_data_tmp_addr);
278 	struct preloaded_file *mfp = fp->f_next;
279 	char *arg, *vga;
280 	char *src, *dst;
281 	int linux_big;
282 	uint32_t moveto, max_addr;
283 	uint16_t segment;
284 	struct i386_devdesc *rootdev;
285 
286 	if (strcmp(fp->f_type, "Linux bzImage") == 0)
287 		linux_big = 1;
288 	else if (strcmp(fp->f_type, "Linux zImage") == 0)
289 		linux_big = 0;
290 	else
291 		return (EFTYPE);
292 
293 	i386_getdev((void **)(&rootdev), fp->f_name, NULL);
294 	if (rootdev != NULL)
295 		relocator_edx = bd_unit2bios(rootdev->d_unit);
296 
297 	/*
298 	 * command line
299 	 * if not set in fp, read from boot-args env
300 	 */
301 	if (fp->f_args == NULL)
302 		fp->f_args = getenv("boot-args");
303 	arg = fp->f_args;		/* it can still be NULL */
304 
305 	/* video mode selection */
306 	if (arg && (vga = strstr(arg, "vga=")) != NULL) {
307 		char *value = vga + 4;
308 		uint16_t vid_mode;
309 
310 		if (strncmp(value, "normal", 6) < 1)
311 			vid_mode = LINUX_VID_MODE_NORMAL;
312 		else if (strncmp(value, "ext", 3) < 1)
313 			vid_mode = LINUX_VID_MODE_EXTENDED;
314 		else if (strncmp(value, "ask", 3) < 1)
315 			vid_mode = LINUX_VID_MODE_ASK;
316 		else {
317 			long mode;
318 			errno = 0;
319 
320 			/*
321 			 * libstand sets ERANGE as only error case;
322 			 * however, the actual value is 16bit, so
323 			 * additional check is needed.
324 			 */
325 			mode = strtol(value, NULL, 0);
326 			if (errno != 0 || mode >> 16 != 0 || mode == 0) {
327 				printf("bad value for video mode\n");
328 				return (EINTR);
329 			}
330 			vid_mode = (uint16_t) mode;
331 		}
332 		lh->vid_mode = vid_mode;
333 	}
334 
335 	src = arg;
336 	dst = (char *)PTOV(linux_data_tmp_addr + LINUX_CL_OFFSET);
337 	if (src != NULL) {
338 		while (*src != 0 && dst < (char *)
339 		    PTOV(linux_data_tmp_addr + LINUX_CL_END_OFFSET))
340 			*(dst++) = *(src++);
341 	}
342 	*dst = 0;
343 
344 	/* set up module relocation */
345 	if (mfp != NULL) {
346 		moveto = (bios_extmem / 1024 + 0x400) << 10;
347 		moveto = (moveto - mfp->f_size) & 0xfffff000;
348 		max_addr = (lh->header == LINUX_MAGIC_SIGNATURE &&
349 		    lh->version >= 0x0203 ?
350 		    lh->initrd_addr_max : LINUX_INITRD_MAX_ADDRESS);
351 		if (moveto + mfp->f_size >= max_addr)
352 			moveto = (max_addr - mfp->f_size) & 0xfffff000;
353 
354 		/*
355 		 * XXX: Linux 2.3.xx has a bug in the memory range check,
356 		 * so avoid the last page.
357 		 * XXX: Linux 2.2.xx has a bug in the memory range check,
358 		 * which is worse than that of Linux 2.3.xx, so avoid the
359 		 * last 64kb. *sigh*
360 		 */
361 		moveto -= 0x10000;
362 
363 		/* need to relocate initrd first */
364 		if (linux_big == 0) {
365 			relocater_data[2].src = relocater_data[1].src;
366 			relocater_data[2].dest = relocater_data[1].dest;
367 			relocater_data[2].size = relocater_data[1].size;
368 			relocater_data[1].src = relocater_data[0].src;
369 			relocater_data[1].dest = relocater_data[0].dest;
370 			relocater_data[1].size = relocater_data[0].size;
371 			relocater_data[0].src = mfp->f_addr;
372 			relocater_data[0].dest = moveto;
373 			relocater_data[0].size = mfp->f_size;
374 		} else {
375 			relocater_data[1].src = relocater_data[0].src;
376 			relocater_data[1].dest = relocater_data[0].dest;
377 			relocater_data[1].size = relocater_data[0].size;
378 			relocater_data[0].src = mfp->f_addr;
379 			relocater_data[0].dest = moveto;
380 			relocater_data[0].size = mfp->f_size;
381 		}
382 		lh->ramdisk_image = moveto;
383 		lh->ramdisk_size = mfp->f_size;
384 	}
385 
386 	segment = linux_data_real_addr >> 4;
387 	relocator_ds = segment;
388 	relocator_es = segment;
389 	relocator_fs = segment;
390 	relocator_gs = segment;
391 	relocator_ss = segment;
392 	relocator_sp = LINUX_ESP;
393 	relocator_ip = 0;
394 	relocator_cs = segment + 0x20;
395 	relocator_a20_enabled = 1;
396 	i386_copyin(relocater, 0x600, relocater_size);
397 
398 	dev_cleanup();
399 
400 	__exec((void *)0x600);
401 
402 	panic("exec returned");
403 
404 	return (EINTR);		/* not reached */
405 }
406 
407 static int
408 linux_loadinitrd(char *filename, uint64_t dest, struct preloaded_file **result)
409 {
410 	struct preloaded_file *mfp;
411 	vm_offset_t mem;
412 
413 	if (filename == NULL)
414 		return (EFTYPE);
415 
416 	/* check if the kernel is loaded */
417 	mfp = file_findfile(NULL, "Linux bzImage");
418 	if (mfp == NULL)
419 		mfp = file_findfile(NULL, "Linux zImage");
420 	if (mfp == NULL)
421 		return (EFTYPE);
422 
423 	mfp = file_loadraw(filename, "module", 0, NULL, 0);
424 	if (mfp == NULL)
425 		return (EFTYPE);
426 	*result = mfp;
427 	return (0);
428 }
429 
430 static int linux_execinitrd(struct preloaded_file *pf)
431 {
432 	return (EFTYPE);
433 }
434