loadbsd.c revision 1.16 1 /* $NetBSD: loadbsd.c,v 1.16 1995/02/12 19:19:41 chopps Exp $ */
2
3 /*
4 * Copyright (c) 1994 Michael L. Hitch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Michael L. Hitch.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34 #include <a.out.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <signal.h>
40 #ifdef __NetBSD__
41 #include <err.h>
42 #endif
43 #include <exec/types.h>
44 #include <exec/execbase.h>
45 #include <exec/memory.h>
46 #include <exec/resident.h>
47 #include <graphics/gfxbase.h>
48 #include <libraries/configregs.h>
49 #include <libraries/configvars.h>
50 #include <libraries/expansion.h>
51 #include <libraries/expansionbase.h>
52
53 #include <inline/exec.h>
54 #include <inline/expansion.h>
55 #include <inline/graphics.h>
56
57 /* Get definitions for boothowto */
58 #include "reboot.h"
59
60 #undef __LDPGSZ
61 #define __LDPGSZ 8192
62
63 #ifndef __NetBSD__
64 #ifndef __P
65 #ifdef __STDC__
66 #define __P(x) x
67 #else
68 #define __P(x)
69 #endif
70 #endif
71 void err __P((int, const char *, ...));
72 void errx __P((int, const char *, ...));
73 void warn __P((const char *, ...));
74 void warnx __P((const char *, ...));
75 #endif
76
77 /*
78 * Version history:
79 * 1.x Kernel parameter passing version check.
80 * 2.0 Added symbol table end address and symbol table support.
81 * 2.1 03/23/94 - Round up end of fastram segment.
82 * Check fastram segment size for minimum of 2M.
83 * Use largest segment of highest priority if -p option.
84 * Print out fastram size in KB if not a multiple of MB.
85 * 2.2 03/24/94 - Zero out all unused registers.
86 * Started version history comment.
87 * 2.3 04/26/94 - Added -D option to enter debugger on boot.
88 * 2.4 04/30/94 - Cpuid includes base machine type.
89 * Also check if CPU is capable of running NetBSD.
90 * 2.5 05/17/94 - Add check for "A3000 bonus".
91 * 2.6 06/05/94 - Added -c option to override machine type.
92 * 2.7 06/15/94 - Pass E clock frequency.
93 * 2.8 06/22/94 - Fix supervisor stack usage.
94 * 2.9 06/26/94 - Use PAL flag for E clock freq on pre 2.0 WB
95 * Added AGA enable parameter
96 * 2.10 12/22/94 - Use FindResident() & OpenResource() for machine
97 * type detection.
98 * Add -n flag & option for non-contiguous memory.
99 * 01/28/95 - Corrected -n on usage & help messages.
100 */
101 static const char _version[] = "$VER: LoadBSD 2.10 (28.1.95)";
102
103 /*
104 * Kernel parameter passing version
105 * 1: first version of loadbsd
106 * 2: needs esym location passed in a4
107 */
108 #define KERNEL_PARAMETER_VERSION 2
109
110 #define MAXMEMSEG 16
111 struct boot_memlist {
112 u_int m_nseg; /* num_mem; */
113 struct boot_memseg {
114 u_int ms_start;
115 u_int ms_size;
116 u_short ms_attrib;
117 short ms_pri;
118 } m_seg[MAXMEMSEG];
119 };
120 struct boot_memlist memlist;
121 struct boot_memlist *kmemlist;
122
123
124 void get_mem_config __P((void **, u_long *, u_long *));
125 void get_cpuid __P((void));
126 void get_eclock __P((void));
127 void get_AGA __P((void));
128 void usage __P((void));
129 void verbose_usage __P((void));
130 void Version __P((void));
131
132 extern struct ExecBase *SysBase;
133 extern char *optarg;
134 extern int optind;
135
136 int k_flag;
137 int p_flag;
138 int t_flag;
139 int reqmemsz;
140 int S_flag;
141 u_long cpuid;
142 long eclock_freq;
143 long amiga_flags;
144 char *program_name;
145 char *kname;
146 struct ExpansionBase *ExpansionBase;
147 struct GfxBase *GfxBase;
148
149
150 int
151 main(argc, argv)
152 int argc;
153 char **argv;
154 {
155 struct exec e;
156 struct ConfigDev *cd, *kcd;
157 u_long fmemsz, cmemsz;
158 int fd, boothowto, ksize, textsz, stringsz, ncd, i, mem_ix, ch;
159 u_short *kvers;
160 int *nkcd;
161 u_char *kp;
162 void *fmem;
163 char *esym;
164
165 program_name = argv[0];
166 boothowto = RB_SINGLE;
167
168 if (argc < 2)
169 usage();
170 if ((GfxBase = (void *)OpenLibrary(GRAPHICSNAME, 0)) == NULL)
171 err(20, "can't open graphics library");
172 if ((ExpansionBase=(void *)OpenLibrary(EXPANSIONNAME, 0)) == NULL)
173 err(20, "can't open expansion library");
174
175 while ((ch = getopt(argc, argv, "aAbc:Dhkm:n:ptSV")) != EOF) {
176 switch (ch) {
177 case 'k':
178 k_flag = 1;
179 break;
180 case 'a':
181 boothowto &= ~(RB_SINGLE);
182 boothowto |= RB_AUTOBOOT;
183 break;
184 case 'b':
185 boothowto |= RB_ASKNAME;
186 break;
187 case 'p':
188 p_flag = 1;
189 break;
190 case 't':
191 t_flag = 1;
192 break;
193 case 'm':
194 reqmemsz = atoi(optarg) * 1024;
195 break;
196 case 'V':
197 fprintf(stderr,"%s\n",_version + 6);
198 break;
199 case 'S':
200 S_flag = 1;
201 break;
202 case 'D':
203 boothowto |= RB_KDB;
204 break;
205 case 'c':
206 cpuid = atoi(optarg) << 16;
207 break;
208 case 'A':
209 amiga_flags |= 1;
210 break;
211 case 'n':
212 i = atoi(optarg);
213 if (i >= 0 && i <= 3)
214 amiga_flags |= i << 1;
215 else
216 err(20, "-n option must be 0, 1, 2, or 3");
217 break;
218 case 'h':
219 verbose_usage();
220 default:
221 usage();
222 }
223 }
224 argc -= optind;
225 argv += optind;
226
227 if (argc != 1)
228 usage();
229 kname = argv[0];
230
231 if ((fd = open(kname, 0)) < 0)
232 err(20, "open");
233 if (read(fd, &e, sizeof(e)) != sizeof(e))
234 err(20, "reading exec");
235 if (e.a_magic != NMAGIC)
236 err(20, "unknown binary");
237
238 for (cd = 0, ncd = 0; cd = FindConfigDev(cd, -1, -1); ncd++)
239 ;
240 get_mem_config(&fmem, &fmemsz, &cmemsz);
241 get_cpuid();
242 get_eclock();
243 get_AGA();
244
245 textsz = (e.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
246 esym = NULL;
247 ksize = textsz + e.a_data + e.a_bss + ncd * sizeof(*cd)
248 + 4 + memlist.m_nseg * sizeof(struct boot_memseg) + 4;
249
250 /*
251 * get symbol table size & string size
252 * (should check kernel version to see if it will handle it)
253 */
254 if (S_flag && e.a_syms) {
255 if (lseek(fd, e.a_text + e.a_data + e.a_syms, SEEK_CUR) <= 0
256 || read(fd, &stringsz, 4) != 4
257 || lseek(fd, sizeof(e), SEEK_SET) < 0)
258 err(20, "lseek for symbols");
259 ksize += e.a_syms + 4 + stringsz;
260 }
261
262 kp = (u_char *)malloc(ksize);
263 if (t_flag) {
264 for (i = 0; i < memlist.m_nseg; ++i) {
265 printf("mem segment %d: start=%08lx size=%08lx"
266 " attribute=%04lx pri=%d\n",
267 i + 1, memlist.m_seg[i].ms_start,
268 memlist.m_seg[i].ms_size,
269 memlist.m_seg[i].ms_attrib,
270 memlist.m_seg[i].ms_pri);
271 }
272 }
273 if (kp == NULL)
274 err(20, "failed malloc %d\n", ksize);
275
276 if (read(fd, kp, e.a_text) != e.a_text
277 || read(fd, kp + textsz, e.a_data) != e.a_data)
278 err(20, "unable to read kernel image\n");
279
280 if (k_flag) {
281 fmem += 4 * 1024 * 1024;
282 fmemsz -= 4 * 1024 * 1024;
283 }
284
285 if (reqmemsz && reqmemsz <= fmemsz)
286 fmemsz = reqmemsz;
287 if (boothowto & RB_AUTOBOOT)
288 printf("Autobooting...");
289 if (boothowto & RB_ASKNAME)
290 printf("Askboot...");
291
292 printf("Using %d%c FASTMEM at 0x%x, %dM CHIPMEM\n",
293 (fmemsz & 0xfffff) ? fmemsz >> 10 : fmemsz >> 20,
294 (fmemsz & 0xfffff) ? 'K' : 'M', fmem, cmemsz >> 20);
295 kvers = (u_short *)(kp + e.a_entry - 2);
296 if (*kvers > KERNEL_PARAMETER_VERSION && *kvers != 0x4e73)
297 err(20, "newer loadbsd required: %d\n", *kvers);
298 if ((cpuid & AFB_68020) == 0)
299 err(20, "cpu not supported");
300 /*
301 * give them a chance to read the information...
302 */
303 sleep(2);
304
305 bzero(kp + textsz + e.a_data, e.a_bss);
306 /*
307 * If symbols wanted (and kernel can handle them),
308 * load symbol table & strings and set esym to end.
309 */
310 nkcd = (int *)(kp + textsz + e.a_data + e.a_bss);
311 if (*kvers != 0x4e73 && *kvers > 1 && S_flag && e.a_syms) {
312 *nkcd++ = e.a_syms;
313 read(fd, (char *)nkcd, e.a_syms);
314 nkcd = (int *)((char *)nkcd + e.a_syms);
315 read(fd, (char *)nkcd, stringsz);
316 nkcd = (int*)((char *)nkcd + stringsz);
317 esym = (char *)(textsz + e.a_data + e.a_bss
318 + e.a_syms + 4 + stringsz);
319 }
320 *nkcd = ncd;
321
322 kcd = (struct ConfigDev *)(nkcd + 1);
323 while(cd = FindConfigDev(cd, -1, -1))
324 *kcd++ = *cd;
325
326 kmemlist = (struct boot_memlist *)kcd;
327 kmemlist->m_nseg = memlist.m_nseg;
328 for (mem_ix = 0; mem_ix < memlist.m_nseg; mem_ix++)
329 kmemlist->m_seg[mem_ix] = memlist.m_seg[mem_ix];
330 /*
331 * if test option set, done
332 */
333 if (t_flag)
334 exit(0);
335
336 /*
337 * XXX AGA startup - may need more
338 */
339 LoadView(NULL); /* Don't do this if AGA active? */
340 startit(kp, ksize, e.a_entry, fmem, fmemsz, cmemsz, boothowto, esym,
341 cpuid, eclock_freq, amiga_flags);
342 /*NOTREACHED*/
343 }
344
345 void
346 get_mem_config(fmem, fmemsz, cmemsz)
347 void **fmem;
348 u_long *fmemsz, *cmemsz;
349 {
350 struct MemHeader *mh, *nmh;
351 u_int segsz, seg, eseg, nmem;
352 char mempri;
353
354 nmem = 0;
355 mempri = -128;
356 *fmemsz = 0;
357 *cmemsz = 0;
358
359 /*
360 * walk thru the exec memory list
361 */
362 Forbid();
363 for (mh = (void *) SysBase->MemList.lh_Head;
364 nmh = (void *) mh->mh_Node.ln_Succ; mh = nmh, nmem++) {
365 memlist.m_seg[nmem].ms_attrib = mh->mh_Attributes;
366 memlist.m_seg[nmem].ms_pri = mh->mh_Node.ln_Pri;
367 seg = (u_int)mh->mh_Lower;
368 eseg = (u_int)mh->mh_Upper;
369 segsz = eseg - seg;
370 memlist.m_seg[nmem].ms_size = segsz;
371 memlist.m_seg[nmem].ms_start = seg;
372
373 if (mh->mh_Attributes & MEMF_CHIP) {
374 /*
375 * there should hardly be more than one entry for
376 * chip mem, but handle it the same nevertheless
377 * cmem always starts at 0, so include vector area
378 */
379 memlist.m_seg[nmem].ms_start = seg = 0;
380 /*
381 * round to multiple of 512K
382 */
383 segsz = (segsz + 512 * 1024 - 1) & -(512 * 1024);
384 memlist.m_seg[nmem].ms_size = segsz;
385 if (segsz > *cmemsz)
386 *cmemsz = segsz;
387 continue;
388 }
389 /*
390 * some heuristics..
391 */
392 seg &= -__LDPGSZ;
393 eseg = (eseg + __LDPGSZ - 1) & -__LDPGSZ;
394
395 /*
396 * get the mem back stolen by incore kickstart on
397 * A3000 with V36 bootrom.
398 */
399 if (eseg == 0x07f80000)
400 eseg = 0x08000000;
401
402 /*
403 * or by zkick on a A2000.
404 */
405 if (seg == 0x280000 &&
406 strcmp(mh->mh_Node.ln_Name, "zkick memory") == 0)
407 seg = 0x200000;
408
409 segsz = eseg - seg;
410 memlist.m_seg[nmem].ms_start = seg;
411 memlist.m_seg[nmem].ms_size = segsz;
412 /*
413 * If this segment is smaller than 2M,
414 * don't use it to load the kernel
415 */
416 if (segsz < 2 * 1024 * 1024)
417 continue;
418 /*
419 * if p_flag is set, select memory by priority
420 * instead of size
421 */
422 if ((!p_flag && segsz > *fmemsz) || (p_flag &&
423 mempri <= mh->mh_Node.ln_Pri && segsz > *fmemsz)) {
424 *fmemsz = segsz;
425 *fmem = (void *)seg;
426 mempri = mh->mh_Node.ln_Pri;
427 }
428 }
429 memlist.m_nseg = nmem;
430 Permit();
431 }
432
433 /*
434 * Try to determine the machine ID by searching the resident module list
435 * for modules only present on specific machines. (Thanks, Bill!)
436 */
437 void
438 get_cpuid()
439 {
440 u_long *rl;
441 struct Resident *rm;
442 struct Node *rn; /* Resource node entry */
443
444 cpuid |= SysBase->AttnFlags; /* get FPU and CPU flags */
445 if (cpuid & 0xffff0000) {
446 switch (cpuid >> 16) {
447 case 500:
448 case 600:
449 case 1000:
450 case 1200:
451 case 2000:
452 case 3000:
453 case 4000:
454 return;
455 default:
456 printf("machine Amiga %d is not recognized\n",
457 cpuid >> 16);
458 exit(1);
459 }
460 }
461 if (FindResident("A4000 Bonus") || FindResident("A1000 Bonus"))
462 cpuid |= 4000 << 16;
463 else if (FindResident("A3000 Bonus") || FindResident("A3000 bonus"))
464 cpuid |= 3000 << 16;
465 else if (OpenResource("card.resource")) {
466 /* Test for AGA? */
467 cpuid |= 1200 << 16;
468 }
469 /*
470 * Nothing found, it's probably an A2000 or A500
471 */
472 if ((cpuid >> 16) == 0)
473 cpuid |= 2000 << 16;
474 }
475
476 void
477 get_eclock()
478 {
479 /* Fix for 1.3 startups? */
480 if (SysBase->LibNode.lib_Version > 36)
481 eclock_freq = SysBase->ex_EClockFrequency;
482 else
483 eclock_freq = (GfxBase->DisplayFlags & PAL) ?
484 709379 : 715909;
485 }
486
487 void
488 get_AGA()
489 {
490 /*
491 * Determine if an AGA mode is active
492 */
493 }
494
495
496 asm("
497 .set ABSEXECBASE,4
498
499 .text
500 .globl _startit
501
502 _startit:
503 movel sp,a3
504 movel 4:w,a6
505 lea pc@(start_super-.+2),a5
506 jmp a6@(-0x1e) | supervisor-call
507
508 start_super:
509 movew #0x2700,sr
510
511 | the BSD kernel wants values into the following registers:
512 | a0: fastmem-start
513 | d0: fastmem-size
514 | d1: chipmem-size
515 | d3: Amiga specific flags
516 | d4: E clock frequency
517 | d5: AttnFlags (cpuid)
518 | d7: boothowto
519 | a4: esym location
520 | All other registers zeroed for possible future requirements.
521
522 lea pc@(_startit-.+2),sp | make sure we have a good stack ***
523 movel a3@(4),a1 | loaded kernel
524 movel a3@(8),d2 | length of loaded kernel
525 | movel a3@(12),sp | entry point in stack pointer
526 movel a3@(12),sp@- | push entry point ***
527 movel a3@(16),a0 | fastmem-start
528 movel a3@(20),d0 | fastmem-size
529 movel a3@(24),d1 | chipmem-size
530 movel a3@(28),d7 | boothowto
531 movel a3@(32),a4 | esym
532 movel a3@(36),d5 | cpuid
533 movel a3@(40),d4 | E clock frequency
534 movel a3@(44),d3 | Amiga flags
535 subl a5,a5 | target, load to 0
536
537 btst #3,(ABSEXECBASE)@(0x129) | AFB_68040,SysBase->AttnFlags
538 beq not040
539
540 | Turn off 68040 MMU
541
542 .word 0x4e7b,0xd003 | movec a5,tc
543 .word 0x4e7b,0xd806 | movec a5,urp
544 .word 0x4e7b,0xd807 | movec a5,srp
545 .word 0x4e7b,0xd004 | movec a5,itt0
546 .word 0x4e7b,0xd005 | movec a5,itt1
547 .word 0x4e7b,0xd006 | movec a5,dtt0
548 .word 0x4e7b,0xd007 | movec a5,dtt1
549 bra nott
550
551 not040:
552 lea pc@(zero-.+2),a3
553 pmove a3@,tc | Turn off MMU
554 lea pc@(nullrp-.+2),a3
555 pmove a3@,crp | Turn off MMU some more
556 pmove a3@,srp | Really, really, turn off MMU
557
558 | Turn off 68030 TT registers
559
560 btst #2,(ABSEXECBASE)@(0x129) | AFB_68030,SysBase->AttnFlags
561 beq nott | Skip TT registers if not 68030
562 lea pc@(zero-.+2),a3
563 .word 0xf013,0x0800 | pmove a3@,tt0 (gas only knows about 68851 ops..)
564 .word 0xf013,0x0c00 | pmove a3@,tt1 (gas only knows about 68851 ops..)
565
566 nott:
567
568 movew #(1<<9),0xdff096 | disable DMA
569
570 L0:
571 moveb a1@+,a5@+
572 subl #1,d2
573 bcc L0
574
575
576 moveq #0,d2 | zero out unused registers
577 moveq #0,d6 | (might make future compatibility
578 movel d6,a1 | would have known contents)
579 movel d6,a2
580 movel d6,a3
581 movel d6,a5
582 movel d6,a6
583 | jmp sp@ | jump to kernel entry point
584 rts | enter kernel at address on stack ***
585
586
587 | A do-nothing MMU root pointer (includes the following long as well)
588
589 nullrp: .long 0x7fff0001
590 zero: .long 0
591
592
593 ");
594
595 void
596 usage()
597 {
598 fprintf(stderr, "usage: %s [-abhkptADSV] [-c machine] [-m mem] [-n mode] kernel\n",
599 program_name);
600 exit(1);
601 }
602
603
604 void
605 verbose_usage()
606 {
607 fprintf(stderr, "
608 NAME
609 \t%s - loads NetBSD from amiga dos.
610 SYNOPSIS
611 \t%s [-abhkptDSV] [-c machine] [-m mem] [-n flags] kernel
612 OPTIONS
613 \t-a Boot up to multiuser mode.
614 \t-b Ask for which root device.
615 \t Its possible to have multiple roots and choose between them.
616 \t-c Set machine type. [e.g 3000]
617 \t-h This help message.
618 \t-k Reserve the first 4M of fast mem [Some one else
619 \t is going to have to answer what that it is used for].
620 \t-m Tweak amount of available memory, for finding minimum amount
621 \t of memory required to run. Sets fastmem size to specified
622 \t size in Kbytes.
623 \t-n Enable multiple non-contiguous memory: value = 0 (disabled),
624 \t 1 (two segments), 2 (all avail segments), 3 (same as 2?).
625 \t-p Use highest priority fastmem segement instead of the largest
626 \t segment. The higher priority segment is usually faster
627 \t (i.e. 32 bit memory), but some people have smaller amounts
628 \t of 32 bit memory.
629 \t-t This is a *test* option. It prints out the memory
630 \t list information being passed to the kernel and also
631 \t exits without actually starting NetBSD.
632 \t-S Include kernel symbol table.
633 \t-D Enter debugger
634 \t-A Use AGA display mode, if available.
635 \t-V Version of loadbsd program.
636 HISTORY
637 \tThis version supports Kernel version 720 +\n",
638 program_name, program_name);
639 exit(1);
640 }
641
642
643 void
644 _Vdomessage(doexit, eval, doerrno, fmt, args)
645 int doexit, doerrno, eval;
646 const char *fmt;
647 va_list args;
648 {
649 fprintf(stderr, "%s: ", program_name);
650 if (fmt) {
651 vfprintf(stderr, fmt, args);
652 fprintf(stderr, ": ");
653 }
654 if (doerrno && errno < sys_nerr) {
655 fprintf(stderr, "%s", strerror(errno));
656 if (errno == EINTR || errno == 0) {
657 int sigs;
658 sigpending((sigset_t *)&sigs);
659 printf("%x\n", sigs);
660 }
661 }
662 fprintf(stderr, "\n");
663 if (doexit)
664 exit(eval);
665 }
666
667 void
668 err(int eval, const char *fmt, ...)
669 {
670 va_list ap;
671 va_start(ap, fmt);
672 _Vdomessage(1, eval, 1, fmt, ap);
673 /*NOTREACHED*/
674 }
675
676 void
677 errx(int eval, const char *fmt, ...)
678 {
679 va_list ap;
680 va_start(ap, fmt);
681 _Vdomessage(1, eval, 0, fmt, ap);
682 /*NOTREACHED*/
683 }
684
685 void
686 warn(const char *fmt, ...)
687 {
688 va_list ap;
689 va_start(ap, fmt);
690 _Vdomessage(0, 0, 1, fmt, ap);
691 va_end(ap);
692 }
693
694 void
695 warnx(const char *fmt, ...)
696 {
697 va_list ap;
698 va_start(ap, fmt);
699 _Vdomessage(0, 0, 0, fmt, ap);
700 va_end(ap);
701 }
702