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