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