Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/i386/isoboot/isoboot.c
34860 views
1
/*-
2
* Copyright (c) 1998 Robert Nordier
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms are freely
6
* permitted provided that the above copyright notice and this
7
* paragraph and the following disclaimer are duplicated in all
8
* such forms.
9
*
10
* This software is provided "AS IS" and without any express or
11
* implied warranties, including, without limitation, the implied
12
* warranties of merchantability and fitness for a particular
13
* purpose.
14
*/
15
16
#include <sys/param.h>
17
#include <sys/gpt.h>
18
#include <sys/dirent.h>
19
#include <sys/reboot.h>
20
21
#include <machine/bootinfo.h>
22
#include <machine/elf.h>
23
#include <machine/pc/bios.h>
24
#include <machine/psl.h>
25
26
#include <stdarg.h>
27
28
#include <a.out.h>
29
30
#include <btxv86.h>
31
32
#include "stand.h"
33
34
#include "bootargs.h"
35
#include "lib.h"
36
#include "rbx.h"
37
#include "drv.h"
38
#include "cons.h"
39
#include "gpt.h"
40
#include "paths.h"
41
42
#define ARGS 0x900
43
#define NOPT 14
44
#define NDEV 3
45
#define MEM_BASE 0x12
46
#define MEM_EXT 0x15
47
48
#define DRV_HARD 0x80
49
#define DRV_MASK 0x7f
50
51
#define TYPE_AD 0
52
#define TYPE_DA 1
53
#define TYPE_MAXHARD TYPE_DA
54
#define TYPE_FD 2
55
56
extern uint32_t _end;
57
58
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
59
static const unsigned char flags[NOPT] = {
60
RBX_DUAL,
61
RBX_SERIAL,
62
RBX_ASKNAME,
63
RBX_CDROM,
64
RBX_CONFIG,
65
RBX_KDB,
66
RBX_GDB,
67
RBX_MUTE,
68
RBX_NOINTR,
69
RBX_PAUSE,
70
RBX_QUIET,
71
RBX_DFLTROOT,
72
RBX_SINGLE,
73
RBX_VERBOSE
74
};
75
uint32_t opts;
76
77
static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
78
static const unsigned char dev_maj[NDEV] = {30, 4, 2};
79
80
static struct dsk dsk;
81
static char kname[1024];
82
static int comspeed = SIOSPD;
83
static struct bootinfo bootinfo;
84
85
static vm_offset_t high_heap_base;
86
static uint32_t bios_basemem, bios_extmem, high_heap_size;
87
88
static struct bios_smap smap;
89
90
/*
91
* The minimum amount of memory to reserve in bios_extmem for the heap.
92
*/
93
#define HEAP_MIN (3 * 1024 * 1024)
94
95
static char *heap_next;
96
static char *heap_end;
97
98
int main(void);
99
100
static void load(void);
101
static int parse_cmds(char *, int *);
102
103
static uint8_t ls, dsk_meta;
104
static uint32_t fs_off;
105
106
#include "cd9660read.c"
107
108
static inline int
109
xfsread(uint64_t inode, void *buf, size_t nbyte)
110
{
111
112
if ((size_t)cd9660_fsread(inode, buf, nbyte) != nbyte) {
113
printf("Invalid %s\n", "format");
114
return (-1);
115
}
116
return (0);
117
}
118
119
static void
120
bios_getmem(void)
121
{
122
uint64_t size;
123
124
/* Parse system memory map */
125
v86.ebx = 0;
126
do {
127
v86.ctl = V86_FLAGS;
128
v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/
129
v86.eax = 0xe820;
130
v86.ecx = sizeof(struct bios_smap);
131
v86.edx = SMAP_SIG;
132
v86.es = VTOPSEG(&smap);
133
v86.edi = VTOPOFF(&smap);
134
v86int();
135
if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
136
break;
137
/* look for a low-memory segment that's large enough */
138
if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
139
(smap.length >= (512 * 1024)))
140
bios_basemem = smap.length;
141
/* look for the first segment in 'extended' memory */
142
if ((smap.type == SMAP_TYPE_MEMORY) &&
143
(smap.base == 0x100000)) {
144
bios_extmem = smap.length;
145
}
146
147
/*
148
* Look for the largest segment in 'extended' memory beyond
149
* 1MB but below 4GB.
150
*/
151
if ((smap.type == SMAP_TYPE_MEMORY) &&
152
(smap.base > 0x100000) && (smap.base < 0x100000000ull)) {
153
size = smap.length;
154
155
/*
156
* If this segment crosses the 4GB boundary,
157
* truncate it.
158
*/
159
if (smap.base + size > 0x100000000ull)
160
size = 0x100000000ull - smap.base;
161
162
if (size > high_heap_size) {
163
high_heap_size = size;
164
high_heap_base = smap.base;
165
}
166
}
167
} while (v86.ebx != 0);
168
169
/* Fall back to the old compatibility function for base memory */
170
if (bios_basemem == 0) {
171
v86.ctl = 0;
172
v86.addr = 0x12; /* int 0x12 */
173
v86int();
174
175
bios_basemem = (v86.eax & 0xffff) * 1024;
176
}
177
178
/*
179
* Fall back through several compatibility functions for extended
180
* memory
181
*/
182
if (bios_extmem == 0) {
183
v86.ctl = V86_FLAGS;
184
v86.addr = 0x15; /* int 0x15 function 0xe801*/
185
v86.eax = 0xe801;
186
v86int();
187
if (!(v86.efl & 1)) {
188
bios_extmem = ((v86.ecx & 0xffff) +
189
((v86.edx & 0xffff) * 64)) * 1024;
190
}
191
}
192
if (bios_extmem == 0) {
193
v86.ctl = 0;
194
v86.addr = 0x15; /* int 0x15 function 0x88*/
195
v86.eax = 0x8800;
196
v86int();
197
bios_extmem = (v86.eax & 0xffff) * 1024;
198
}
199
200
/*
201
* If we have extended memory and did not find a suitable heap
202
* region in the SMAP, use the last 3MB of 'extended' memory as a
203
* high heap candidate.
204
*/
205
if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
206
high_heap_size = HEAP_MIN;
207
high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
208
}
209
}
210
211
int
212
main(void)
213
{
214
char cmd[512], cmdtmp[512];
215
ssize_t sz;
216
int autoboot, dskupdated;
217
uint64_t ino;
218
219
bios_getmem();
220
221
if (high_heap_size > 0) {
222
heap_end = PTOV(high_heap_base + high_heap_size);
223
heap_next = PTOV(high_heap_base);
224
} else {
225
heap_next = (char *)
226
(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
227
heap_end = (char *)PTOV(bios_basemem);
228
}
229
setheap(heap_next, heap_end);
230
231
v86.ctl = V86_FLAGS;
232
v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
233
dsk.drive = *(uint8_t *)PTOV(ARGS);
234
dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
235
dsk.unit = dsk.drive & DRV_MASK;
236
dsk.part = -1;
237
dsk.start = 0;
238
bootinfo.bi_version = BOOTINFO_VERSION;
239
bootinfo.bi_size = sizeof(bootinfo);
240
bootinfo.bi_basemem = bios_basemem / 1024;
241
bootinfo.bi_extmem = bios_extmem / 1024;
242
bootinfo.bi_memsizes_valid++;
243
bootinfo.bi_bios_dev = dsk.drive;
244
245
autoboot = 1;
246
*cmd = '\0';
247
248
for (;;) {
249
*kname = '\0';
250
if ((ino = cd9660_lookup(PATH_CONFIG)) ||
251
(ino = cd9660_lookup(PATH_DOTCONFIG))) {
252
sz = cd9660_fsread(ino, cmd, sizeof(cmd) - 1);
253
cmd[(sz < 0) ? 0 : sz] = '\0';
254
}
255
if (*cmd != '\0') {
256
memcpy(cmdtmp, cmd, sizeof(cmdtmp));
257
if (parse_cmds(cmdtmp, &dskupdated))
258
break;
259
if (!OPT_CHECK(RBX_QUIET))
260
printf("%s: %s", PATH_CONFIG, cmd);
261
*cmd = '\0';
262
}
263
264
if (autoboot && keyhit(3)) {
265
if (*kname == '\0')
266
memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
267
break;
268
}
269
autoboot = 0;
270
271
/*
272
* Try to exec stage 3 boot loader. If interrupted by a
273
* keypress, or in case of failure, try to load a kernel
274
* directly instead.
275
*/
276
if (*kname != '\0')
277
load();
278
memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER));
279
load();
280
memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
281
load();
282
dsk_meta = 0;
283
}
284
285
/* Present the user with the boot2 prompt. */
286
287
for (;;) {
288
if (!OPT_CHECK(RBX_QUIET)) {
289
printf("\nFreeBSD/x86 boot\n"
290
"Default: %u:%s(%up%u)%s\n"
291
"boot: ",
292
dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
293
dsk.part, kname);
294
}
295
if (ioctrl & IO_SERIAL)
296
sio_flush();
297
*cmd = '\0';
298
if (keyhit(0))
299
getstr(cmd, sizeof(cmd));
300
else if (!OPT_CHECK(RBX_QUIET))
301
putchar('\n');
302
if (parse_cmds(cmd, &dskupdated)) {
303
putchar('\a');
304
continue;
305
}
306
load();
307
}
308
/* NOTREACHED */
309
}
310
311
/* Needed so btxld can link us properly; do not remove. */
312
void
313
exit(int x)
314
{
315
316
while (1);
317
__unreachable();
318
}
319
320
static void
321
load(void)
322
{
323
union {
324
struct exec ex;
325
Elf32_Ehdr eh;
326
} hdr;
327
static Elf32_Phdr ep[2];
328
static Elf32_Shdr es[2];
329
caddr_t p;
330
uint64_t ino;
331
uint32_t addr, x;
332
int fmt, i, j;
333
334
if (!(ino = cd9660_lookup(kname))) {
335
if (!ls) {
336
printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG,
337
kname, dsk.drive & DRV_MASK, dev_nm[dsk.type],
338
dsk.unit,
339
dsk.part);
340
}
341
return;
342
}
343
if (xfsread(ino, &hdr, sizeof(hdr)))
344
return;
345
if (N_GETMAGIC(hdr.ex) == ZMAGIC)
346
fmt = 0;
347
else if (IS_ELF(hdr.eh))
348
fmt = 1;
349
else {
350
printf("Invalid %s\n", "format");
351
return;
352
}
353
if (fmt == 0) {
354
addr = hdr.ex.a_entry & 0xffffff;
355
p = PTOV(addr);
356
fs_off = PAGE_SIZE;
357
if (xfsread(ino, p, hdr.ex.a_text))
358
return;
359
p += roundup2(hdr.ex.a_text, PAGE_SIZE);
360
if (xfsread(ino, p, hdr.ex.a_data))
361
return;
362
p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
363
bootinfo.bi_symtab = VTOP(p);
364
memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
365
p += sizeof(hdr.ex.a_syms);
366
if (hdr.ex.a_syms) {
367
if (xfsread(ino, p, hdr.ex.a_syms))
368
return;
369
p += hdr.ex.a_syms;
370
if (xfsread(ino, p, sizeof(int)))
371
return;
372
x = *(uint32_t *)p;
373
p += sizeof(int);
374
x -= sizeof(int);
375
if (xfsread(ino, p, x))
376
return;
377
p += x;
378
}
379
} else {
380
fs_off = hdr.eh.e_phoff;
381
for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
382
if (xfsread(ino, ep + j, sizeof(ep[0])))
383
return;
384
if (ep[j].p_type == PT_LOAD)
385
j++;
386
}
387
for (i = 0; i < 2; i++) {
388
p = PTOV(ep[i].p_paddr & 0xffffff);
389
fs_off = ep[i].p_offset;
390
if (xfsread(ino, p, ep[i].p_filesz))
391
return;
392
}
393
p += roundup2(ep[1].p_memsz, PAGE_SIZE);
394
bootinfo.bi_symtab = VTOP(p);
395
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
396
fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
397
(hdr.eh.e_shstrndx + 1);
398
if (xfsread(ino, &es, sizeof(es)))
399
return;
400
for (i = 0; i < 2; i++) {
401
memcpy(p, &es[i].sh_size,
402
sizeof(es[i].sh_size));
403
p += sizeof(es[i].sh_size);
404
fs_off = es[i].sh_offset;
405
if (xfsread(ino, p, es[i].sh_size))
406
return;
407
p += es[i].sh_size;
408
}
409
}
410
addr = hdr.eh.e_entry & 0xffffff;
411
}
412
bootinfo.bi_esymtab = VTOP(p);
413
bootinfo.bi_kernelname = VTOP(kname);
414
bootinfo.bi_bios_dev = dsk.drive;
415
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
416
MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.unit, 0),
417
0, 0, 0, VTOP(&bootinfo));
418
}
419
420
static int
421
parse_cmds(char *cmdstr, int *dskupdated)
422
{
423
char *arg;
424
char *ep, *p, *q;
425
const char *cp;
426
unsigned int drv;
427
int c, i, j;
428
429
arg = cmdstr;
430
*dskupdated = 0;
431
while ((c = *arg++)) {
432
if (c == ' ' || c == '\t' || c == '\n')
433
continue;
434
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
435
ep = p;
436
if (*p)
437
*p++ = 0;
438
if (c == '-') {
439
while ((c = *arg++)) {
440
if (c == 'P') {
441
if (*(uint8_t *)PTOV(0x496) & 0x10) {
442
cp = "yes";
443
} else {
444
opts |= OPT_SET(RBX_DUAL) |
445
OPT_SET(RBX_SERIAL);
446
cp = "no";
447
}
448
printf("Keyboard: %s\n", cp);
449
continue;
450
} else if (c == 'S') {
451
j = 0;
452
while ((unsigned int)(i = *arg++ - '0')
453
<= 9)
454
j = j * 10 + i;
455
if (j > 0 && i == -'0') {
456
comspeed = j;
457
break;
458
}
459
/*
460
* Fall through to error below
461
* ('S' not in optstr[]).
462
*/
463
}
464
for (i = 0; c != optstr[i]; i++)
465
if (i == NOPT - 1)
466
return (-1);
467
opts ^= OPT_SET(flags[i]);
468
}
469
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
470
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
471
if (ioctrl & IO_SERIAL) {
472
if (sio_init(115200 / comspeed) != 0)
473
ioctrl &= ~IO_SERIAL;
474
}
475
} else {
476
for (q = arg--; *q && *q != '('; q++);
477
if (*q) {
478
drv = -1;
479
if (arg[1] == ':') {
480
drv = *arg - '0';
481
if (drv > 9)
482
return (-1);
483
arg += 2;
484
}
485
if (q - arg != 2)
486
return (-1);
487
for (i = 0; arg[0] != dev_nm[i][0] ||
488
arg[1] != dev_nm[i][1]; i++)
489
if (i == NDEV - 1)
490
return (-1);
491
dsk.type = i;
492
arg += 3;
493
dsk.unit = *arg - '0';
494
if (arg[1] != 'p' || dsk.unit > 9)
495
return (-1);
496
arg += 2;
497
dsk.part = *arg - '0';
498
if (dsk.part < 1 || dsk.part > 9)
499
return (-1);
500
arg++;
501
if (arg[0] != ')')
502
return (-1);
503
arg++;
504
if (drv == -1)
505
drv = dsk.unit;
506
dsk.drive = (dsk.type <= TYPE_MAXHARD
507
? DRV_HARD : 0) + drv;
508
*dskupdated = 1;
509
}
510
if ((i = ep - arg)) {
511
if ((size_t)i >= sizeof(kname))
512
return (-1);
513
memcpy(kname, arg, i + 1);
514
}
515
}
516
arg = p;
517
}
518
return (0);
519
}
520
521