xref: /netbsd-src/sys/arch/amiga/stand/loadbsd/loadbsd.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
1 /*
2  *	$Id: loadbsd.c,v 1.10 1994/05/12 05:57:33 chopps Exp $
3  */
4 
5 #include <sys/types.h>
6 #include <a.out.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 
10 #include <exec/types.h>
11 #include <exec/execbase.h>
12 #include <exec/memory.h>
13 #include <exec/resident.h>
14 #include <libraries/configregs.h>
15 #include <libraries/expansionbase.h>
16 #include <graphics/gfxbase.h>
17 
18 #include <inline/exec.h>
19 #include <inline/expansion.h>
20 #include <inline/graphics.h>
21 
22 /* Get definitions for boothowto */
23 #include "reboot.h"
24 
25 static char usage[] =
26 "
27 NAME
28 \t%s - loads NetBSD from amiga dos.
29 SYNOPSIS
30 \t%s some-vmunix [-a] [-b] [-k] [-m memory] [-p] [-t] [-V]
31 OPTIONS
32 \t-a  Boot up to multiuser mode.
33 \t-b  Ask for which root device.
34 \t    Its possible to have multiple roots and choose between them.
35 \t-k  Reserve the first 4M of fast mem [Some one else
36 \t    is going to have to answer what that it is used for].
37 \t-m  Tweak amount of available memory, for finding minimum amount
38 \t    of memory required to run. Sets fastmem size to specified
39 \t    size in Kbytes.
40 \t-p  Use highest priority fastmem segement instead of the largest
41 \t    segment. The higher priority segment is usually faster
42 \t    (i.e. 32 bit memory), but some people have smaller amounts
43 \t    of 32 bit memory.
44 \t-t  This is a *test* option.  It prints out the memory
45 \t    list information being passed to the kernel and also
46 \t    exits without actually starting NetBSD.
47 \t-S  Include kernel symbol table.
48 \t-D  Enter debugger
49 \t-V  Version of loadbsd program.
50 HISTORY
51       This version supports Kernel version 720 +
52 ";
53 
54 struct ExpansionBase *ExpansionBase;
55 struct GfxBase *GfxBase;
56 
57 #undef __LDPGSZ
58 #define __LDPGSZ 8192
59 
60 #define MAX_MEM_SEG	16
61 
62 /*
63  * Kernel parameter passing version
64  *	1:	first version of loadbsd
65  *	2:	needs esym location passed in a4
66  */
67 #define KERNEL_PARAMETER_VERSION	2
68 
69 /*
70  *	Version history:
71  *	1.x	Kernel parameter passing version check.
72  *	2.0	Added symbol table end address and symbol table support.
73  *	2.1	03/23/94 - Round up end of fastram segment.
74  *		Check fastram segment size for minimum of 2M.
75  *		Use largest segment of highest priority if -p option.
76  *		Print out fastram size in KB if not a multiple of MB.
77  *	2.2	03/24/94 - Zero out all unused registers.
78  *		Started version history comment.
79  *	2.3	04/26/94 - Added -D option to enter debugger on boot.
80  *	2.4	04/30/94 - Cpuid includes base machine type.
81  *		Also check if CPU is capable of running NetBSD.
82  */
83 
84 struct MEM_LIST {
85 	u_long	num_mem;
86 	struct MEM_SEG {
87 		u_long	mem_start;
88 		u_long	mem_size;
89 		u_short	mem_attrib;
90 		short	mem_prio;
91 	} mem_seg[MAX_MEM_SEG];
92 } mem_list, *kmem_list;
93 
94 int k_opt;
95 int a_opt;
96 int b_opt;
97 int p_opt;
98 int t_opt;
99 int m_opt;
100 int S_opt;
101 int D_opt;
102 
103 u_long cpuid;
104 
105 extern char *optarg;
106 extern int optind;
107 
108 void get_mem_config (void **fastmem_start, u_long *fastmem_size, u_long *chipmem_size);
109 void get_cpuid (void);
110 void Usage (char *program_name);
111 void Version (void);
112 
113 static const char _version[] = "$VER: LoadBSD 2.4 (30.4.94)";
114 
115 int
116 main (int argc, char *argv[])
117 {
118   struct exec e;
119   int fd;
120   int boothowto = RB_SINGLE;
121 
122   if (argc >= 2)
123     {
124       if ((fd = open (argv[1], 0)) >= 0)
125 	{
126 	  if (read (fd, &e, sizeof (e)) == sizeof (e))
127 	    {
128 	      if (e.a_magic == NMAGIC)
129 		{
130 		  u_char *kernel;
131 		  int kernel_size;
132 		  int text_size;
133 		  struct ConfigDev *cd;
134 		  int num_cd;
135 		  void *fastmem_start;
136 		  u_long fastmem_size, chipmem_size;
137 		  int i;
138 		  u_short *kern_vers;
139 		  char *esym;
140 		  int string_size;
141 
142 		  GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0);
143 		  if (! GfxBase)	/* not supposed to fail... */
144 		    abort();
145 		  ExpansionBase= (struct ExpansionBase *) OpenLibrary ("expansion.library", 0);
146 		  if (! ExpansionBase)	/* not supposed to fail... */
147 		    abort();
148 		  optind = 2;
149 		  while ((i = getopt (argc, argv, "kabptVm:SD")) != EOF)
150 		    switch (i) {
151 		    case 'k':
152 		      k_opt = 1;
153 		      break;
154 		    case 'a':
155 		      a_opt = 1;
156 		      break;
157 		    case 'b':
158 		      b_opt = 1;
159 		      break;
160 		    case 'p':
161 		      p_opt = 1;
162 		      break;
163 		    case 't':
164 		      t_opt = 1;
165 		      break;
166 		    case 'm':
167 		      m_opt = atoi (optarg) * 1024;
168 		      break;
169 		    case 'V':
170 		      Version();
171 		      break;
172 		    case 'S':
173 		      S_opt = 1;
174 		      break;
175 		    case 'D':
176 		      D_opt = 1;
177 		      break;
178 		    default:
179 		      Usage(argv[0]);
180 		      fprintf(stderr,"Unrecognized option \n");
181 		      exit(-1);
182 		    }
183 
184 		  for (cd = 0, num_cd = 0; cd = FindConfigDev (cd, -1, -1); num_cd++) ;
185 		  get_mem_config (&fastmem_start, &fastmem_size, &chipmem_size);
186 		  get_cpuid ();
187 
188 		  text_size = (e.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
189 		  esym = NULL;
190 		  kernel_size = text_size + e.a_data + e.a_bss
191 		      + num_cd*sizeof(*cd) + 4
192 		      + mem_list.num_mem*sizeof(struct MEM_SEG) + 4;
193 		  /*
194 		   * get symbol table size & string size
195 		   * (should check kernel version to see if it will handle it)
196 		   */
197 		  if (S_opt && e.a_syms) {
198 		    S_opt = 0;			/* prepare for failure */
199 		    if (lseek(fd, e.a_text + e.a_data + e.a_syms, SEEK_CUR) > 0) {
200 		      if (read (fd, &string_size, 4) == 4) {
201 			if (lseek(fd, sizeof(e), SEEK_SET) < 0) {
202 		      	  printf ("Error repositioning to text\n");
203 		      	  exit(0);		/* Give up! */
204 			}
205 			kernel_size += e.a_syms + 4 + string_size;
206 			S_opt = 1;		/* sucess!  Keep -S option */
207 		      }
208 		    }
209 		  }
210 
211 		  kernel = (u_char *) malloc (kernel_size);
212 
213 		  if (t_opt)
214 		    for (i = 0; i < mem_list.num_mem; ++i) {
215 		      printf ("mem segment %d: start=%08lx size=%08lx attribute=%04lx pri=%d\n",
216 			i + 1, mem_list.mem_seg[i].mem_start,
217 			mem_list.mem_seg[i].mem_size,
218 			mem_list.mem_seg[i].mem_attrib,
219 			mem_list.mem_seg[i].mem_prio);
220 		    }
221 
222 		  if (kernel)
223 		    {
224 		      if (read (fd, kernel, e.a_text) == e.a_text
225 			  && read (fd, kernel + text_size, e.a_data) == e.a_data)
226 			{
227 			  int *knum_cd;
228 			  struct ConfigDev *kcd;
229 			  int mem_ix;
230 
231 			  if (k_opt)
232 			    {
233 			      fastmem_start += 4*1024*1024;
234 			      fastmem_size  -= 4*1024*1024;
235 			    }
236 
237 			  if (m_opt && m_opt <= fastmem_size)
238 			    {
239 			      fastmem_size = m_opt;
240 			    }
241 
242 			  if (a_opt)
243 			    {
244 			      printf("Autobooting...");
245 			      boothowto = RB_AUTOBOOT;
246 			    }
247 
248 			  if (b_opt)
249 			    {
250 			      printf("Askboot...");
251 			      boothowto |= RB_ASKNAME;
252 			    }
253 
254 			  if (D_opt)
255 			    {
256 			      boothowto |= RB_KDB;
257 			    }
258 
259 			  printf ("Using %d%c FASTMEM at 0x%x, %dM CHIPMEM\n",
260 				(fastmem_size & 0xfffff) ? fastmem_size>>10 :
261 				fastmem_size>>20,
262 				(fastmem_size & 0xfffff) ? 'K' : 'M',
263 				  fastmem_start, chipmem_size>>20);
264 			  kern_vers = (u_short *) (kernel + e.a_entry - 2);
265 			  if (*kern_vers > KERNEL_PARAMETER_VERSION &&
266 			      *kern_vers != 0x4e73)
267 			    {
268 			      printf ("This kernel requires a newer version of loadbsd: %d\n", *kern_vers);
269 			      exit (0);
270 			    }
271 			  if ((cpuid & AFB_68020) == 0)
272 			    {
273 			      printf ("Hmmm... You don't seem to have a CPU capable\n");
274 			      printf ("        of running NetBSD.  You need a 68020\n");
275 			      printf ("        or better\n");
276 			      exit (0);
277 			    }
278 			  /* give them a chance to read the information... */
279 			  sleep(2);
280 
281 			  bzero (kernel + text_size + e.a_data, e.a_bss);
282 			  /*
283 			   * If symbols wanted (and kernel can handle them),
284 			   * load symbol table & strings and set esym to end.
285 			   */
286 			  knum_cd = (int *) (kernel + text_size + e.a_data + e.a_bss);
287 			  if (*kern_vers != 0x4e73 && *kern_vers > 1 && S_opt && e.a_syms) {
288 			    *knum_cd++ = e.a_syms;
289 			    read(fd, (char *)knum_cd, e.a_syms);
290 			    knum_cd = (int *)((char *)knum_cd + e.a_syms);
291 			    read(fd, (char *)knum_cd, string_size);
292 			    knum_cd = (int*)((char *)knum_cd + string_size);
293 			    esym = (char *) (text_size + e.a_data + e.a_bss
294 			      + e.a_syms + 4 + string_size);
295 			  }
296 			  *knum_cd = num_cd;
297 			  for (kcd = (struct ConfigDev *) (knum_cd+1);
298 			       cd = FindConfigDev (cd, -1, -1);
299 			       *kcd++ = *cd)
300 				;
301 			  kmem_list = (struct MEM_LIST *)kcd;
302 			  kmem_list->num_mem = mem_list.num_mem;
303 			  for (mem_ix = 0; mem_ix < mem_list.num_mem; mem_ix++)
304 			  	kmem_list->mem_seg[mem_ix] = mem_list.mem_seg[mem_ix];
305 			  if (t_opt)		/* if test option */
306 			    exit (0);		/*   don't start kernel */
307 			  /* AGA startup - may need more */
308 			  LoadView (NULL);
309 			  startit (kernel, kernel_size,
310 				   e.a_entry, fastmem_start,
311 				   fastmem_size, chipmem_size,
312 				   boothowto, esym, cpuid );
313 			}
314 		      else
315 			fprintf (stderr, "Executable corrupt!\n");
316 		    }
317 		  else
318 		    fprintf (stderr, "Out of memory! (%d)\n", text_size + e.a_data + e.a_bss
319 				   + num_cd*sizeof(*cd) + 4
320 				   + mem_list.num_mem*sizeof(struct MEM_SEG) + 4);
321 		}
322 	      else
323 		fprintf (stderr, "Unsupported executable: %o\n", e.a_magic);
324 	    }
325 	  else
326 	    fprintf (stderr, "Can't read header of %s\n", argv[1]);
327 
328 	  close (fd);
329 	}
330       else
331 	perror ("open");
332     }
333   else
334     Usage(argv[0]);
335   Version();
336 }/* main() */
337 
338 void
339 get_mem_config (void **fastmem_start, u_long *fastmem_size, u_long *chipmem_size)
340 {
341   extern struct ExecBase *SysBase;
342   struct MemHeader *mh, *nmh;
343   int num_mem = 0;
344   u_int seg_size;
345   u_int seg_start;
346   u_int seg_end;
347   char mem_pri = -128;
348 
349   *fastmem_size = 0;
350   *chipmem_size = 0;
351 
352   /* walk thru the exec memory list */
353   Forbid ();
354   for (mh  = (struct MemHeader *) SysBase->MemList.lh_Head;
355        nmh = (struct MemHeader *) mh->mh_Node.ln_Succ;
356        mh  = nmh, num_mem++)
357     {
358       mem_list.mem_seg[num_mem].mem_attrib = mh->mh_Attributes;
359       mem_list.mem_seg[num_mem].mem_prio = mh->mh_Node.ln_Pri;
360       seg_start = (u_int)mh->mh_Lower;
361       seg_end = (u_int)mh->mh_Upper;
362       seg_size = seg_end - seg_start;
363       mem_list.mem_seg[num_mem].mem_size = seg_size;
364       mem_list.mem_seg[num_mem].mem_start = seg_start;
365 
366       if (mh->mh_Attributes & MEMF_CHIP)
367 	{
368 	  /* there should hardly be more than one entry for chip mem, but
369 	     handle it the same nevertheless */
370 	  /* chipmem always starts at 0, so include vector area */
371 	  mem_list.mem_seg[num_mem].mem_start = seg_start = 0;
372 	  /* round to multiple of 512K */
373 	  seg_size = (seg_size + 512*1024 - 1) & -(512*1024);
374 	  mem_list.mem_seg[num_mem].mem_size = seg_size;
375 	  if (seg_size > *chipmem_size)
376 	    {
377 	      *chipmem_size = seg_size;
378 	    }
379 	}
380       else
381 	{
382 	  /* some heuristics.. */
383 	  seg_start &= -__LDPGSZ;
384 	  seg_end = (seg_end + __LDPGSZ - 1) & -__LDPGSZ;
385 	  /* get the mem back stolen by incore kickstart on A3000 with
386 	     V36 bootrom. */
387 	  if (seg_end == 0x07f80000)
388 	    seg_end = 0x08000000;
389 
390 	  /* or by zkick on a A2000.  */
391 	  if (seg_start == 0x280000
392 	    && strcmp (mh->mh_Node.ln_Name, "zkick memory") == 0)
393 	      seg_start = 0x200000;
394 
395 	  seg_size = seg_end - seg_start;
396 	  mem_list.mem_seg[num_mem].mem_start = seg_start;
397 	  mem_list.mem_seg[num_mem].mem_size = seg_size;
398 	  /*
399 	   *  If this segment is smaller than 2M,
400 	   *  don't use it to load the kernel
401 	   */
402 	  if (seg_size < 2 * 1024 * 1024)
403 	    continue;
404 /* if p_opt is set, select memory by priority instead of size */
405 	  if ((!p_opt && seg_size > *fastmem_size) ||
406 	    (p_opt && mem_pri <= mh->mh_Node.ln_Pri && seg_size > *fastmem_size))
407 	    {
408 	      *fastmem_size = seg_size;
409 	      *fastmem_start = (void *)seg_start;
410 	      mem_pri = mh->mh_Node.ln_Pri;
411 	    }
412 	}
413     }
414   mem_list.num_mem = num_mem;
415   Permit();
416 }
417 
418 /*
419  * Try to determine the machine ID by searching the resident module list
420  * for modules only present on specific machines.  (Thanks, Bill!)
421  */
422 
423 void
424 get_cpuid ()
425 {
426 	extern struct ExecBase *SysBase;
427 	u_long *rl;
428 	struct Resident *rm;
429 
430 	cpuid = SysBase->AttnFlags;	/* get FPU and CPU flags */
431 	rl = (u_long *) SysBase->ResModules;
432 	if (rl == NULL)
433 		return;
434 
435 	while (*rl) {
436 		rm = (struct Resident *) *rl;
437 		if (strcmp (rm->rt_Name, "A4000 Bonus") == 0 ||
438 		    strcmp (rm->rt_Name, "A1000 Bonus") == 0) {
439 			cpuid |= 4000 << 16;
440 			break;
441 		}
442 		if (strcmp (rm->rt_Name, "A3000 Bonus") == 0) {
443 			cpuid |= 3000 << 16;
444 			break;
445 		}
446 		if (strcmp (rm->rt_Name, "card.resource") == 0) {
447 			cpuid |= 1200 << 16;	/* or A600 :-) */
448 			break;
449 		}
450 		++rl;
451 	}
452 	if (*rl == 0)		/* Nothing found, it's probably an A2000 or A500 */
453 		cpuid |= 2000 << 16;
454 }
455 
456 
457 asm ("
458 	.set	ABSEXECBASE,4
459 
460 	.text
461 	.globl	_startit
462 
463 _startit:
464 	movel	sp,a3
465 	movel	4:w,a6
466 	lea	pc@(start_super-.+2),a5
467 	jmp	a6@(-0x1e)		| supervisor-call
468 
469 start_super:
470 	movew	#0x2700,sr
471 
472 	| the BSD kernel wants values into the following registers:
473 	| a0:  fastmem-start
474 	| d0:  fastmem-size
475 	| d1:  chipmem-size
476 	| d5:  AttnFlags (cpuid)
477 	| d7:  boothowto
478 	| a4:  esym location
479 	| All other registers zeroed for possible future requirements.
480 
481 	movel	a3@(4),a1		| loaded kernel
482 	movel	a3@(8),d2		| length of loaded kernel
483 	movel	a3@(12),sp@-		| entry point [save on stack for rts]
484 	movel	a3@(16),a0		| fastmem-start
485 	movel	a3@(20),d0		| fastmem-size
486 	movel	a3@(24),d1		| chipmem-size
487 	movel	a3@(28),d7		| boothowto
488 	movel	a3@(32),a4		| esym
489 	movel	a3@(36),d5		| cpuid
490 	subl	a5,a5			| target, load to 0
491 
492 	btst	#3,(ABSEXECBASE)@(0x129) | AFB_68040,SysBase->AttnFlags
493 	beq	not040
494 
495 | Turn off 68040 MMU
496 
497 	.word 0x4e7b,0xd003		| movec a5,tc
498 	.word 0x4e7b,0xd806		| movec a5,urp
499 	.word 0x4e7b,0xd807		| movec a5,srp
500 	.word 0x4e7b,0xd004		| movec a5,itt0
501 	.word 0x4e7b,0xd005		| movec a5,itt1
502 	.word 0x4e7b,0xd006		| movec a5,dtt0
503 	.word 0x4e7b,0xd007		| movec a5,dtt1
504 	bra	nott
505 
506 not040:
507 	lea	pc@(zero-.+2),a3
508 	pmove	a3@,tc			| Turn off MMU
509 	lea	pc@(nullrp-.+2),a3
510 	pmove	a3@,crp			| Turn off MMU some more
511 	pmove	a3@,srp			| Really, really, turn off MMU
512 
513 | Turn off 68030 TT registers
514 
515 	btst	#2,(ABSEXECBASE)@(0x129) | AFB_68030,SysBase->AttnFlags
516 	beq	nott			| Skip TT registers if not 68030
517 	lea	pc@(zero-.+2),a3
518 	.word 0xf013,0x0800		| pmove a3@,tt0 (gas only knows about 68851 ops..)
519 	.word 0xf013,0x0c00		| pmove a3@,tt1 (gas only knows about 68851 ops..)
520 
521 nott:
522 
523 	movew	#(1<<9),0xdff096	| disable DMA
524 
525 L0:
526 	moveb	a1@+,a5@+
527 	subl	#1,d2
528 	bcc	L0
529 
530 
531 	moveq	#0,d2			| zero out unused registers
532 	moveq	#0,d3			| (might make future compatibility
533 	moveq	#0,d4			|  a little easier, since all registers
534 	moveq	#0,d6			|  would have known contents)
535 	movel	d6,a1
536 	movel	d6,a2
537 	movel	d6,a3
538 	movel	d6,a5
539 	movel	d6,a6
540 	rts				| return to kernel entry point
541 
542 
543 | A do-nothing MMU root pointer (includes the following long as well)
544 
545 nullrp:	.long	0x7fff0001
546 zero:	.long	0
547 
548 
549 ");
550 
551 void Usage(char *program_name)
552 {
553    fprintf(stderr,usage,program_name,program_name);
554 }
555 
556 void Version()
557 {
558   fprintf(stderr,"%s\n",_version + 6);
559 }
560