xref: /netbsd/sys/arch/amiga/stand/bootblock/boot/main.c (revision f9f2c924)
1 /*
2  * $NetBSD: main.c,v 1.12 2000/09/24 20:56:04 jdolecek Exp $
3  *
4  *
5  * Copyright (c) 1996,1999 Ignatios Souvatzis
6  * Copyright (c) 1994 Michael L. Hitch
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Michael L. Hitch.
20  * 4. The name of the authors may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/cdefs.h>
37 #include <sys/reboot.h>
38 #include <sys/types.h>
39 
40 #include <sys/exec_aout.h>
41 
42 #include <amiga/cfdev.h>
43 #include <amiga/memlist.h>
44 #include <include/cpu.h>
45 
46 #include <saerrno.h>
47 #include <stand.h>
48 
49 #include "libstubs.h"
50 #include "samachdep.h"
51 
52 #undef __LDPGSZ
53 #define __LDPGSZ 8192
54 #define __PGSZ 8192
55 
56 #define DRACOREVISION	(*(u_int8_t *)0x02000009)
57 #define DRACOMMUMARGIN	0x200000
58 #define DRACOZ2OFFSET	0x3000000
59 #define DRACOZ2MAX	0x1000000
60 
61 #define EXECMIN 36
62 
63 void startit __P((void *, u_long, u_long, void *, u_long, u_long, int, void *,
64 	int, int, u_long, u_long, u_long, int));
65 int get_cpuid __P((u_int32_t *));
66 #ifdef PPCBOOTER
67 u_int16_t kickstart[];
68 size_t kicksize;
69 #else
70 void startit_end __P((void));
71 #endif
72 
73 /*
74  * Kernel startup interface version
75  *	1:      first version of loadbsd
76  *	2:      needs esym location passed in a4
77  *	3:      load kernel image into fastmem rather than chipmem
78  *	MAX:    highest version with backward compatibility.
79  */
80 
81 #define KERNEL_STARTUP_VERSION		3
82 #define KERNEL_STARTUP_VERSION_MAX	9
83 
84 static long get_number(char **);
85 
86 #define VERSION "2.2"
87 
88 char default_command[] = "netbsd -ASn2";
89 
90 int
91 pain(aio)
92 	void *aio;
93 {
94 	long int io = 0;
95 	char linebuf[128];
96 	char	*kernel_name = default_command;
97 	char	*path = default_command;
98 	int	boothowto = RB_AUTOBOOT;
99 	u_int32_t cpuid = 0;
100 	int	amiga_flags = 0;
101 	u_int32_t I_flag = 0;
102 	int	k_flag = 0;
103 	int	p_flag = 0;
104 	int	m_value = 0;
105 	int	S_flag = 0;
106 	int	t_flag = 0;
107 	long stringsz;
108 
109 	u_int32_t fmem = 0x0;
110 	int	fmemsz = 0x0;
111 	int	cmemsz = 0x0;
112 	int	eclock = SysBase->EClockFreq;
113 	/* int	skip_chipmem = 0; */
114 
115 	void (*start_it)(void *, u_long, u_long, void *, u_long, u_long, int,
116 	    void *, int, int, u_long, u_long, u_long, int);
117 
118 	caddr_t kp;
119 	u_int16_t *kvers;
120 	struct exec ehs;
121 	int	textsz, ksize;
122 	void	*esym = 0;
123 	int32_t *nkcd;
124 	struct cfdev *cd, *kcd;
125 	struct boot_memseg *kmemseg;
126 	struct boot_memseg *memseg;
127 	struct MemHead *mh;
128 	u_int32_t from, size, vfrom, vsize;
129 	int contflag, mapped1to1;
130 
131 	int ncd, nseg;
132 	char c;
133 
134 	extern u_int16_t timelimit;
135 
136 	extern u_int32_t aio_base;
137 
138 	xdinit(aio);
139 
140 	if (consinit())
141 		return(1);
142 
143 	/*
144 	 * we need V36 for: EClock, RDB Bootblocks, CacheClearU
145 	 */
146 
147 	if (SysBase->LibNode.Version < EXECMIN) {
148 		printf("Exec V%ld, need V%ld\n",
149 		    (long)SysBase->LibNode.Version, (long)EXECMIN);
150 		goto out;
151 	}
152 
153 #ifdef PPCBOOTER
154 	printf("\2337mNetBSD/AmigaPPC bootblock " VERSION "\2330m\n%s :- ",
155 		kernel_name);
156 #else
157 	printf("\2337mNetBSD/Amiga bootblock " VERSION "\2330m\n%s :- ",
158 		kernel_name);
159 #endif
160 
161 	timelimit = 3;
162 	gets(linebuf);
163 
164 	if (*linebuf == 'q')
165 		return 1;
166 
167 	if (*linebuf)
168 		path = linebuf;
169 
170 	/*
171 	 * parse boot command for path name and process any options
172 	 */
173 	while ((c = *path)) {
174 		while (c == ' ')
175 			c = *++path;
176 		if (c == '-') {
177 			while ((c = *++path) && c != ' ') {
178 				switch (c) {
179 				case 'a':	/* multi-user state */
180 					boothowto &= ~RB_SINGLE;
181 					break;
182 				case 'b':	/* ask for root device */
183 					boothowto |= RB_ASKNAME;
184 					break;
185 				case 'c':	/* force machine model */
186 					cpuid = get_number(&path) << 16;
187 					break;
188 				case 'k':	/* Reserve first 4M fastmem */
189 					k_flag++;
190 					break;
191 				case 'm':	/* Force fastmem size */
192 					m_value = get_number(&path) * 1024;
193 					break;
194 				case 'n':	/* non-contiguous memory */
195 					amiga_flags |=
196 					    (get_number(&path) & 3) << 1;
197 					break;
198 				case 'p':	/* Select fastmem by priority */
199 					p_flag++;
200 					break;
201 				case 'q':
202 					boothowto |= AB_QUIET;
203 					break;
204 				case 's':	/* single-user state */
205 					boothowto |= RB_SINGLE;
206 					break;
207 				case 't':	/* test flag */
208 					t_flag = 1;
209 					break;
210 				case 'v':
211 					boothowto |= AB_VERBOSE;
212 					break;
213 				case 'A':	/* enable AGA modes */
214 					amiga_flags |= 1;
215 					break;
216 				case 'D':	/* enter Debugger */
217 					boothowto |= RB_KDB;
218 					break;
219 				case 'I':	/* inhibit sync negotiation */
220 					I_flag = get_number(&path);
221 					break;
222 				case 'K':	/* remove 1st 4MB fastmem */
223 					break;
224 				case 'S':	/* include debug symbols */
225 					S_flag = 1;
226 					break;
227 				}
228 			}
229 		} else {
230 			kernel_name = path;
231 			while ((c = *++path) && c != ' ')
232 				;
233 			if (c)
234 				*path++ = 0;
235 		}
236 	}
237 	while ((c = *kernel_name) && c == ' ')
238 		++kernel_name;
239 	path = kernel_name;
240 	while ((c = *path) && c != ' ')
241 		++path;
242 	if (c)
243 		*path = 0;
244 
245 	if (get_cpuid(&cpuid))
246 		goto out;
247 
248 	ExpansionBase = OpenLibrary("expansion.library", 0);
249 	if (!ExpansionBase) {
250 		printf("can't open %s\n", "expansion.library");
251 		return 1;
252 	}
253 
254 	for (ncd=0, cd=0; (cd = FindConfigDev(cd, -1, -1)); ncd++)
255 		/* nothing */;
256 
257 	/* find memory list */
258 
259 	memseg = (struct boot_memseg *)alloc(16*sizeof(struct boot_memseg));
260 
261 	/* Forbid(); */
262 
263 	nseg = 0;
264 	mh = SysBase->MemLst;
265 	vfrom = mh->Lower & -__PGSZ;
266 	vsize = (mh->Upper & -__PGSZ) - vfrom;
267 	contflag = mapped1to1 = 0;
268 
269 	do {
270 		size = vsize;
271 
272 		if (SysBase->LibNode.Version > 36) {
273 			from = CachePreDMA(vfrom, &size, contflag);
274 			contflag = DMAF_Continue;
275 			mapped1to1 = (from == vfrom);
276 			vsize -= size;
277 			vfrom += size;
278 		} else {
279 			from = vfrom;
280 			mapped1to1 = 1;
281 			vsize = 0;
282 		}
283 
284 #ifdef DEBUG_MEMORY_LIST
285 		printf("%lx %lx %lx %ld/%lx %lx\n",
286 			(long)from, (long)size,
287 			(long)mh->Attribs, (long)mh->Pri,
288 			(long)vfrom, (long)vsize);
289 #endif
290 		/* Insert The Evergrowing Kludge List Here: */
291 
292 		/* a) dont load kernel over DraCo MMU table */
293 
294 		if (((cpuid >> 24) == 0x7D) &&
295 		    ((from & -DRACOMMUMARGIN) == 0x40000000) &&
296 		    (size >= DRACOMMUMARGIN)) {
297 
298 			memseg[nseg].ms_start = from & -DRACOMMUMARGIN;
299 			memseg[nseg].ms_size = DRACOMMUMARGIN;
300 			memseg[nseg].ms_attrib = mh->Attribs;
301 			memseg[nseg].ms_pri = mh->Pri;
302 
303 			size -= DRACOMMUMARGIN - (from & (DRACOMMUMARGIN - 1));
304 			from += DRACOMMUMARGIN - (from & (DRACOMMUMARGIN - 1));
305 			++nseg;
306 		}
307 
308 		if ((mh->Attribs & (MEMF_CHIP|MEMF_FAST)) == MEMF_CHIP) {
309 			size += from;
310 			cmemsz = size;;
311 			from = 0;
312 		} else if ((fmemsz < size) && mapped1to1) {
313 			fmem = from;
314 			fmemsz = size;
315 		}
316 
317 		memseg[nseg].ms_start = from;
318 		memseg[nseg].ms_size = size;
319 		memseg[nseg].ms_attrib = mh->Attribs;
320 		memseg[nseg].ms_pri = mh->Pri;
321 
322 		if (vsize == 0) {
323 			mh = mh->next;
324 			contflag = 0;
325 			if (mh->next) {
326 				vfrom = mh->Lower & -__PGSZ;
327 				vsize = (mh->Upper & -__PGSZ) - vfrom;
328 			}
329 		}
330 	} while ((++nseg <= 16) && vsize);
331 
332 	/* Permit(); */
333 
334 	if (k_flag) {
335 		fmem += 4*1024*1024;
336 		fmemsz -= 4*1024*1024;
337 	}
338 	if (m_value && m_value < fmemsz)
339 		fmemsz = m_value;
340 
341 	printf("Loading %s: ", kernel_name);
342 	io = open(kernel_name, 0);
343 	if (io < 0)
344 		goto err;
345 
346 	if (read(io, &ehs, sizeof(ehs)) != sizeof(ehs)) {
347 		errno = ENOEXEC;
348 		goto err;
349 	}
350 
351 #ifdef PPCBOOTER
352 	/* XXX to be done */
353 #else
354 	if ((N_GETMAGIC(ehs) != NMAGIC) || (N_GETMID(ehs) != MID_M68K)) {
355 		errno = ENOEXEC;
356 		goto err;
357 	}
358 #endif
359 
360 	textsz = (ehs.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
361 	esym = 0;
362 
363 	ksize = textsz + ehs.a_data + ehs.a_bss
364 	    + sizeof(*nkcd) + ncd*sizeof(*cd)
365 	    + sizeof(*nkcd) + nseg * sizeof(struct boot_memseg);
366 
367 	if (S_flag && ehs.a_syms) {
368 		if (lseek(io, ehs.a_text+ ehs.a_data+ ehs.a_syms, SEEK_CUR)
369 		    <= 0
370 		    || read(io, &stringsz, 4) != 4
371 		    || lseek(io, sizeof(ehs), SEEK_SET) < 0)
372 			goto err;
373 		ksize += ehs.a_syms + 4 + ((stringsz + 3) & ~3);
374 	}
375 #ifdef PPCBOOTER
376 	kp = alloc(ksize);
377 #else
378 	kp = alloc(ksize + 256 + ((u_char *)startit_end - (u_char *)startit));
379 #endif
380 	if (kp == 0) {
381 		errno = ENOMEM;
382 		goto err;
383 	}
384 
385 	printf("%ld", ehs.a_text);
386 	if (ehs.a_text && (read(io, kp, ehs.a_text) != ehs.a_text))
387 		goto err;
388 
389 	printf("+%ld", ehs.a_data);
390 	if (ehs.a_data && (read(io, kp + textsz, ehs.a_data) != ehs.a_data))
391 		goto err;
392 
393 	printf("+%ld", ehs.a_bss);
394 
395         nkcd = (int *)(kp + textsz + ehs.a_data + ehs.a_bss);
396 #ifndef PPCBOOTER
397 	kvers = (u_short *)(kp + ehs.a_entry - 2);
398 
399 	if (*kvers > KERNEL_STARTUP_VERSION_MAX && *kvers != 0x4e73) {
400                 printf("\nnewer bootblock required: %ld\n", (long)*kvers);
401 		goto freeall;
402 	}
403 	if (*kvers < KERNEL_STARTUP_VERSION || *kvers == 0x4e73) {
404                 printf("\nkernel too old for bootblock\n");
405 		goto freeall;
406 	}
407 #if 0
408         if (*kvers > KERNEL_STARTUP_VERSION)
409 		printf("\nKernel V%ld newer than bootblock V%ld\n",
410 		    (long)*kvers, (long)KERNEL_STARTUP_VERSION);
411 #endif
412         if (*kvers != 0x4e73 && *kvers > 1 && S_flag && ehs.a_syms) {
413                 *nkcd++ = ehs.a_syms;
414 		printf("+[%ld", ehs.a_syms);
415                 if (read(io, (char *)nkcd, ehs.a_syms) != ehs.a_syms)
416 			goto err;
417                 nkcd = (int *)((char *)nkcd + ehs.a_syms);
418 		printf("+%ld]", stringsz);
419                 if (read(io, (char *)nkcd, stringsz) != stringsz)
420 			goto err;
421                 nkcd = (int*)((char *)nkcd + ((stringsz + 3) & ~3));
422                 esym = (char *)(textsz + ehs.a_data + ehs.a_bss
423                     + ehs.a_syms + 4 + ((stringsz + 3) & ~3));
424         }
425 #endif
426 	putchar('\n');
427 
428 	*nkcd = ncd;
429 	kcd = (struct cfdev *)(nkcd + 1);
430 
431 	while ((cd = FindConfigDev(cd, -1, -1))) {
432 		*kcd = *cd;
433 #ifndef PPCBOOTER
434 		if (((cpuid >> 24) == 0x7D) &&
435 		    ((u_long)kcd->addr < 0x1000000)) {
436 			kcd->addr += 0x3000000;
437 		}
438 #endif
439 		++kcd;
440 	}
441 
442 	nkcd = (u_int32_t *)kcd;
443 	*nkcd = nseg;
444 
445 	kmemseg = (struct boot_memseg *)(nkcd + 1);
446 
447 	while (nseg-- > 0)
448 		*kmemseg++ = *memseg++;
449 
450 #ifdef PPCBOOTER
451 	/*
452 	 * we use the ppc starter...
453 	 */
454 	start_it = startit;
455 #else
456 	/*
457 	 * Copy startup code to end of kernel image and set start_it.
458 	 */
459 	memcpy(kp + ksize + 256, (char *)startit,
460 	    (char *)startit_end - (char *)startit);
461 	CacheClearU();
462 	(caddr_t)start_it = kp + ksize + 256;
463 #endif
464 	printf("*** Loading from %08lx to Fastmem %08lx ***\n",
465 	    (u_long)kp, (u_long)fmem);
466 	/* sleep(2); */
467 
468 #if 0
469 	printf("would start(kp=0x%lx, ksize=%ld, entry=0x%lx,\n"
470 		"fmem=0x%lx, fmemsz=%ld, cmemsz=%ld\n"
471 		"boothow=0x%lx, esym=0x%lx, cpuid=0x%lx, eclock=%ld\n"
472 		"amigaflags=0x%lx, I_flags=0x%lx, ok?\n",
473 	    (u_long)kp, (u_long)ksize, ehs.a_entry,
474 	    (u_long)fmem, (u_long)fmemsz, (u_long)cmemsz,
475 	    (u_long)boothowto, (u_long)esym, (u_long)cpuid, (u_long)eclock,
476 	    (u_long)amiga_flags, (u_long)I_flag);
477 #endif
478 #ifdef DEBUG_MEMORY_LIST
479 	timelimit = 0;
480 #else
481 	timelimit = 2;
482 #endif
483 	(void)getchar();
484 
485 #ifdef PPCBOOTER
486 	startit
487 #else
488 	start_it
489 #endif
490 		(kp, ksize, ehs.a_entry, (void *)fmem, fmemsz, cmemsz,
491 	    boothowto, esym, cpuid, eclock, amiga_flags, I_flag,
492 	    aio_base >> 9, 1);
493 	/*NOTREACHED*/
494 
495 freeall:
496 	free(kp, ksize);
497 err:
498 	printf("\nError %ld\n", (long)errno);
499 	close(io);
500 out:
501 	timelimit = 10;
502 	(void)getchar();
503 	return 1;
504 }
505 
506 static
507 long get_number(ptr)
508 char **ptr;
509 {
510 	long value = 0;
511 	int base = 10;
512 	char *p = *ptr;
513 	char c;
514 	char sign = 0;
515 
516 	c = *++p;
517 	while (c == ' ')
518 		c = *++p;
519 	if (c == '-') {
520 		sign = -1;
521 		c = *++p;
522 	}
523 	if (c == '$') {
524 		base = 16;
525 		c = *++p;
526 	} else if (c == '0') {
527 		c = *++p;
528 		if ((c & 0xdf) == 'X') {
529 			base = 16;
530 			c = *++p;
531 		}
532 	}
533 	while (c) {
534 		if (c >= '0' && c <= '9')
535 			c -= '0';
536 		else {
537 			c = (c & 0xdf) - 'A' + 10;
538 			if (base != 16 || c < 10 || c > 15)
539 				break;
540 		}
541 		value = value * base + c;
542 		c = *++p;
543 	}
544 	*ptr = p - 1;
545 #ifdef TEST
546 	fprintf(stderr, "get_number: got %c0x%x",
547 	    sign ? '-' : '+', value);
548 #endif
549 	return (sign ? -value : value);
550 }
551 
552 /*
553  * Try to determine the machine ID by searching the resident module list
554  * for modules only present on specific machines.  (Thanks, Bill!)
555  */
556 
557 int
558 get_cpuid(cpuid)
559 	u_int32_t *cpuid;
560 {
561 	*cpuid |= SysBase->AttnFlags;	/* get FPU and CPU flags */
562 	if (*cpuid & 0xffff0000) {
563 		if ((*cpuid >> 24) == 0x7D)
564 			return 0;
565 
566 		switch (*cpuid >> 16) {
567 		case 500:
568 		case 600:
569 		case 1000:
570 		case 1200:
571 		case 2000:
572 		case 3000:
573 		case 4000:
574 			return 0;
575 		default:
576 			printf("Amiga %ld ???\n",
577 			    (long)(*cpuid >> 16));
578 			return(1);
579 		}
580 	}
581 	if (FindResident("A4000 Bonus") || FindResident("A4000 bonus")
582 	    || FindResident("A1000 Bonus"))
583 		*cpuid |= 4000 << 16;
584 	else if (FindResident("A3000 Bonus") || FindResident("A3000 bonus")
585 	    || (SysBase->LibNode.Version == 36))
586 		*cpuid |= 3000 << 16;
587 	else if (OpenResource("card.resource")) {
588 		/* Test for AGA? */
589 		*cpuid |= 1200 << 16;
590 	} else if (OpenResource("draco.resource")) {
591 		*cpuid |= (32000 | DRACOREVISION) << 16;
592 	}
593 	/*
594 	 * Nothing found, it's probably an A2000 or A500
595 	 */
596 	if ((*cpuid >> 16) == 0)
597 		*cpuid |= 2000 << 16;
598 
599 	return 0;
600 }
601