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