NetBSD/sys/arch/amiga/stand/loadbsd/loadbsd.c

701 lines
17 KiB
C

/* $NetBSD: loadbsd.c,v 1.15 1994/10/26 02:07:01 cgd Exp $ */
/*
* Copyright (c) 1994 Michael L. Hitch
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Michael L. Hitch.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <a.out.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <signal.h>
#ifdef __NetBSD__
#include <err.h>
#endif
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <graphics/gfxbase.h>
#include <libraries/configregs.h>
#include <libraries/configvars.h>
#include <libraries/expansion.h>
#include <libraries/expansionbase.h>
#include <inline/exec.h>
#include <inline/expansion.h>
#include <inline/graphics.h>
/* Get definitions for boothowto */
#include "reboot.h"
#undef __LDPGSZ
#define __LDPGSZ 8192
#ifndef __NetBSD__
#ifndef __P
#ifdef __STDC__
#define __P(x) x
#else
#define __P(x)
#endif
#endif
void err __P((int, const char *, ...));
void errx __P((int, const char *, ...));
void warn __P((const char *, ...));
void warnx __P((const char *, ...));
#endif
/*
* Version history:
* 1.x Kernel parameter passing version check.
* 2.0 Added symbol table end address and symbol table support.
* 2.1 03/23/94 - Round up end of fastram segment.
* Check fastram segment size for minimum of 2M.
* Use largest segment of highest priority if -p option.
* Print out fastram size in KB if not a multiple of MB.
* 2.2 03/24/94 - Zero out all unused registers.
* Started version history comment.
* 2.3 04/26/94 - Added -D option to enter debugger on boot.
* 2.4 04/30/94 - Cpuid includes base machine type.
* Also check if CPU is capable of running NetBSD.
* 2.5 05/17/94 - Add check for "A3000 bonus".
* 2.6 06/05/94 - Added -c option to override machine type.
* 2.7 06/15/94 - Pass E clock frequency.
* 2.8 06/22/94 - Fix supervisor stack usage.
* 2.9 06/26/94 - Use PAL flag for E clock freq on pre 2.0 WB
* Added AGA enable parameter
*/
static const char _version[] = "$VER: LoadBSD 2.9 (26.6.94)";
/*
* Kernel parameter passing version
* 1: first version of loadbsd
* 2: needs esym location passed in a4
*/
#define KERNEL_PARAMETER_VERSION 2
#define MAXMEMSEG 16
struct boot_memlist {
u_int m_nseg; /* num_mem; */
struct boot_memseg {
u_int ms_start;
u_int ms_size;
u_short ms_attrib;
short ms_pri;
} m_seg[MAXMEMSEG];
};
struct boot_memlist memlist;
struct boot_memlist *kmemlist;
void get_mem_config __P((void **, u_long *, u_long *));
void get_cpuid __P((void));
void get_eclock __P((void));
void get_AGA __P((void));
void usage __P((void));
void verbose_usage __P((void));
void Version __P((void));
extern struct ExecBase *SysBase;
extern char *optarg;
extern int optind;
int k_flag;
int p_flag;
int t_flag;
int reqmemsz;
int S_flag;
u_long cpuid;
long eclock_freq;
long AGA_mode;
char *program_name;
char *kname;
struct ExpansionBase *ExpansionBase;
struct GfxBase *GfxBase;
int
main(argc, argv)
int argc;
char **argv;
{
struct exec e;
struct ConfigDev *cd, *kcd;
u_long fmemsz, cmemsz;
int fd, boothowto, ksize, textsz, stringsz, ncd, i, mem_ix, ch;
u_short *kvers;
int *nkcd;
u_char *kp;
void *fmem;
char *esym;
program_name = argv[0];
boothowto = RB_SINGLE;
if (argc < 2)
usage();
if ((GfxBase = (void *)OpenLibrary(GRAPHICSNAME, 0)) == NULL)
err(20, "can't open graphics library");
if ((ExpansionBase=(void *)OpenLibrary(EXPANSIONNAME, 0)) == NULL)
err(20, "can't open expansion library");
while ((ch = getopt(argc, argv, "aAbc:Dhkm:ptSV")) != EOF) {
switch (ch) {
case 'k':
k_flag = 1;
break;
case 'a':
boothowto &= ~(RB_SINGLE);
boothowto |= RB_AUTOBOOT;
break;
case 'b':
boothowto |= RB_ASKNAME;
break;
case 'p':
p_flag = 1;
break;
case 't':
t_flag = 1;
break;
case 'm':
reqmemsz = atoi(optarg) * 1024;
break;
case 'V':
fprintf(stderr,"%s\n",_version + 6);
break;
case 'S':
S_flag = 1;
break;
case 'D':
boothowto |= RB_KDB;
break;
case 'c':
cpuid = atoi(optarg) << 16;
break;
case 'A':
AGA_mode = 1;
break;
case 'h':
verbose_usage();
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 1)
usage();
kname = argv[0];
if ((fd = open(kname, 0)) < 0)
err(20, "open");
if (read(fd, &e, sizeof(e)) != sizeof(e))
err(20, "reading exec");
if (e.a_magic != NMAGIC)
err(20, "unknown binary");
for (cd = 0, ncd = 0; cd = FindConfigDev(cd, -1, -1); ncd++)
;
get_mem_config(&fmem, &fmemsz, &cmemsz);
get_cpuid();
get_eclock();
get_AGA();
textsz = (e.a_text + __LDPGSZ - 1) & (-__LDPGSZ);
esym = NULL;
ksize = textsz + e.a_data + e.a_bss + ncd * sizeof(*cd)
+ 4 + memlist.m_nseg * sizeof(struct boot_memseg) + 4;
/*
* get symbol table size & string size
* (should check kernel version to see if it will handle it)
*/
if (S_flag && e.a_syms) {
if (lseek(fd, e.a_text + e.a_data + e.a_syms, SEEK_CUR) <= 0
|| read(fd, &stringsz, 4) != 4
|| lseek(fd, sizeof(e), SEEK_SET) < 0)
err(20, "lseek for symbols");
ksize += e.a_syms + 4 + stringsz;
}
kp = (u_char *)malloc(ksize);
if (t_flag) {
for (i = 0; i < memlist.m_nseg; ++i) {
printf("mem segment %d: start=%08lx size=%08lx"
" attribute=%04lx pri=%d\n",
i + 1, memlist.m_seg[i].ms_start,
memlist.m_seg[i].ms_size,
memlist.m_seg[i].ms_attrib,
memlist.m_seg[i].ms_pri);
}
}
if (kp == NULL)
err(20, "failed malloc %d\n", ksize);
if (read(fd, kp, e.a_text) != e.a_text
|| read(fd, kp + textsz, e.a_data) != e.a_data)
err(20, "unable to read kernel image\n");
if (k_flag) {
fmem += 4 * 1024 * 1024;
fmemsz -= 4 * 1024 * 1024;
}
if (reqmemsz && reqmemsz <= fmemsz)
fmemsz = reqmemsz;
if (boothowto & RB_AUTOBOOT)
printf("Autobooting...");
if (boothowto & RB_ASKNAME)
printf("Askboot...");
printf("Using %d%c FASTMEM at 0x%x, %dM CHIPMEM\n",
(fmemsz & 0xfffff) ? fmemsz >> 10 : fmemsz >> 20,
(fmemsz & 0xfffff) ? 'K' : 'M', fmem, cmemsz >> 20);
kvers = (u_short *)(kp + e.a_entry - 2);
if (*kvers > KERNEL_PARAMETER_VERSION && *kvers != 0x4e73)
err(20, "newer loadbsd required: %d\n", *kvers);
if ((cpuid & AFB_68020) == 0)
err(20, "cpu not supported");
/*
* give them a chance to read the information...
*/
sleep(2);
bzero(kp + textsz + e.a_data, e.a_bss);
/*
* If symbols wanted (and kernel can handle them),
* load symbol table & strings and set esym to end.
*/
nkcd = (int *)(kp + textsz + e.a_data + e.a_bss);
if (*kvers != 0x4e73 && *kvers > 1 && S_flag && e.a_syms) {
*nkcd++ = e.a_syms;
read(fd, (char *)nkcd, e.a_syms);
nkcd = (int *)((char *)nkcd + e.a_syms);
read(fd, (char *)nkcd, stringsz);
nkcd = (int*)((char *)nkcd + stringsz);
esym = (char *)(textsz + e.a_data + e.a_bss
+ e.a_syms + 4 + stringsz);
}
*nkcd = ncd;
kcd = (struct ConfigDev *)(nkcd + 1);
while(cd = FindConfigDev(cd, -1, -1))
*kcd++ = *cd;
kmemlist = (struct boot_memlist *)kcd;
kmemlist->m_nseg = memlist.m_nseg;
for (mem_ix = 0; mem_ix < memlist.m_nseg; mem_ix++)
kmemlist->m_seg[mem_ix] = memlist.m_seg[mem_ix];
/*
* if test option set, done
*/
if (t_flag)
exit(0);
/*
* XXX AGA startup - may need more
*/
LoadView(NULL); /* Don't do this if AGA_mode? */
startit(kp, ksize, e.a_entry, fmem, fmemsz, cmemsz, boothowto, esym,
cpuid, eclock_freq, AGA_mode);
/*NOTREACHED*/
}
void
get_mem_config(fmem, fmemsz, cmemsz)
void **fmem;
u_long *fmemsz, *cmemsz;
{
struct MemHeader *mh, *nmh;
u_int segsz, seg, eseg, nmem;
char mempri;
nmem = 0;
mempri = -128;
*fmemsz = 0;
*cmemsz = 0;
/*
* walk thru the exec memory list
*/
Forbid();
for (mh = (void *) SysBase->MemList.lh_Head;
nmh = (void *) mh->mh_Node.ln_Succ; mh = nmh, nmem++) {
memlist.m_seg[nmem].ms_attrib = mh->mh_Attributes;
memlist.m_seg[nmem].ms_pri = mh->mh_Node.ln_Pri;
seg = (u_int)mh->mh_Lower;
eseg = (u_int)mh->mh_Upper;
segsz = eseg - seg;
memlist.m_seg[nmem].ms_size = segsz;
memlist.m_seg[nmem].ms_start = seg;
if (mh->mh_Attributes & MEMF_CHIP) {
/*
* there should hardly be more than one entry for
* chip mem, but handle it the same nevertheless
* cmem always starts at 0, so include vector area
*/
memlist.m_seg[nmem].ms_start = seg = 0;
/*
* round to multiple of 512K
*/
segsz = (segsz + 512 * 1024 - 1) & -(512 * 1024);
memlist.m_seg[nmem].ms_size = segsz;
if (segsz > *cmemsz)
*cmemsz = segsz;
continue;
}
/*
* some heuristics..
*/
seg &= -__LDPGSZ;
eseg = (eseg + __LDPGSZ - 1) & -__LDPGSZ;
/*
* get the mem back stolen by incore kickstart on
* A3000 with V36 bootrom.
*/
if (eseg == 0x07f80000)
eseg = 0x08000000;
/*
* or by zkick on a A2000.
*/
if (seg == 0x280000 &&
strcmp(mh->mh_Node.ln_Name, "zkick memory") == 0)
seg = 0x200000;
segsz = eseg - seg;
memlist.m_seg[nmem].ms_start = seg;
memlist.m_seg[nmem].ms_size = segsz;
/*
* If this segment is smaller than 2M,
* don't use it to load the kernel
*/
if (segsz < 2 * 1024 * 1024)
continue;
/*
* if p_flag is set, select memory by priority
* instead of size
*/
if ((!p_flag && segsz > *fmemsz) || (p_flag &&
mempri <= mh->mh_Node.ln_Pri && segsz > *fmemsz)) {
*fmemsz = segsz;
*fmem = (void *)seg;
mempri = mh->mh_Node.ln_Pri;
}
}
memlist.m_nseg = nmem;
Permit();
}
/*
* Try to determine the machine ID by searching the resident module list
* for modules only present on specific machines. (Thanks, Bill!)
*/
void
get_cpuid()
{
u_long *rl;
struct Resident *rm;
cpuid |= SysBase->AttnFlags; /* get FPU and CPU flags */
if (cpuid & 0xffff0000) {
switch (cpuid >> 16) {
case 500:
case 600:
case 1000:
case 1200:
case 2000:
case 3000:
case 4000:
return;
default:
printf("machine Amiga %d is not recognized\n",
cpuid >> 16);
exit(1);
}
}
rl = (u_long *)SysBase->ResModules;
if (rl == NULL)
return;
while (*rl) {
rm = (struct Resident *) *rl;
if (strcmp(rm->rt_Name, "A4000 Bonus") == 0 ||
strcmp(rm->rt_Name, "A1000 Bonus") == 0) {
cpuid |= 4000 << 16;
break;
}
if (strcmp(rm->rt_Name, "A3000 bonus") == 0 ||
strcmp(rm->rt_Name, "A3000 Bonus") == 0) {
cpuid |= 3000 << 16;
break;
}
if (strcmp(rm->rt_Name, "card.resource") == 0) {
cpuid |= 1200 << 16; /* or A600 :-) */
break;
}
++rl;
}
/*
* Nothing found, it's probably an A2000 or A500
*/
if (*rl == 0)
cpuid |= 2000 << 16;
}
void
get_eclock()
{
/* Fix for 1.3 startups? */
if (SysBase->LibNode.lib_Version > 36)
eclock_freq = SysBase->ex_EClockFrequency;
else
eclock_freq = (GfxBase->DisplayFlags & PAL) ?
709379 : 715909;
}
void
get_AGA()
{
/*
* Determine if an AGA mode is active
*/
}
asm("
.set ABSEXECBASE,4
.text
.globl _startit
_startit:
movel sp,a3
movel 4:w,a6
lea pc@(start_super-.+2),a5
jmp a6@(-0x1e) | supervisor-call
start_super:
movew #0x2700,sr
| the BSD kernel wants values into the following registers:
| a0: fastmem-start
| d0: fastmem-size
| d1: chipmem-size
| d3: AGA mode enable
| d4: E clock frequency
| d5: AttnFlags (cpuid)
| d7: boothowto
| a4: esym location
| All other registers zeroed for possible future requirements.
lea pc@(_startit-.+2),sp | make sure we have a good stack ***
movel a3@(4),a1 | loaded kernel
movel a3@(8),d2 | length of loaded kernel
| movel a3@(12),sp | entry point in stack pointer
movel a3@(12),sp@- | push entry point ***
movel a3@(16),a0 | fastmem-start
movel a3@(20),d0 | fastmem-size
movel a3@(24),d1 | chipmem-size
movel a3@(28),d7 | boothowto
movel a3@(32),a4 | esym
movel a3@(36),d5 | cpuid
movel a3@(40),d4 | E clock frequency
movel a3@(44),d3 | AGA mode enable
subl a5,a5 | target, load to 0
btst #3,(ABSEXECBASE)@(0x129) | AFB_68040,SysBase->AttnFlags
beq not040
| Turn off 68040 MMU
.word 0x4e7b,0xd003 | movec a5,tc
.word 0x4e7b,0xd806 | movec a5,urp
.word 0x4e7b,0xd807 | movec a5,srp
.word 0x4e7b,0xd004 | movec a5,itt0
.word 0x4e7b,0xd005 | movec a5,itt1
.word 0x4e7b,0xd006 | movec a5,dtt0
.word 0x4e7b,0xd007 | movec a5,dtt1
bra nott
not040:
lea pc@(zero-.+2),a3
pmove a3@,tc | Turn off MMU
lea pc@(nullrp-.+2),a3
pmove a3@,crp | Turn off MMU some more
pmove a3@,srp | Really, really, turn off MMU
| Turn off 68030 TT registers
btst #2,(ABSEXECBASE)@(0x129) | AFB_68030,SysBase->AttnFlags
beq nott | Skip TT registers if not 68030
lea pc@(zero-.+2),a3
.word 0xf013,0x0800 | pmove a3@,tt0 (gas only knows about 68851 ops..)
.word 0xf013,0x0c00 | pmove a3@,tt1 (gas only knows about 68851 ops..)
nott:
movew #(1<<9),0xdff096 | disable DMA
L0:
moveb a1@+,a5@+
subl #1,d2
bcc L0
moveq #0,d2 | zero out unused registers
moveq #0,d6 | (might make future compatibility
movel d6,a1 | would have known contents)
movel d6,a2
movel d6,a3
movel d6,a5
movel d6,a6
| jmp sp@ | jump to kernel entry point
rts | enter kernel at address on stack ***
| A do-nothing MMU root pointer (includes the following long as well)
nullrp: .long 0x7fff0001
zero: .long 0
");
void
usage()
{
fprintf(stderr, "usage: %s [-abkptADSV] [-c machine] [-m mem] kernel\n",
program_name);
exit(1);
}
void
verbose_usage()
{
fprintf(stderr, "
NAME
\t%s - loads NetBSD from amiga dos.
SYNOPSIS
\t%s [-abkptDSV] [-c machine] [-m mem] kernel
OPTIONS
\t-a Boot up to multiuser mode.
\t-b Ask for which root device.
\t Its possible to have multiple roots and choose between them.
\t-c Set machine type. [e.g 3000]
\t-k Reserve the first 4M of fast mem [Some one else
\t is going to have to answer what that it is used for].
\t-m Tweak amount of available memory, for finding minimum amount
\t of memory required to run. Sets fastmem size to specified
\t size in Kbytes.
\t-p Use highest priority fastmem segement instead of the largest
\t segment. The higher priority segment is usually faster
\t (i.e. 32 bit memory), but some people have smaller amounts
\t of 32 bit memory.
\t-t This is a *test* option. It prints out the memory
\t list information being passed to the kernel and also
\t exits without actually starting NetBSD.
\t-S Include kernel symbol table.
\t-D Enter debugger
\t-A Use AGA display mode, if available.
\t-V Version of loadbsd program.
HISTORY
\tThis version supports Kernel version 720 +\n",
program_name, program_name);
exit(1);
}
void
_Vdomessage(doexit, eval, doerrno, fmt, args)
int doexit, doerrno, eval;
const char *fmt;
va_list args;
{
fprintf(stderr, "%s: ", program_name);
if (fmt) {
vfprintf(stderr, fmt, args);
fprintf(stderr, ": ");
}
if (doerrno && errno < sys_nerr) {
fprintf(stderr, "%s", strerror(errno));
if (errno == EINTR || errno == 0) {
int sigs;
sigpending((sigset_t *)&sigs);
printf("%x\n", sigs);
}
}
fprintf(stderr, "\n");
if (doexit)
exit(eval);
}
void
err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_Vdomessage(1, eval, 1, fmt, ap);
/*NOTREACHED*/
}
void
errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_Vdomessage(1, eval, 0, fmt, ap);
/*NOTREACHED*/
}
void
warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_Vdomessage(0, 0, 1, fmt, ap);
va_end(ap);
}
void
warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_Vdomessage(0, 0, 0, fmt, ap);
va_end(ap);
}