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