diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-01-07 02:33:00 +0000 |
commit | beb116954b9b7f3bb56412b2494b562f02b864b1 (patch) | |
tree | 120e997879884e1b9d93b265221b939d2ef1ade1 /arch/m68k/boot | |
parent | 908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff) |
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'arch/m68k/boot')
-rw-r--r-- | arch/m68k/boot/Makefile | 62 | ||||
-rw-r--r-- | arch/m68k/boot/amiga/bootstrap.c | 333 | ||||
-rw-r--r-- | arch/m68k/boot/amiga/bootstrap.h | 175 | ||||
-rw-r--r-- | arch/m68k/boot/amiga/linuxboot.c | 1128 | ||||
-rw-r--r-- | arch/m68k/boot/amiga/linuxboot.h | 427 | ||||
-rw-r--r-- | arch/m68k/boot/atari/bootp.c | 793 | ||||
-rw-r--r-- | arch/m68k/boot/atari/bootp.h | 44 | ||||
-rw-r--r-- | arch/m68k/boot/atari/bootstrap.c | 1083 | ||||
-rw-r--r-- | arch/m68k/boot/atari/bootstrap.h | 147 | ||||
-rw-r--r-- | arch/m68k/boot/atari/ethlance.c | 435 | ||||
-rw-r--r-- | arch/m68k/boot/atari/ethlance.h | 7 | ||||
-rw-r--r-- | arch/m68k/boot/atari/sysvars.h | 22 |
12 files changed, 4656 insertions, 0 deletions
diff --git a/arch/m68k/boot/Makefile b/arch/m68k/boot/Makefile new file mode 100644 index 000000000..e67a9c4d1 --- /dev/null +++ b/arch/m68k/boot/Makefile @@ -0,0 +1,62 @@ +# +# linux/arch/m68k/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. + +ifdef CONFIG_AMIGA +AMIGA_BOOTSTRAP = amiga_bootstrap +AMIGA_BOOTOBJS := amiga/bootstrap.o amiga/linuxboot.o +AMIGA_HOSTCC = m68k-cbm-amigados-gcc -I$(TOPDIR)/include +AMIGA_HOSTFLAGS=-m68030 -O2 -Wall -Dlinux +endif + +ifdef CONFIG_ATARI +ATARI_BOOTSTRAP = atari_bootstrap +ATARI_BOOTOBJS := atari/bootstrap.o +ATARI_HOSTCC = m68k-mint-gcc -I$(TOPDIR)/include +ATARI_HOSTFLAGS = -m68030 -m68881 -Dlinux -O2 -Wall + +# BOOTP/TFTP support in bootstrap? +# USE_BOOTP = y + +ifdef USE_BOOTP +ATARI_BOOTOBJS += atari/bootp.o +ATARI_HOSTFLAGS += -DUSE_BOOTP + +# low-level Ethernet drivers: + +# Lance (RieblCard, PAM-VME) +ATARI_BOOTOBJS += atari/ethlance.o +ATARI_HOSTFLAGS += -DETHLL_LANCE + +endif +endif + +ifdef CONFIG_ATARI +atari_bootstrap: $(ATARI_BOOTOBJS) + $(ATARI_HOSTCC) $(ATARI_HOSTFLAGS) -o $@ $(ATARI_BOOTOBJS) + rm -f ../../../bootstrap + ln $@ ../../../bootstrap +endif + +ifdef CONFIG_AMIGA +amiga_bootstrap: $(AMIGA_BOOTOBJS) + $(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -o $@ -s -noixemul $(AMIGA_BOOTOBJS) + rm -f ../../../bootstrap + ln $@ ../../../bootstrap +endif + +$(AMIGA_BOOTOBJS): %.o: %.c + $(AMIGA_HOSTCC) $(AMIGA_HOSTFLAGS) -c $< -o $@ + +$(ATARI_BOOTOBJS): %.o: %.c + $(ATARI_HOSTCC) $(ATARI_HOSTFLAGS) -c $< -o $@ + +bootstrap: $(AMIGA_BOOTSTRAP) $(ATARI_BOOTSTRAP) + +clean: + rm -f *.o amiga/*.o atari/*.o amiga_bootstrap atari_bootstrap + +dep: diff --git a/arch/m68k/boot/amiga/bootstrap.c b/arch/m68k/boot/amiga/bootstrap.c new file mode 100644 index 000000000..20ca0621f --- /dev/null +++ b/arch/m68k/boot/amiga/bootstrap.c @@ -0,0 +1,333 @@ +/* +** linux/arch/m68k/boot/amiga/bootstrap.c -- This program loads the Linux/m68k +** kernel into an Amiga and launches +** it. +** +** Copyright 1993,1994 by Hamish Macdonald, Greg Harp +** +** Modified 11-May-94 by Geert Uytterhoeven +** (Geert.Uytterhoeven@cs.kuleuven.ac.be) +** - A3640 MapROM check +** Modified 31-May-94 by Geert Uytterhoeven +** - Memory thrash problem solved +** Modified 07-March-95 by Geert Uytterhoeven +** - Memory block sizes are rounded to a multiple of 256K instead of 1M +** This _requires_ >0.9pl5 to work! +** (unless all block sizes are multiples of 1M :-) +** Modified 11-July-95 by Andreas Schwab +** - Support for ELF kernel (untested!) +** Modified 10-Jan-96 by Geert Uytterhoeven +** - The real Linux/m68k boot code moved to linuxboot.[ch] +** Modified 9-Sep-96 by Geert Uytterhoeven +** - Rewritten option parsing +** - New parameter passing to linuxboot() (linuxboot_args) +** +** This file is subject to the terms and conditions of the GNU General Public +** License. See the file COPYING in the main directory of this archive +** for more details. +** +*/ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <sys/file.h> +#include <sys/types.h> +#include <unistd.h> + +/* required Linux/m68k include files */ +#include <linux/a.out.h> +#include <linux/elf.h> +#include <asm/setup.h> +#include <asm/page.h> + +/* Amiga bootstrap include files */ +#include "linuxboot.h" +#include "bootstrap.h" + + +/* Library Bases */ +extern const struct ExecBase *SysBase; +const struct ExpansionBase *ExpansionBase; +const struct GfxBase *GfxBase; + +static const char *memfile_name = NULL; + +static int model = AMI_UNKNOWN; + +static const char *ProgramName; + +struct linuxboot_args args; + + + /* + * Function Prototypes + */ + +static void Usage(void) __attribute__ ((noreturn)); +int main(int argc, char *argv[]); +static void Puts(const char *str); +static long GetChar(void); +static void PutChar(char c); +static void Printf(const char *fmt, ...); +static int Open(const char *path); +static int Seek(int fd, int offset); +static int Read(int fd, char *buf, int count); +static void Close(int fd); +static int FileSize(const char *path); +static void Sleep(u_long micros); +static int ModifyBootinfo(struct bootinfo *bi); + + +static void Usage(void) +{ + fprintf(stderr, + "Linux/m68k Amiga Bootstrap version " AMIBOOT_VERSION "\n\n" + "Usage: %s [options] [kernel command line]\n\n" + "Valid options are:\n" + " -h, --help Display this usage information\n" + " -k, --kernel file Use kernel image `file' (default is `vmlinux')\n" + " -r, --ramdisk file Use ramdisk image `file'\n" + " -d, --debug Enable debug mode\n" + " -m, --memfile file Use memory file `file'\n" + " -v, --keep-video Don't reset the video mode\n" + " -t, --model id Set the Amiga model to `id'\n\n", + ProgramName); + exit(EXIT_FAILURE); +} + + +int main(int argc, char *argv[]) +{ + int i; + int debugflag = 0, keep_video = 0; + const char *kernel_name = NULL; + const char *ramdisk_name = NULL; + char commandline[CL_SIZE] = ""; + + ProgramName = argv[0]; + while (--argc) { + argv++; + if (!strcmp(argv[0], "-h") || !strcmp(argv[0], "--help")) + Usage(); + else if (!strcmp(argv[0], "-k") || !strcmp(argv[0], "--kernel")) + if (--argc && !kernel_name) { + kernel_name = argv[1]; + argv++; + } else + Usage(); + else if (!strcmp(argv[0], "-r") || !strcmp(argv[0], "--ramdisk")) + if (--argc && !ramdisk_name) { + ramdisk_name = argv[1]; + argv++; + } else + Usage(); + else if (!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")) + debugflag = 1; + else if (!strcmp(argv[0], "-m") || !strcmp(argv[0], "--memfile")) + if (--argc && !memfile_name) { + memfile_name = argv[1]; + argv++; + } else + Usage(); + else if (!strcmp(argv[0], "-v") || !strcmp(argv[0], "--keep-video")) + keep_video = 1; + else if (!strcmp(argv[0], "-t") || !strcmp(argv[0], "--model")) + if (--argc && !model) { + model = atoi(argv[1]); + argv++; + } else + Usage(); + else + break; + } + if (!kernel_name) + kernel_name = "vmlinux"; + + SysBase = *(struct ExecBase **)4; + + /* open Expansion Library */ + ExpansionBase = (struct ExpansionBase *)OpenLibrary("expansion.library", + 36); + if (!ExpansionBase) { + fputs("Unable to open expansion.library V36 or greater! Aborting...\n", + stderr); + exit(EXIT_FAILURE); + } + + /* open Graphics Library */ + GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", 0); + if (!GfxBase) { + fputs("Unable to open graphics.library! Aborting...\n", stderr); + exit(EXIT_FAILURE); + } + + /* + * Join command line options + */ + i = 0; + while (argc--) { + if ((i+strlen(*argv)+1) < CL_SIZE) { + i += strlen(*argv) + 1; + if (commandline[0]) + strcat(commandline, " "); + strcat(commandline, *argv++); + } + } + + args.kernelname = kernel_name; + args.ramdiskname = ramdisk_name; + args.commandline = commandline; + args.debugflag = debugflag; + args.keep_video = keep_video; + args.reset_boards = 1; + args.puts = Puts; + args.getchar = GetChar; + args.putchar = PutChar; + args.printf = Printf; + args.open = Open; + args.seek = Seek; + args.read = Read; + args.close = Close; + args.filesize = FileSize; + args.sleep = Sleep; + args.modify_bootinfo = ModifyBootinfo; + + /* Do The Right Stuff */ + linuxboot(&args); + + CloseLibrary((struct Library *)GfxBase); + CloseLibrary((struct Library *)ExpansionBase); + + /* if we ever get here, something went wrong */ + exit(EXIT_FAILURE); +} + + + /* + * Routines needed by linuxboot + */ + +static void Puts(const char *str) +{ + fputs(str, stderr); +} + +static long GetChar(void) +{ + return(getchar()); +} + +static void PutChar(char c) +{ + fputc(c, stderr); +} + +static void Printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static int Open(const char *path) +{ + return(open(path, O_RDONLY)); +} + +static int Seek(int fd, int offset) +{ + return(lseek(fd, offset, SEEK_SET)); +} + + +static int Read(int fd, char *buf, int count) +{ + return(read(fd, buf, count)); +} + +static void Close(int fd) +{ + close(fd); +} + +static int FileSize(const char *path) +{ + int fd, size = -1; + + if ((fd = open(path, O_RDONLY)) != -1) { + size = lseek(fd, 0, SEEK_END); + close(fd); + } + return(size); +} + +static void Sleep(u_long micros) +{ + struct MsgPort *TimerPort; + struct timerequest *TimerRequest; + + if ((TimerPort = CreateMsgPort())) { + if ((TimerRequest = CreateIORequest(TimerPort, + sizeof(struct timerequest)))) { + if (!OpenDevice("timer.device", UNIT_VBLANK, + (struct IORequest *)TimerRequest, 0)) { + TimerRequest->io_Command = TR_ADDREQUEST; + TimerRequest->io_Flags = IOF_QUICK; + TimerRequest->tv_secs = micros/1000000; + TimerRequest->tv_micro = micros%1000000; + DoIO((struct IORequest *)TimerRequest); + CloseDevice((struct IORequest *)TimerRequest); + } + DeleteIORequest(TimerRequest); + } + DeleteMsgPort(TimerPort); + } +} + + +static int ModifyBootinfo(struct bootinfo *bi) +{ + /* + * if we have a memory file, read the memory information from it + */ + if (memfile_name) { + FILE *fp; + int i; + + if ((fp = fopen(memfile_name, "r")) == NULL) { + perror("open memory file"); + fprintf(stderr, "Cannot open memory file %s\n", memfile_name); + return(FALSE); + } + + if (fscanf(fp, "%lu", &bi->bi_amiga.chip_size) != 1) { + fprintf(stderr, "memory file does not contain chip memory size\n"); + fclose(fp); + return(FALSE); + } + + for (i = 0; i < NUM_MEMINFO; i++) { + if (fscanf(fp, "%lx %lu", &bi->memory[i].addr, &bi->memory[i].size) + != 2) + break; + } + + fclose(fp); + + if (i != bi->num_memory && i > 0) + bi->num_memory = i; + } + + /* + * change the Amiga model, if necessary + */ + if (model != AMI_UNKNOWN) + bi->bi_amiga.model = model; + + return(TRUE); +} diff --git a/arch/m68k/boot/amiga/bootstrap.h b/arch/m68k/boot/amiga/bootstrap.h new file mode 100644 index 000000000..d7a5c44fd --- /dev/null +++ b/arch/m68k/boot/amiga/bootstrap.h @@ -0,0 +1,175 @@ +/* +** linux/arch/m68k/boot/amiga/bootstrap.h -- This file is part of the Amiga +** bootloader. +** +** Copyright 1993, 1994 by Hamish Macdonald +** +** Some minor additions by Michael Rausch 1-11-94 +** Modified 11-May-94 by Geert Uytterhoeven +** (Geert.Uytterhoeven@cs.kuleuven.ac.be) +** - inline Supervisor() call +** Modified 10-Jan-96 by Geert Uytterhoeven +** - The real Linux/m68k boot code moved to linuxboot.[ch] +** Modified 9-Sep-96 by Geert Uytterhoeven +** - const library bases +** - fixed register naming for m68k-cbm-amigados-gcc +** +** This file is subject to the terms and conditions of the GNU General Public +** License. See the file COPYING in the main directory of this archive +** for more details. +** +*/ + +#include <asm/amigatypes.h> + + +struct MsgPort { + u_char fill1[15]; + u_char mp_SigBit; + u_char fill2[18]; +}; + +struct IOStdReq { + u_char fill1[20]; + struct Device *io_Device; + u_char fill2[4]; + u_short io_Command; + u_char io_Flags; + char io_Error; + u_long io_Actual; + u_long io_Length; + void *io_Data; + u_char fill4[4]; +}; + +#define IOF_QUICK (1<<0) + +struct timerequest { + u_char fill1[28]; + u_short io_Command; + u_char io_Flags; + u_char fill2[1]; + u_long tv_secs; + u_long tv_micro; +}; + +#define UNIT_VBLANK 1 +#define TR_ADDREQUEST 9 + + +struct Library; +struct IORequest; + + +static __inline void CloseLibrary(struct Library *library) +{ + register const struct ExecBase *a6 __asm("a6") = SysBase; + register struct Library *a1 __asm("a1") = library; + __asm __volatile ("jsr a6@(-0x19e)" + : /* no output */ + : "r" (a6), "r" (a1) + : "a0","a1","d0","d1", "memory"); +} + +static __inline struct Library *OpenLibrary(char *libName, + unsigned long version) +{ + register struct Library * _res __asm("d0"); + register const struct ExecBase *a6 __asm("a6") = SysBase; + register u_char *a1 __asm("a1") = libName; + register unsigned long d0 __asm("d0") = version; + __asm __volatile ("jsr a6@(-0x228)" + : "=r" (_res) + : "r" (a6), "r" (a1), "r" (d0) + : "a0","a1","d0","d1", "memory"); + return _res; +} + +static __inline char OpenDevice(u_char *devName, u_long unit, + struct IORequest *ioRequest, u_long flags) +{ + register char _res __asm("d0"); + register const struct ExecBase *a6 __asm("a6") = SysBase; + register u_char *a0 __asm("a0") = devName; + register u_long d0 __asm("d0") = unit; + register struct IORequest *a1 __asm("a1") = ioRequest; + register u_long d1 __asm("d1") = flags; + + __asm __volatile ("jsr a6@(-0x1bc)" + : "=r" (_res) + : "r" (a6), "r" (a0), "r" (a1), "r" (d0), "r" (d1) + : "a0","a1","d0","d1", "memory"); + return(_res); +} + +static __inline void CloseDevice(struct IORequest *ioRequest) +{ + register const struct ExecBase *a6 __asm("a6") = SysBase; + register struct IORequest *a1 __asm("a1") = ioRequest; + + __asm __volatile ("jsr a6@(-0x1c2)" + : /* no output */ + : "r" (a6), "r" (a1) + : "a0","a1","d0","d1", "memory"); +} + +static __inline char DoIO(struct IORequest *ioRequest) +{ + register char _res __asm("d0"); + register const struct ExecBase *a6 __asm("a6") = SysBase; + register struct IORequest *a1 __asm("a1") = ioRequest; + + __asm __volatile ("jsr a6@(-0x1c8)" + : "=r" (_res) + : "r" (a6), "r" (a1) + : "a0","a1","d0","d1", "memory"); + return(_res); +} + +static __inline void *CreateIORequest(struct MsgPort *port, u_long size) +{ + register struct Library *_res __asm("d0"); + register const struct ExecBase *a6 __asm("a6") = SysBase; + register struct MsgPort *a0 __asm("a0") = port; + register u_long d0 __asm("d0") = size; + + __asm __volatile ("jsr a6@(-0x28e)" + : "=r" (_res) + : "r" (a6), "r" (a0), "r" (d0) + : "a0","a1","d0","d1", "memory"); + return(_res); +} + +static __inline void DeleteIORequest(void *ioRequest) +{ + register const struct ExecBase *a6 __asm("a6") = SysBase; + register void *a0 __asm("a0") = ioRequest; + + __asm __volatile ("jsr a6@(-0x294)" + : /* no output */ + : "r" (a6), "r" (a0) + : "a0","a1","d0","d1", "memory"); +} + +static __inline struct MsgPort *CreateMsgPort(void) +{ + register struct MsgPort *_res __asm("d0"); + register const struct ExecBase *a6 __asm("a6") = SysBase; + + __asm __volatile ("jsr a6@(-0x29a)" + : "=r" (_res) + : "r" (a6) + : "a0","a1","d0","d1", "memory"); + return(_res); +} + +static __inline void DeleteMsgPort(struct MsgPort *port) +{ + register const struct ExecBase *a6 __asm("a6") = SysBase; + register struct MsgPort *a0 __asm("a0") = port; + + __asm __volatile ("jsr a6@(-0x2a0)" + : /* no output */ + : "r" (a6), "r" (a0) + : "a0","a1","d0","d1", "memory"); +} diff --git a/arch/m68k/boot/amiga/linuxboot.c b/arch/m68k/boot/amiga/linuxboot.c new file mode 100644 index 000000000..975868c45 --- /dev/null +++ b/arch/m68k/boot/amiga/linuxboot.c @@ -0,0 +1,1128 @@ +/* + * linux/arch/m68k/boot/amiga/linuxboot.c -- Generic routine to boot Linux/m68k + * on Amiga, used by both Amiboot and + * Amiga-Lilo. + * + * Created 1996 by Geert Uytterhoeven + * + * + * This file is based on the original bootstrap code (bootstrap.c): + * + * Copyright (C) 1993, 1994 Hamish Macdonald + * Greg Harp + * + * with work by Michael Rausch + * Geert Uytterhoeven + * Frank Neumann + * Andreas Schwab + * + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + +#ifndef __GNUC__ +#error GNU CC is required to compile this program +#endif /* __GNUC__ */ + + +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> + +#include <linux/a.out.h> +#include <linux/elf.h> +#include <linux/linkage.h> +#include <asm/setup.h> +#include <asm/amigatypes.h> +#include <asm/amigahw.h> +#include <asm/page.h> + +#include "linuxboot.h" + + +#undef custom +#define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR))) + +/* temporary stack size */ +#define TEMP_STACKSIZE (256) + +extern char copyall, copyallend; + +static struct exec kexec; +static Elf32_Ehdr kexec_elf; +static struct bootinfo bi; + +static const struct linuxboot_args *linuxboot_args; + +#define kernelname linuxboot_args->kernelname +#define ramdiskname linuxboot_args->ramdiskname +#define commandline linuxboot_args->commandline +#define debugflag linuxboot_args->debugflag +#define keep_video linuxboot_args->keep_video +#define reset_boards linuxboot_args->reset_boards + +#define Puts linuxboot_args->puts +#define GetChar linuxboot_args->getchar +#define PutChar linuxboot_args->putchar +#define Printf linuxboot_args->printf +#define Open linuxboot_args->open +#define Seek linuxboot_args->seek +#define Read linuxboot_args->read +#define Close linuxboot_args->close +#define FileSize linuxboot_args->filesize +#define Sleep linuxboot_args->sleep +#define ModifyBootinfo linuxboot_args->modify_bootinfo + + + /* + * Function Prototypes + */ + +static u_long get_chipset(void); +static u_long get_cpu(void); +static u_long get_model(u_long chipset); +static int probe_resident(const char *name); +static int probe_resource(const char *name); +static int check_bootinfo_version(const char *memptr); +static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, + u_long start_mem, u_long mem_size, u_long rd_size, + u_long kernel_size) __attribute__ ((noreturn)); +asmlinkage u_long maprommed(void); + + + /* + * Reset functions for nasty Zorro boards + */ + +static void reset_rb3(const struct ConfigDev *cd); +static void reset_piccolo(const struct ConfigDev *cd); +static void reset_sd64(const struct ConfigDev *cd); +static void reset_ariadne(const struct ConfigDev *cd); +static void reset_hydra(const struct ConfigDev *cd); +#if 0 +static void reset_a2060(const struct ConfigDev *cd); +#endif + +struct boardreset { + u_short manuf; + u_short prod; + const char *name; + void (*reset)(const struct ConfigDev *cd); +}; + +static struct boardreset boardresetdb[] = { + { MANUF_HELFRICH1, PROD_RAINBOW3, "Rainbow 3", reset_rb3 }, + { MANUF_HELFRICH2, PROD_PICCOLO_REG, "Piccolo", reset_piccolo }, + { MANUF_HELFRICH2, PROD_SD64_REG, "SD64", reset_sd64 }, + { MANUF_VILLAGE_TRONIC, PROD_ARIADNE, "Ariadne", reset_ariadne }, + { MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, "Hydra", reset_hydra }, +#if 0 + { MANUF_COMMODORE, PROD_A2060, "A2060", reset_a2060 }, +#endif +}; +#define NUM_BOARDRESET sizeof(boardresetdb)/sizeof(*boardresetdb) + +static void (*boardresetfuncs[NUM_AUTO])(const struct ConfigDev *cd); + + +const char *amiga_models[] = { + "Amiga 500", "Amiga 500+", "Amiga 600", "Amiga 1000", "Amiga 1200", + "Amiga 2000", "Amiga 2500", "Amiga 3000", "Amiga 3000T", "Amiga 3000+", + "Amiga 4000", "Amiga 4000T", "CDTV", "CD32", "Draco" +}; +const u_long first_amiga_model = AMI_500; +const u_long last_amiga_model = AMI_DRACO; + + +#define MASK(model) (1<<AMI_##model) + +#define CLASS_A3000 (MASK(3000) | MASK(3000T)) +#define CLASS_A4000 (MASK(4000) | MASK(4000T)) +#define CLASS_ZKICK (MASK(500) | MASK(1000) | MASK(2000) | MASK(2500)) + + + /* + * Boot the Linux/m68k Operating System + */ + +u_long linuxboot(const struct linuxboot_args *args) +{ + int kfd = -1, rfd = -1, elf_kernel = 0; + int i, j; + const struct MemHeader *mnp; + struct ConfigDev *cdp = NULL; + char *memptr = NULL; + u_long *stack = NULL; + u_long fast_total, model_mask, startcodesize, start_mem, mem_size, rd_size; + u_long kernel_size; + u_long memreq = 0, text_offset = 0; + Elf32_Phdr *kernel_phdrs = NULL; + void (*startfunc)(void); + u_short manuf; + u_char prod; + + linuxboot_args = args; + + /* print the greet message */ + Puts("\nLinux/m68k Amiga Bootstrap version " AMIBOOT_VERSION "\n"); + Puts("Copyright 1993,1994 by Hamish Macdonald and Greg Harp\n\n"); + + /* machine is Amiga */ + bi.machtype = MACH_AMIGA; + + /* determine chipset */ + bi.bi_amiga.chipset = get_chipset(); + + /* determine CPU type */ + bi.cputype = get_cpu(); + + /* determine Amiga model */ + bi.bi_amiga.model = get_model(bi.bi_amiga.chipset); + model_mask = (bi.bi_amiga.model != AMI_UNKNOWN) ? 1<<bi.bi_amiga.model : 0; + + /* Memory & AutoConfig based on 'unix_boot.c' by C= */ + + /* find all of the autoconfig boards in the system */ + bi.bi_amiga.num_autocon = 0; + for (i = 0; (cdp = (struct ConfigDev *)FindConfigDev(cdp, -1, -1)); i++) { + if (bi.bi_amiga.num_autocon < NUM_AUTO) { + /* copy the contents of each structure into our boot info */ + memcpy(&bi.bi_amiga.autocon[bi.bi_amiga.num_autocon], cdp, + sizeof(struct ConfigDev)); + /* count this device */ + bi.bi_amiga.num_autocon++; + } else + Printf("Warning: too many AutoConfig devices. Ignoring device at " + "0x%08lx\n", cdp->cd_BoardAddr); + } + + /* find out the memory in the system */ + bi.num_memory = 0; + for (mnp = (struct MemHeader *)SysBase->MemList.lh_Head; + mnp->mh_Node.ln_Succ; + mnp = (struct MemHeader *)mnp->mh_Node.ln_Succ) { + struct MemHeader mh; + + /* copy the information */ + mh = *mnp; + + /* skip virtual memory */ + if (!(mh.mh_Attributes & MEMF_PUBLIC)) + continue; + + /* if we suspect that Kickstart is shadowed in an A3000, + modify the entry to show 512K more at the top of RAM + Check first for a MapROMmed A3640 board: overwriting the + Kickstart image causes an infinite lock-up on reboot! */ + if ((mh.mh_Upper == (void *)0x07f80000) && + (model_mask & (CLASS_A3000 | CLASS_A4000))) + if ((bi.cputype & CPU_68040) && Supervisor(maprommed)) + Puts("A3640 MapROM detected.\n"); + else if (model_mask & CLASS_A3000) { + mh.mh_Upper = (void *)0x08000000; + Puts("A3000 shadowed Kickstart detected.\n"); + } + + /* if we suspect that Kickstart is zkicked, + modify the entry to show 512K more at the botton of RAM */ + if ((mh.mh_Lower == (void *)0x00280020) && + (model_mask & CLASS_ZKICK)) { + mh.mh_Lower = (void *)0x00200000; + Puts("ZKick detected.\n"); + } + + /* mask the memory limit values */ + mh.mh_Upper = (void *)((u_long)mh.mh_Upper & 0xfffff000); + mh.mh_Lower = (void *)((u_long)mh.mh_Lower & 0xfffff000); + + /* if fast memory */ + if (mh.mh_Attributes & MEMF_FAST) { + /* set the size value to the size of this block and mask off to a + 256K increment */ + u_long size = ((u_long)mh.mh_Upper-(u_long)mh.mh_Lower)&0xfffc0000; + if (size > 0) + if (bi.num_memory < NUM_MEMINFO) { + /* record the start and size */ + bi.memory[bi.num_memory].addr = (u_long)mh.mh_Lower; + bi.memory[bi.num_memory].size = size; + /* count this block */ + bi.num_memory++; + } else + Printf("Warning: too many memory blocks. Ignoring block " + "of %ldK at 0x%08x\n", size>>10, + (u_long)mh.mh_Lower); + } else if (mh.mh_Attributes & MEMF_CHIP) + /* if CHIP memory, record the size */ + bi.bi_amiga.chip_size = (u_long)mh.mh_Upper; + } + + /* get info from ExecBase */ + bi.bi_amiga.vblank = SysBase->VBlankFrequency; + bi.bi_amiga.psfreq = SysBase->PowerSupplyFrequency; + bi.bi_amiga.eclock = SysBase->ex_EClockFrequency; + + /* copy command line options into the kernel command line */ + strncpy(bi.command_line, commandline, CL_SIZE); + bi.command_line[CL_SIZE-1] = '\0'; + + + /* modify the bootinfo, e.g. to change the memory configuration */ + if (ModifyBootinfo && !ModifyBootinfo(&bi)) + goto Fail; + + + /* display Amiga model */ + if (bi.bi_amiga.model >= first_amiga_model && + bi.bi_amiga.model <= last_amiga_model) + Printf("%s ", amiga_models[bi.bi_amiga.model-first_amiga_model]); + else + Puts("Amiga "); + + /* display the CPU type */ + Puts("CPU: "); + switch (bi.cputype & CPU_MASK) { + case CPU_68020: + Puts("68020 (Do you have an MMU?)"); + break; + case CPU_68030: + Puts("68030"); + break; + case CPU_68040: + Puts("68040"); + break; + case CPU_68060: + Puts("68060"); + break; + default: + Puts("Insufficient for Linux. Aborting...\n"); + Printf("SysBase->AttnFlags = 0x%08lx\n", SysBase->AttnFlags); + goto Fail; + } + switch (bi.cputype & ~CPU_MASK) { + case FPU_68881: + Puts(" with 68881 FPU"); + break; + case FPU_68882: + Puts(" with 68882 FPU"); + break; + case FPU_68040: + case FPU_68060: + Puts(" with internal FPU"); + break; + default: + Puts(" without FPU"); + break; + } + + /* display the chipset */ + switch(bi.bi_amiga.chipset) { + case CS_STONEAGE: + Puts(", old or unknown chipset"); + break; + case CS_OCS: + Puts(", OCS"); + break; + case CS_ECS: + Puts(", ECS"); + break; + case CS_AGA: + Puts(", AGA chipset"); + break; + } + + Puts("\n\n"); + + /* display the command line */ + Printf("Command line is '%s'\n", bi.command_line); + + /* display the clock statistics */ + Printf("Vertical Blank Frequency: %ldHz\n", bi.bi_amiga.vblank); + Printf("Power Supply Frequency: %ldHz\n", bi.bi_amiga.psfreq); + Printf("EClock Frequency: %ldHz\n\n", bi.bi_amiga.eclock); + + /* display autoconfig devices */ + if (bi.bi_amiga.num_autocon) { + Printf("Found %ld AutoConfig Device%s\n", bi.bi_amiga.num_autocon, + bi.bi_amiga.num_autocon > 1 ? "s" : ""); + for (i = 0; i < bi.bi_amiga.num_autocon; i++) { + Printf("Device %ld: addr = 0x%08lx", i, + (u_long)bi.bi_amiga.autocon[i].cd_BoardAddr); + boardresetfuncs[i] = NULL; + if (reset_boards) { + manuf = bi.bi_amiga.autocon[i].cd_Rom.er_Manufacturer; + prod = bi.bi_amiga.autocon[i].cd_Rom.er_Product; + for (j = 0; j < NUM_BOARDRESET; j++) + if ((manuf == boardresetdb[j].manuf) && + (prod == boardresetdb[j].prod)) { + Printf(" [%s - will be reset at kernel boot time]", + boardresetdb[j].name); + boardresetfuncs[i] = boardresetdb[j].reset; + break; + } + } + PutChar('\n'); + } + } else + Puts("No AutoConfig Devices Found\n"); + + /* display memory */ + if (bi.num_memory) { + Printf("\nFound %ld Block%sof Memory\n", bi.num_memory, + bi.num_memory > 1 ? "s " : " "); + for (i = 0; i < bi.num_memory; i++) + Printf("Block %ld: 0x%08lx to 0x%08lx (%ldK)\n", i, + bi.memory[i].addr, bi.memory[i].addr+bi.memory[i].size, + bi.memory[i].size>>10); + } else { + Puts("No memory found?! Aborting...\n"); + goto Fail; + } + + /* display chip memory size */ + Printf("%ldK of CHIP memory\n", bi.bi_amiga.chip_size>>10); + + start_mem = bi.memory[0].addr; + mem_size = bi.memory[0].size; + + /* tell us where the kernel will go */ + Printf("\nThe kernel will be located at 0x%08lx\n", start_mem); + + /* verify that there is enough Chip RAM */ + if (bi.bi_amiga.chip_size < 512*1024) { + Puts("Not enough Chip RAM in this system. Aborting...\n"); + goto Fail; + } + + /* verify that there is enough Fast RAM */ + for (fast_total = 0, i = 0; i < bi.num_memory; i++) + fast_total += bi.memory[i].size; + if (fast_total < 2*1024*1024) { + Puts("Not enough Fast RAM in this system. Aborting...\n"); + goto Fail; + } + + /* support for ramdisk */ + if (ramdiskname) { + int size; + + if ((size = FileSize(ramdiskname)) == -1) { + Printf("Unable to find size of ramdisk file `%s'\n", ramdiskname); + goto Fail; + } + /* record ramdisk size */ + bi.ramdisk_size = (size+1023)>>10; + } else + bi.ramdisk_size = 0; + rd_size = bi.ramdisk_size<<10; + bi.ramdisk_addr = start_mem+mem_size-rd_size; + + /* open kernel executable and read exec header */ + if ((kfd = Open(kernelname)) == -1) { + Printf("Unable to open kernel file `%s'\n", kernelname); + goto Fail; + } + if (Read(kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) { + Puts("Unable to read exec header from kernel file\n"); + goto Fail; + } + + switch (N_MAGIC(kexec)) { + case ZMAGIC: + if (debugflag) + Puts("\nLoading a.out (ZMAGIC) Linux/m68k kernel...\n"); + text_offset = N_TXTOFF(kexec); + break; + + case QMAGIC: + if (debugflag) + Puts("\nLoading a.out (QMAGIC) Linux/m68k kernel...\n"); + text_offset = sizeof(kexec); + /* the text size includes the exec header; remove this */ + kexec.a_text -= sizeof(kexec); + break; + + default: + /* Try to parse it as an ELF header */ + Seek(kfd, 0); + if ((Read(kfd, (void *)&kexec_elf, sizeof(kexec_elf)) == + sizeof(kexec_elf)) && + (memcmp(&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0)) { + elf_kernel = 1; + if (debugflag) + Puts("\nLoading ELF Linux/m68k kernel...\n"); + /* A few plausibility checks */ + if ((kexec_elf.e_type != ET_EXEC) || + (kexec_elf.e_machine != EM_68K) || + (kexec_elf.e_version != EV_CURRENT)) { + Puts("Invalid ELF header contents in kernel\n"); + goto Fail; + } + /* Load the program headers */ + if (!(kernel_phdrs = + (Elf32_Phdr *)AllocMem(kexec_elf.e_phnum*sizeof(Elf32_Phdr), + MEMF_FAST | MEMF_PUBLIC | + MEMF_CLEAR))) { + Puts("Unable to allocate memory for program headers\n"); + goto Fail; + } + Seek(kfd, kexec_elf.e_phoff); + if (Read(kfd, (void *)kernel_phdrs, + kexec_elf.e_phnum*sizeof(*kernel_phdrs)) != + kexec_elf.e_phnum*sizeof(*kernel_phdrs)) { + Puts("Unable to read program headers from kernel file\n"); + goto Fail; + } + break; + } + Printf("Wrong magic number 0x%08lx in kernel header\n", + N_MAGIC(kexec)); + goto Fail; + } + + /* Load the kernel at one page after start of mem */ + start_mem += PAGE_SIZE; + mem_size -= PAGE_SIZE; + /* Align bss size to multiple of four */ + if (!elf_kernel) + kexec.a_bss = (kexec.a_bss+3) & ~3; + + /* calculate the total required amount of memory */ + if (elf_kernel) { + u_long min_addr = 0xffffffff, max_addr = 0; + for (i = 0; i < kexec_elf.e_phnum; i++) { + if (min_addr > kernel_phdrs[i].p_vaddr) + min_addr = kernel_phdrs[i].p_vaddr; + if (max_addr < kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz) + max_addr = kernel_phdrs[i].p_vaddr+kernel_phdrs[i].p_memsz; + } + /* This is needed for newer linkers that include the header in + the first segment. */ + if (min_addr == 0) { + min_addr = PAGE_SIZE; + kernel_phdrs[0].p_vaddr += PAGE_SIZE; + kernel_phdrs[0].p_offset += PAGE_SIZE; + kernel_phdrs[0].p_filesz -= PAGE_SIZE; + kernel_phdrs[0].p_memsz -= PAGE_SIZE; + } + kernel_size = max_addr-min_addr; + } else + kernel_size = kexec.a_text+kexec.a_data+kexec.a_bss; + memreq = kernel_size+sizeof(struct bootinfo)+rd_size; + if (!(memptr = (char *)AllocMem(memreq, MEMF_FAST | MEMF_PUBLIC | + MEMF_CLEAR))) { + Puts("Unable to allocate memory\n"); + goto Fail; + } + + /* read the text and data segments from the kernel image */ + if (elf_kernel) + for (i = 0; i < kexec_elf.e_phnum; i++) { + if (Seek(kfd, kernel_phdrs[i].p_offset) == -1) { + Printf("Failed to seek to segment %ld\n", i); + goto Fail; + } + if (Read(kfd, memptr+kernel_phdrs[i].p_vaddr-PAGE_SIZE, + kernel_phdrs[i].p_filesz) != kernel_phdrs[i].p_filesz) { + Printf("Failed to read segment %ld\n", i); + goto Fail; + } + } + else { + if (Seek(kfd, text_offset) == -1) { + Printf("Failed to seek to text\n"); + goto Fail; + } + if (Read(kfd, memptr, kexec.a_text) != kexec.a_text) { + Printf("Failed to read text\n"); + goto Fail; + } + /* data follows immediately after text */ + if (Read(kfd, memptr+kexec.a_text, kexec.a_data) != kexec.a_data) { + Printf("Failed to read data\n"); + goto Fail; + } + } + Close(kfd); + kfd = -1; + + /* Check kernel's bootinfo version */ + if (!check_bootinfo_version(memptr)) + goto Fail; + + /* copy the bootinfo to the end of the kernel image */ + memcpy((void *)(memptr+kernel_size), &bi, sizeof(struct bootinfo)); + + if (ramdiskname) { + if ((rfd = Open(ramdiskname)) == -1) { + Printf("Unable to open ramdisk file `%s'\n", ramdiskname); + goto Fail; + } + if (Read(rfd, memptr+kernel_size+sizeof(bi), rd_size) != rd_size) { + Printf("Failed to read ramdisk file\n"); + goto Fail; + } + Close(rfd); + rfd = -1; + } + + /* allocate temporary chip ram stack */ + if (!(stack = (u_long *)AllocMem(TEMP_STACKSIZE, MEMF_CHIP | MEMF_CLEAR))) { + Puts("Unable to allocate memory for stack\n"); + goto Fail; + } + + /* allocate chip ram for copy of startup code */ + startcodesize = ©allend-©all; + if (!(startfunc = (void (*)(void))AllocMem(startcodesize, + MEMF_CHIP | MEMF_CLEAR))) { + Puts("Unable to allocate memory for startcode\n"); + goto Fail; + } + + /* copy startup code to CHIP RAM */ + memcpy(startfunc, ©all, startcodesize); + + if (debugflag) { + if (bi.ramdisk_size) + Printf("RAM disk at 0x%08lx, size is %ldK\n", + (u_long)memptr+kernel_size, bi.ramdisk_size); + + if (elf_kernel) { + PutChar('\n'); + for (i = 0; i < kexec_elf.e_phnum; i++) + Printf("Kernel segment %ld at 0x%08lx, size %ld\n", i, + start_mem+kernel_phdrs[i].p_vaddr-PAGE_SIZE, + kernel_phdrs[i].p_memsz); + Printf("Boot info at 0x%08lx\n", start_mem+kernel_size); + } else { + Printf("\nKernel text at 0x%08lx, code size 0x%08lx\n", start_mem, + kexec.a_text); + Printf("Kernel data at 0x%08lx, data size 0x%08lx\n", + start_mem+kexec.a_text, kexec.a_data); + Printf("Kernel bss at 0x%08lx, bss size 0x%08lx\n", + start_mem+kexec.a_text+kexec.a_data, kexec.a_bss); + Printf("Boot info at 0x%08lx\n", start_mem+kernel_size); + } + Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry : + kexec.a_entry); + + Printf("ramdisk dest top is 0x%08lx\n", start_mem+mem_size); + Printf("ramdisk lower limit is 0x%08lx\n", (u_long)memptr+kernel_size); + Printf("ramdisk src top is 0x%08lx\n", + (u_long)memptr+kernel_size+rd_size); + + Puts("\nType a key to continue the Linux/m68k boot..."); + GetChar(); + PutChar('\n'); + } + + /* wait for things to settle down */ + Sleep(1000000); + + if (!keep_video) + /* set graphics mode to a nice normal one */ + LoadView(NULL); + + Disable(); + + /* reset nasty Zorro boards */ + if (reset_boards) + for (i = 0; i < bi.bi_amiga.num_autocon; i++) + if (boardresetfuncs[i]) + boardresetfuncs[i](&bi.bi_amiga.autocon[i]); + + /* Turn off all DMA */ + custom.dmacon = DMAF_ALL | DMAF_MASTER; + + /* turn off caches */ + CacheControl(0, ~0); + + /* Go into supervisor state */ + SuperState(); + + /* turn off any mmu translation */ + disable_mmu(); + + /* execute the copy-and-go code (from CHIP RAM) */ + start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem, + mem_size, rd_size, kernel_size); + + /* Clean up and exit in case of a failure */ +Fail: + if (kfd != -1) + Close(kfd); + if (rfd != -1) + Close(rfd); + if (memptr) + FreeMem((void *)memptr, memreq); + if (stack) + FreeMem((void *)stack, TEMP_STACKSIZE); + if (kernel_phdrs) + FreeMem((void *)kernel_phdrs, kexec_elf.e_phnum*sizeof(Elf32_Phdr)); + return(FALSE); +} + + + /* + * Determine the Chipset + */ + +static u_long get_chipset(void) +{ + u_char cs; + u_long chipset; + + if (GfxBase->Version >= 39) + cs = SetChipRev(SETCHIPREV_BEST); + else + cs = GfxBase->ChipRevBits0; + if ((cs & GFXG_AGA) == GFXG_AGA) + chipset = CS_AGA; + else if ((cs & GFXG_ECS) == GFXG_ECS) + chipset = CS_ECS; + else if ((cs & GFXG_OCS) == GFXG_OCS) + chipset = CS_OCS; + else + chipset = CS_STONEAGE; + return(chipset); +} + + + /* + * Determine the CPU Type + */ + +static u_long get_cpu(void) +{ + u_long cpu = 0; + + if (SysBase->AttnFlags & AFF_68060) { + cpu = CPU_68060; + if (SysBase->AttnFlags & AFF_FPU40) + cpu |= FPU_68060; + } else if (SysBase->AttnFlags & AFF_68040) { + cpu = CPU_68040; + if (SysBase->AttnFlags & AFF_FPU40) + cpu |= FPU_68040; + } else { + if (SysBase->AttnFlags & AFF_68030) + cpu = CPU_68030; + else if (SysBase->AttnFlags & AFF_68020) + cpu = CPU_68020; + if (SysBase->AttnFlags & AFF_68882) + cpu |= FPU_68882; + else if (SysBase->AttnFlags & AFF_68881) + cpu |= FPU_68881; + } + return(cpu); +} + + + /* + * Determine the Amiga Model + */ + +static u_long get_model(u_long chipset) +{ + u_long model = AMI_UNKNOWN; + + if (debugflag) + Puts("Amiga model identification:\n"); + if (probe_resource("draco.resource")) + model = AMI_DRACO; + else { + if (debugflag) + Puts(" Chipset: "); + switch(chipset) { + case CS_STONEAGE: + if (debugflag) + Puts("Old or unknown\n"); + goto OCS; + break; + + case CS_OCS: + if (debugflag) + Puts("OCS\n"); +OCS: if (probe_resident("cd.device")) + model = AMI_CDTV; + else + /* let's call it an A2000 (may be A500, A1000, A2500) */ + model = AMI_2000; + break; + + case CS_ECS: + if (debugflag) + Puts("ECS\n"); + if (probe_resident("Magic 36.7") || + probe_resident("kickad 36.57") || + probe_resident("A3000 Bonus") || + probe_resident("A3000 bonus")) + /* let's call it an A3000 (may be A3000T) */ + model = AMI_3000; + else if (probe_resource("card.resource")) + model = AMI_600; + else + /* let's call it an A2000 (may be A500[+], A1000, A2500) */ + model = AMI_2000; + break; + + case CS_AGA: + if (debugflag) + Puts("AGA\n"); + if (probe_resident("A1000 Bonus") || + probe_resident("A4000 bonus")) + model = probe_resident("NCR scsi.device") ? AMI_4000T : + AMI_4000; + else if (probe_resource("card.resource")) + model = AMI_1200; + else if (probe_resident("cd.device")) + model = AMI_CD32; + else + model = AMI_3000PLUS; + break; + } + } + if (debugflag) { + Puts("\nType a key to continue..."); + GetChar(); + Puts("\n\n"); + } + return(model); +} + + + /* + * Probe for a Resident Modules + */ + +static int probe_resident(const char *name) +{ + const struct Resident *res; + + if (debugflag) + Printf(" Module `%s': ", name); + res = FindResident(name); + if (debugflag) + if (res) + Printf("0x%08lx\n", res); + else + Printf("not present\n"); + return(res ? TRUE : FALSE); +} + + + /* + * Probe for an available Resource + */ + +static int probe_resource(const char *name) +{ + const void *res; + + if (debugflag) + Printf(" Resource `%s': ", name); + res = OpenResource(name); + if (debugflag) + if (res) + Printf("0x%08lx\n", res); + else + Printf("not present\n"); + return(res ? TRUE : FALSE); +} + + + /* + * Compare the Bootstrap and Kernel Versions + */ + +static int check_bootinfo_version(const char *memptr) +{ + const struct bootversion *bv = (struct bootversion *)memptr; + unsigned long version = 0; + int i, kernel_major, kernel_minor, boots_major, boots_minor; + + if (bv->magic == BOOTINFOV_MAGIC) + for (i = 0; bv->machversions[i].machtype != 0; ++i) + if (bv->machversions[i].machtype == MACH_AMIGA) { + version = bv->machversions[i].version; + break; + } + if (!version) + Printf("Kernel has no bootinfo version info, assuming 0.0\n"); + + kernel_major = BI_VERSION_MAJOR(version); + kernel_minor = BI_VERSION_MINOR(version); + boots_major = BI_VERSION_MAJOR(AMIGA_BOOTI_VERSION); + boots_minor = BI_VERSION_MINOR(AMIGA_BOOTI_VERSION); + Printf("Bootstrap's bootinfo version: %ld.%ld\n", boots_major, + boots_minor); + Printf("Kernel's bootinfo version : %ld.%ld\n", kernel_major, + kernel_minor); + + if (kernel_major != boots_major) { + Printf("\nThis bootstrap is too %s for this kernel!\n", + boots_major < kernel_major ? "old" : "new"); + return(0); + } + if (kernel_minor > boots_minor) { + Printf("Warning: Bootinfo version of bootstrap and kernel differ!\n" ); + Printf(" Certain features may not work.\n"); + } + return(1); +} + + + /* + * Call the copy-and-go-code + */ + +static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, + u_long start_mem, u_long mem_size, u_long rd_size, + u_long kernel_size) +{ + register void (*a0)() __asm("a0") = startfunc; + register char *a2 __asm("a2") = stackp; + register char *a3 __asm("a3") = memptr; + register u_long a4 __asm("a4") = start_mem; + register u_long d0 __asm("d0") = mem_size; + register u_long d1 __asm("d1") = rd_size; + register u_long d2 __asm("d2") = kernel_size; + register u_long d3 __asm("d3") = sizeof(struct bootinfo); + + __asm __volatile ("movel a2,sp;" + "jmp a0@" + : /* no outputs */ + : "r" (a0), "r" (a2), "r" (a3), "r" (a4), "r" (d0), + "r" (d1), "r" (d2), "r" (d3) + /* no return */); + /* fake a noreturn */ + for (;;); +} + + + /* + * This assembler code is copied to chip ram, and then executed. + * It copies the kernel to it's final resting place. + * + * It is called with: + * + * a3 = memptr + * a4 = start_mem + * d0 = mem_size + * d1 = rd_size + * d2 = kernel_size + * d3 = sizeof(struct bootinfo) + */ + +asm(".text\n" +ALIGN_STR "\n" +SYMBOL_NAME_STR(copyall) ": + | /* copy kernel text and data */ + movel a3,a0 | src = (u_long *)memptr; + movel a0,a2 | limit = (u_long *)(memptr+kernel_size); + addl d2,a2 + movel a4,a1 | dest = (u_long *)start_mem; +1: cmpl a0,a2 + jeq 2f | while (src < limit) + moveb a0@+,a1@+ | *dest++ = *src++; + jra 1b +2: + | /* copy early bootinfo to end of bss */ + movel a3,a0 | src = (u_long *)(memptr+kernel_size); + addl d2,a0 | dest = end of bss (already in a1) + movel d3,d7 | count = sizeof(struct bootinfo) + subql #1,d7 +1: moveb a0@+,a1@+ | while (--count > -1) + dbra d7,1b | *dest++ = *src++ + + | /* copy the ramdisk to the top of memory */ + | /* (from back to front) */ + movel a4,a1 | dest = (u_long *)(start_mem+mem_size); + addl d0,a1 + movel a3,a2 | limit = (u_long *)(memptr+kernel_size + + addl d2,a2 | sizeof(struct bootinfo)); + addl d3,a2 + movel a2,a0 | src = (u_long *)((u_long)limit+rd_size); + addl d1,a0 +1: cmpl a0,a2 + beqs 2f | while (src > limit) + moveb a0@-,a1@- | *--dest = *--src; + bras 1b +2: + | /* jump to start of kernel */ + movel a4,a0 | jump_to (start_mem); + jmp a0@ +" +SYMBOL_NAME_STR(copyallend) ": +"); + + + /* + * Test for a MapROMmed A3640 Board + */ + +asm(".text\n" +ALIGN_STR "\n" +SYMBOL_NAME_STR(maprommed) ": + oriw #0x0700,sr + moveml #0x3f20,sp@- + | /* Save cache settings */ + .long 0x4e7a1002 | movec cacr,d1 */ + | /* Save MMU settings */ + .long 0x4e7a2003 | movec tc,d2 + .long 0x4e7a3004 | movec itt0,d3 + .long 0x4e7a4005 | movec itt1,d4 + .long 0x4e7a5006 | movec dtt0,d5 + .long 0x4e7a6007 | movec dtt1,d6 + moveq #0,d0 + movel d0,a2 + | /* Disable caches */ + .long 0x4e7b0002 | movec d0,cacr + | /* Disable MMU */ + .long 0x4e7b0003 | movec d0,tc + .long 0x4e7b0004 | movec d0,itt0 + .long 0x4e7b0005 | movec d0,itt1 + .long 0x4e7b0006 | movec d0,dtt0 + .long 0x4e7b0007 | movec d0,dtt1 + lea 0x07f80000,a0 + lea 0x00f80000,a1 + movel a0@,d7 + cmpl a1@,d7 + jne 1f + movel d7,d0 + notl d0 + movel d0,a0@ + nop | /* Thanks to Jörg Mayer! */ + cmpl a1@,d0 + jne 1f + moveq #-1,d0 | /* MapROMmed A3640 present */ + movel d0,a2 +1: movel d7,a0@ + | /* Restore MMU settings */ + .long 0x4e7b2003 | movec d2,tc + .long 0x4e7b3004 | movec d3,itt0 + .long 0x4e7b4005 | movec d4,itt1 + .long 0x4e7b5006 | movec d5,dtt0 + .long 0x4e7b6007 | movec d6,dtt1 + | /* Restore cache settings */ + .long 0x4e7b1002 | movec d1,cacr + movel a2,d0 + moveml sp@+,#0x04fc + rte +"); + + + /* + * Reset functions for nasty Zorro boards + */ + +static void reset_rb3(const struct ConfigDev *cd) +{ + volatile u_char *rb3_reg = (u_char *)(cd->cd_BoardAddr+0x01002000); + + /* FN: If a Rainbow III board is present, reset it to disable */ + /* its (possibly activated) vertical blank interrupts as the */ + /* kernel is not yet prepared to handle them (level 6). */ + + /* set RESET bit in special function register */ + *rb3_reg = 0x01; + /* actually, only a few cycles delay are required... */ + Sleep(1000000); + /* clear reset bit */ + *rb3_reg = 0x00; +} + +static void reset_piccolo(const struct ConfigDev *cd) +{ + volatile u_char *piccolo_reg = (u_char *)(cd->cd_BoardAddr+0x8000); + + /* FN: the same stuff as above, for the Piccolo board. */ + /* this also has the side effect of resetting the board's */ + /* output selection logic to use the Amiga's display in single */ + /* monitor systems - which is currently what we want. */ + + /* set RESET bit in special function register */ + *piccolo_reg = 0x01; + /* actually, only a few cycles delay are required... */ + Sleep(1000000); + /* clear reset bit */ + *piccolo_reg = 0x51; +} + +static void reset_sd64(const struct ConfigDev *cd) +{ + volatile u_char *sd64_reg = (u_char *)(cd->cd_BoardAddr+0x8000); + + /* FN: the same stuff as above, for the SD64 board. */ + /* just as on the Piccolo, this also resets the monitor switch */ + + /* set RESET bit in special function register */ + *sd64_reg = 0x1f; + /* actually, only a few cycles delay are required... */ + Sleep(1000000); + /* clear reset bit AND switch monitor bit (0x20) */ + *sd64_reg = 0x4f; +} + +static void reset_ariadne(const struct ConfigDev *cd) +{ + volatile u_short *lance_rdp = (u_short *)(cd->cd_BoardAddr+0x0370); + volatile u_short *lance_rap = (u_short *)(cd->cd_BoardAddr+0x0372); + volatile u_short *lance_reset = (u_short *)(cd->cd_BoardAddr+0x0374); + + volatile u_char *pit_paddr = (u_char *)(cd->cd_BoardAddr+0x1004); + volatile u_char *pit_pbddr = (u_char *)(cd->cd_BoardAddr+0x1006); + volatile u_char *pit_pacr = (u_char *)(cd->cd_BoardAddr+0x100b); + volatile u_char *pit_pbcr = (u_char *)(cd->cd_BoardAddr+0x100e); + volatile u_char *pit_psr = (u_char *)(cd->cd_BoardAddr+0x101a); + + u_short in; + + Disable(); + + /* + * Reset the Ethernet part (Am79C960 PCnet-ISA) + */ + + in = *lance_reset; /* Reset Chip on Read Access */ + *lance_rap = 0x0000; /* PCnet-ISA Controller Status (CSR0) */ + *lance_rdp = 0x0400; /* STOP */ + + /* + * Reset the Parallel part (MC68230 PI/T) + */ + + *pit_pacr &= 0xfd; /* Port A Control Register */ + *pit_pbcr &= 0xfd; /* Port B Control Register */ + *pit_psr = 0x05; /* Port Status Register */ + *pit_paddr = 0x00; /* Port A Data Direction Register */ + *pit_pbddr = 0x00; /* Port B Data Direction Register */ + + Enable(); +} + +static void reset_hydra(const struct ConfigDev *cd) +{ + volatile u_char *nic_cr = (u_char *)(cd->cd_BoardAddr+0xffe1); + volatile u_char *nic_isr = (u_char *)(cd->cd_BoardAddr+0xffe1 + 14); + int n = 5000; + + Disable(); + + *nic_cr = 0x21; /* nic command register: software reset etc. */ + while(((*nic_isr & 0x80) == 0) && --n) /* wait for reset to complete */ + ; + + Enable(); +} + +#if 0 +static void reset_a2060(const struct ConfigDev *cd) +{ +#error reset_a2060: not yet implemented +} +#endif diff --git a/arch/m68k/boot/amiga/linuxboot.h b/arch/m68k/boot/amiga/linuxboot.h new file mode 100644 index 000000000..2331a994d --- /dev/null +++ b/arch/m68k/boot/amiga/linuxboot.h @@ -0,0 +1,427 @@ +/* + * linux/arch/m68k/boot/amiga/linuxboot.h -- Generic routine to boot Linux/m68k + * on Amiga, used by both Amiboot and + * Amiga-Lilo. + * + * Created 1996 by Geert Uytterhoeven + * + * + * This file is based on the original bootstrap code (bootstrap.c): + * + * Copyright (C) 1993, 1994 Hamish Macdonald + * Greg Harp + * + * with work by Michael Rausch + * Geert Uytterhoeven + * Frank Neumann + * Andreas Schwab + * + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + +#include <asm/setup.h> +#include <asm/zorro.h> + + + /* + * Amiboot Version + */ + +#define AMIBOOT_VERSION "4.0" + + + /* + * Parameters passed to linuxboot() + */ + +struct linuxboot_args { + const char *kernelname; + const char *ramdiskname; + const char *commandline; + int debugflag; + int keep_video; + int reset_boards; + void (*puts)(const char *str); + long (*getchar)(void); + void (*putchar)(char c); + void (*printf)(const char *fmt, ...); + int (*open)(const char *path); + int (*seek)(int fd, int offset); + int (*read)(int fd, char *buf, int count); + void (*close)(int fd); + int (*filesize)(const char *path); + void (*sleep)(u_long micros); + int (*modify_bootinfo)(struct bootinfo *bi); +}; + + + /* + * Boot the Linux/m68k Operating System + */ + +extern u_long linuxboot(const struct linuxboot_args *args); + + + /* + * Amiga Models + */ + +extern const char *amiga_models[]; +extern const u_long first_amiga_model; +extern const u_long last_amiga_model; + + + /* + * Exec Library Definitions + */ + +#define TRUE (1) +#define FALSE (0) + + +struct List { + struct Node *lh_Head; + struct Node *lh_Tail; + struct Node *lh_TailPred; + u_char lh_Type; + u_char l_pad; +}; + +struct MemChunk { + struct MemChunk *mc_Next; /* pointer to next chunk */ + u_long mc_Bytes; /* chunk byte size */ +}; + +#define MEMF_PUBLIC (1<<0) +#define MEMF_CHIP (1<<1) +#define MEMF_FAST (1<<2) +#define MEMF_LOCAL (1<<8) +#define MEMF_CLEAR (1<<16) + +struct MemHeader { + struct Node mh_Node; + u_short mh_Attributes; /* characteristics of this region */ + struct MemChunk *mh_First; /* first free region */ + void *mh_Lower; /* lower memory bound */ + void *mh_Upper; /* upper memory bound+1 */ + u_long mh_Free; /* total number of free bytes */ +}; + +struct ExecBase { + u_char fill1[20]; + u_short Version; + u_char fill2[274]; + u_short AttnFlags; + u_char fill3[24]; + struct List MemList; + u_char fill4[194]; + u_char VBlankFrequency; + u_char PowerSupplyFrequency; + u_char fill5[36]; + u_long ex_EClockFrequency; + u_char fill6[60]; +}; + +#define AFB_68020 (1) +#define AFF_68020 (1<<AFB_68020) +#define AFB_68030 (2) +#define AFF_68030 (1<<AFB_68030) +#define AFB_68040 (3) +#define AFF_68040 (1<<AFB_68040) +#define AFB_68881 (4) +#define AFF_68881 (1<<AFB_68881) +#define AFB_68882 (5) +#define AFF_68882 (1<<AFB_68882) +#define AFB_FPU40 (6) /* ONLY valid if AFB_68040 or AFB_68060 */ +#define AFF_FPU40 (1<<AFB_FPU40) /* is set; also set for 68060 FPU */ +#define AFB_68060 (7) +#define AFF_68060 (1<<AFB_68060) + +struct Resident; + + + /* + * Graphics Library Definitions + */ + +struct GfxBase { + u_char fill1[20]; + u_short Version; + u_char fill2[194]; + u_short NormalDisplayRows; + u_short NormalDisplayColumns; + u_char fill3[16]; + u_char ChipRevBits0; + u_char fill4[307]; +}; + +#define GFXB_HR_AGNUS (0) +#define GFXF_HR_AGNUS (1<<GFXB_HR_AGNUS) +#define GFXB_HR_DENISE (1) +#define GFXF_HR_DENISE (1<<GFXB_HR_DENISE) +#define GFXB_AA_ALICE (2) +#define GFXF_AA_ALICE (1<<GFXB_AA_ALICE) +#define GFXB_AA_LISA (3) +#define GFXF_AA_LISA (1<<GFXB_AA_LISA) + + /* + * HiRes(=Big) Agnus present; i.e. + * 1MB chipmem, big blits (none of interest so far) and programmable sync + */ +#define GFXG_OCS (GFXF_HR_AGNUS) + /* + * HiRes Agnus/Denise present; we are running on ECS + */ +#define GFXG_ECS (GFXF_HR_AGNUS|GFXF_HR_DENISE) + /* + * Alice and Lisa present; we are running on AGA + */ +#define GFXG_AGA (GFXF_AA_ALICE|GFXF_AA_LISA) + +#define SETCHIPREV_BEST (0xffffffff) +#define HIRES (0x8000) + +struct View; + + + /* + * Amiga Shared Library/Device Functions + */ + +extern const struct ExecBase *SysBase; + +#define LVOAllocMem (-0xc6) +#define LVOAllocVec (-0x2ac) +#define LVOCacheControl (-0x288) +#define LVODisable (-0x78) +#define LVOEnable (-0x7e) +#define LVOFindResident (-0x60) +#define LVOFreeMem (-0xd2) +#define LVOFreeVec (-0x2b2) +#define LVOOpenresource (-0x1f2) +#define LVOSuperState (-0x96) +#define LVOSupervisor (-0x1e) + +static __inline void *AllocMem(u_long byteSize, u_long requirements) +{ + register void *_res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register u_long d0 __asm("d0") = byteSize; + register u_long d1 __asm("d1") = requirements; + + __asm __volatile ("jsr a6@(-0xc6)" + : "=r" (_res) + : "r" (_base), "r" (d0), "r" (d1) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + +static __inline void *AllocVec(u_long byteSize, u_long requirements) +{ + register void *_res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register u_long d0 __asm("d0") = byteSize; + register u_long d1 __asm("d1") = requirements; + + __asm __volatile ("jsr a6@(-0x2ac)" + : "=r" (_res) + : "r" (_base), "r" (d0), "r" (d1) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + +static __inline u_long CacheControl(u_long cacheBits, u_long cacheMask) +{ + register u_long _res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register u_long d0 __asm("d0") = cacheBits; + register u_long d1 __asm("d1") = cacheMask; + + __asm __volatile ("jsr a6@(-0x288)" + : "=r" (_res) + : "r" (_base), "r" (d0), "r" (d1) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + +static __inline void Disable(void) +{ + register const struct ExecBase *_base __asm("a6") = SysBase; + + __asm __volatile ("jsr a6@(-0x78)" + : /* no output */ + : "r" (_base) + : "a0", "a1", "d0", "d1", "memory"); +} + +static __inline void Enable(void) +{ + register const struct ExecBase *_base __asm("a6") = SysBase; + + __asm __volatile ("jsr a6@(-0x7e)" + : /* no output */ + : "r" (_base) + : "a0", "a1", "d0", "d1", "memory"); +} + +static __inline struct Resident *FindResident(const u_char *name) +{ + register struct Resident *_res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register const u_char *a1 __asm("a1") = name; + + __asm __volatile ("jsr a6@(-0x60)" + : "=r" (_res) + : "r" (_base), "r" (a1) + : "a0", "a1", "d0", "d1", "memory"); + return _res; +} + +static __inline void FreeMem(void *memoryBlock, u_long byteSize) +{ + register const struct ExecBase *_base __asm("a6") = SysBase; + register void *a1 __asm("a1") = memoryBlock; + register u_long d0 __asm("d0") = byteSize; + + __asm __volatile ("jsr a6@(-0xd2)" + : /* no output */ + : "r" (_base), "r" (a1), "r" (d0) + : "a0", "a1", "d0", "d1", "memory"); +} + +static __inline void FreeVec(void *memoryBlock) +{ + register const struct ExecBase *_base __asm("a6") = SysBase; + register void *a1 __asm("a1") = memoryBlock; + + __asm __volatile ("jsr a6@(-0x2b2)" + : /* no output */ + : "r" (_base), "r" (a1) + : "a0", "a1", "d0", "d1", "memory"); +} + +static __inline void *OpenResource(const u_char *resName) +{ + register void *_res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register const u_char *a1 __asm("a1") = resName; + + __asm __volatile ("jsr a6@(-0x1f2)" + : "=r" (_res) + : "r" (_base), "r" (a1) + : "a0", "a1", "d0", "d1", "memory"); + return _res; +} + +static __inline void *SuperState(void) +{ + register void *_res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + + __asm __volatile ("jsr a6@(-0x96)" + : "=r" (_res) + : "r" (_base) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + +static __inline u_long Supervisor(u_long (*userfunc)(void)) +{ + register u_long _res __asm("d0"); + register const struct ExecBase *_base __asm("a6") = SysBase; + register u_long (*d7)() __asm("d7") = userfunc; + + __asm __volatile ("exg d7,a5;" + "jsr a6@(-0x1e);" + "exg d7,a5" + : "=r" (_res) + : "r" (_base), "r" (d7) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + + +extern const struct ExpansionBase *ExpansionBase; + +#define LVOFindConfigDev (-0x48) + +static __inline struct ConfigDev *FindConfigDev(struct ConfigDev *oldConfigDev, + long manufacturer, long product) +{ + register struct ConfigDev *_res __asm("d0"); + register const struct ExpansionBase *_base __asm("a6") = ExpansionBase; + register struct ConfigDev *a0 __asm("a0") = oldConfigDev; + register long d0 __asm("d0") = manufacturer; + register long d1 __asm("d1") = product; + + __asm __volatile ("jsr a6@(-0x48)" + : "=r" (_res) + : "r" (_base), "r" (a0), "r" (d0), "r" (d1) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + + +extern const struct GfxBase *GfxBase; + +#define LVOLoadView (-0xde) +#define LVOSetChipRev (-0x378) + +static __inline void LoadView(struct View *view) +{ + register const struct GfxBase *_base __asm("a6") = GfxBase; + register struct View *a1 __asm("a1") = view; + + __asm __volatile ("jsr a6@(-0xde)" + : /* no output */ + : "r" (_base), "r" (a1) + : "a0", "a1", "d0", "d1", "memory"); +} + +static __inline u_long SetChipRev(u_long want) +{ + register u_long _res __asm("d0"); + register const struct GfxBase *_base __asm("a6") = GfxBase; + register u_long d0 __asm("d0") = want; + + __asm __volatile ("jsr a6@(-0x378)" + : "=r" (_res) + : "r" (_base), "r" (d0) + : "a0", "a1", "d0", "d1", "memory"); + return(_res); +} + + + /* + * Bootstrap Support Functions + */ + +static __inline void disable_mmu(void) +{ + if (SysBase->AttnFlags & AFF_68040) + __asm __volatile ("moveq #0,d0;" + ".long 0x4e7b0003;" /* movec d0,tc */ + ".long 0x4e7b0004;" /* movec d0,itt0 */ + ".long 0x4e7b0005;" /* movec d0,itt1 */ + ".long 0x4e7b0006;" /* movec d0,dtt0 */ + ".long 0x4e7b0007" /* movec d0,dtt1 */ + : /* no outputs */ + : /* no inputs */ + : "d0"); + else { + __asm __volatile ("subl #4,sp;" + "pmove tc,sp@;" + "bclr #7,sp@;" + "pmove sp@,tc;" + "addl #4,sp"); + if (SysBase->AttnFlags & AFF_68030) + __asm __volatile ("clrl sp@-;" + ".long 0xf0170800;" /* pmove sp@,tt0 */ + ".long 0xf0170c00;" /* pmove sp@,tt1 */ + "addql #4,sp"); + } +} diff --git a/arch/m68k/boot/atari/bootp.c b/arch/m68k/boot/atari/bootp.c new file mode 100644 index 000000000..db4fc284f --- /dev/null +++ b/arch/m68k/boot/atari/bootp.c @@ -0,0 +1,793 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "bootp.h" + + +/* --------------------------------------------------------------------- */ +/* Protocol Header Structures */ + +struct etherhdr { + HWADDR dst_addr; + HWADDR src_addr; + unsigned short type; +}; + +struct arphdr { + unsigned short hrd; /* format of hardware address */ + unsigned short pro; /* format of protocol address */ + unsigned char hln; /* length of hardware address */ + unsigned char pln; /* length of protocol address */ + unsigned short op; /* ARP opcode (command) */ + unsigned char addr[0]; /* addresses (var len) */ +}; + +struct iphdr { + unsigned char version : 4; + unsigned char ihl : 4; + unsigned char tos; + unsigned short tot_len; + unsigned short id; + unsigned short frag_off; + unsigned char ttl; + unsigned char protocol; + unsigned short chksum; + IPADDR src_addr; + IPADDR dst_addr; +}; + +struct udphdr { + unsigned short src_port; + unsigned short dst_port; + unsigned short len; + unsigned short chksum; +}; + +struct bootp { + unsigned char op; /* packet opcode type */ + unsigned char htype; /* hardware addr type */ + unsigned char hlen; /* hardware addr length */ + unsigned char hops; /* gateway hops */ + unsigned long xid; /* transaction ID */ + unsigned short secs; /* seconds since boot began */ + unsigned short unused; + IPADDR ciaddr; /* client IP address */ + IPADDR yiaddr; /* 'your' IP address */ + IPADDR siaddr; /* server IP address */ + IPADDR giaddr; /* gateway IP address */ + unsigned char chaddr[16]; /* client hardware address */ + unsigned char sname[64]; /* server host name */ + unsigned char file[128]; /* boot file name */ + unsigned char vend[64]; /* vendor-specific area */ +}; + +struct tftp_req { + unsigned short opcode; + char name[512]; +}; + +struct tftp_data { + unsigned short opcode; + unsigned short nr; + unsigned char data[512]; +}; + +struct tftp_ack { + unsigned short opcode; + unsigned short nr; +}; + +struct tftp_error { + unsigned short opcode; + unsigned short errcode; + char str[512]; +}; + + +typedef struct { + struct etherhdr ether; + struct arphdr arp; +} ARP; + +typedef struct { + struct etherhdr ether; + struct iphdr ip; + struct udphdr udp; +} UDP; + +#define UDP_BOOTPS 67 +#define UDP_BOOTPC 68 +#define UDP_TFTP 69 + +typedef struct { + struct etherhdr ether; + struct iphdr ip; + struct udphdr udp; + struct bootp bootp; +} BOOTP; + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 +#define BOOTP_RETRYS 5 + +typedef struct { + struct etherhdr ether; + struct iphdr ip; + struct udphdr udp; + union tftp { + unsigned short opcode; + struct tftp_req req; + struct tftp_data data; + struct tftp_ack ack; + struct tftp_error error; + } tftp; +} TFTP; + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + + +/* --------------------------------------------------------------------- */ +/* Addresses */ + +static HWADDR MyHwaddr; +static HWADDR ServerHwaddr; +static IPADDR MyIPaddr; +static IPADDR ServerIPaddr; + +static IPADDR IP_Unknown_Addr = 0x00000000; +static IPADDR IP_Broadcast_Addr = 0xffffffff; +static HWADDR Eth_Broadcast_Addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +#define HZ 200 +#define _hz_200 (*(volatile unsigned long *)0x4ba) + + +/* --------------------------------------------------------------------- */ +/* Error Strings */ + +static char *ErrStr[] = { + "timeout", + "general Ethernet transmit error", + "general Ethernet receive error", + "Ethernet framing error", + "Ethernet overflow error", + "Ethernet CRC error" +}; + + +/* --------------------------------------------------------------------- */ +/* Kfile Emulation Definitions */ + +#define KFILE_CHUNK_BITS 16 /* chunk is 64 KB */ +#define KFILE_CHUNK_SIZE (1 << KFILE_CHUNK_BITS) +#define KFILE_CHUNK_MASK (KFILE_CHUNK_SIZE-1) +#define KFILE_N_CHUNKS (2*1024*1024/KFILE_CHUNK_SIZE) + +char *KFile[KFILE_N_CHUNKS]; +int KFileSize = 0; +int KFpos = 0; + + + + +/***************************** Prototypes *****************************/ + +static void free_kfile( void ); +static int bootp( char *image_name ); +static int tftp( char *image_name ); +static int udp_send( UDP *pkt, int len, int fromport, int toport ); +static unsigned short ip_checksum( struct iphdr *buf ); +static int udp_rcv( UDP *pkt, int *len, int fromport, int atport ); +static void print_ip( IPADDR addr ); +static void print_hw( HWADDR addr ); +static int check_ethif( void ); +static int eth_send( Packet *pkt, int len ); +static int eth_rcv( Packet *pkt, int *len ); + +/************************* End of Prototypes **************************/ + + + + +/* --------------------------------------------------------------------- */ +/* Interface to bootstrap.c */ + +/* get_remote_kernel(): + * Perform all necessary steps to get the kernel image + * from the boot server. If successfull (retval == 0), subsequent calls to + * kread() can access the data. + */ + +int get_remote_kernel( const char *kname /* optional */ ) + +{ char image_name[256]; + + /* Check if a Ethernet interface is present and determine the Ethernet + * address */ + if (check_ethif() < 0) { + printf( "No Ethernet interface found -- no remote boot possible.\n" ); + return( -1 ); + } + + /* Do a BOOTP request to find out our IP address and the kernel image's + * name; we also learn the IP and Ethernet address of our server */ + if (kname) + strcpy( image_name, kname ); + else + *image_name = 0; + if (bootp( image_name ) < 0) + return( -1 ); + + /* Now start a TFTP connection to receive the kernel image */ + if (tftp( image_name ) < 0) + return( -1 ); + + return( 0 ); +} + + +/* kread(), klseek(), kclose(): + * Functions for accessing the received kernel image like with read(), + * lseek(), close(). + */ + +int kread( int fd, void *buf, unsigned cnt ) + +{ unsigned done = 0; + + if (!KFileSize) + return( read( fd, buf, cnt ) ); + + if (KFpos + cnt > KFileSize) + cnt = KFileSize - KFpos; + + while( cnt > 0 ) { + unsigned chunk = KFpos >> KFILE_CHUNK_BITS; + unsigned endchunk = (chunk+1) << KFILE_CHUNK_BITS; + unsigned n = cnt; + + if (KFpos + n > endchunk) + n = endchunk - KFpos; + memcpy( buf, KFile[chunk] + (KFpos & KFILE_CHUNK_MASK), n ); + cnt -= n; + buf += n; + done += n; + KFpos += n; + } + + return( done ); +} + + +int klseek( int fd, int where, int whence ) + +{ + if (!KFileSize) + return( lseek( fd, where, whence ) ); + + switch( whence ) { + case SEEK_SET: + KFpos = where; + break; + case SEEK_CUR: + KFpos += where; + break; + case SEEK_END: + KFpos = KFileSize + where; + break; + default: + return( -1 ); + } + if (KFpos < 0) { + KFpos = 0; + return( -1 ); + } + else if (KFpos > KFileSize) { + KFpos = KFileSize; + return( -1 ); + } + + return( KFpos ); +} + + +int kclose( int fd ) + +{ + if (!KFileSize) + return( close( fd ) ); + + free_kfile(); + return( 0 ); +} + + +static void free_kfile( void ) + +{ int i; + + for( i = 0; i < KFILE_N_CHUNKS; ++i ) + if (KFile[i]) free( KFile[i] ); +} + + + +/* --------------------------------------------------------------------- */ +/* BOOTP Procedure */ + + +static int bootp( char *image_name ) + +{ BOOTP req; + Packet _reply; + BOOTP *reply = (BOOTP *)_reply; + static unsigned char mincookie[] = { 99, 130, 83, 99, 255 }; + unsigned long starttime, rancopy; + int err, len, retry; + + memset( (char *)&req, 0, sizeof(req) ); + /* Now fill in the packet... */ + req.bootp.op = BOOTREQUEST; + req.bootp.htype = 1; /* 10Mb/s Ethernet */ + req.bootp.hlen = 6; + memcpy( req.bootp.chaddr, &MyHwaddr, ETHADDRLEN ); + + /* Put in the minimal RFC1497 Magic cookie */ + memcpy( req.bootp.vend, mincookie, sizeof(mincookie) ); + /* Put the user precified bootfile name in place */ + memcpy( req.bootp.file, image_name, strlen(image_name)+1); + + starttime = _hz_200; + for( retry = 0; retry < BOOTP_RETRYS; ++retry ) { + + /* Initialize server addresses and own IP to defaults */ + ServerIPaddr = IP_Broadcast_Addr; /* 255.255.255.255 */ + MyIPaddr = IP_Unknown_Addr; /* 0.0.0.0 */ + memcpy( ServerHwaddr, Eth_Broadcast_Addr, ETHADDRLEN ); + + if (retry) + sleep( 3 ); + + req.bootp.xid = rancopy = _hz_200; + req.bootp.secs = (_hz_200 - starttime) / HZ; + + if ((err = udp_send( (UDP *)&req, sizeof(req.bootp), + UDP_BOOTPC, UDP_BOOTPS )) < 0) { + printf( "bootp send: %s\n", ErrStr[-err-1] ); + continue; + } + + if ((err = udp_rcv( (UDP *)reply, &len, + UDP_BOOTPS, UDP_BOOTPC )) < 0) { + printf( "bootp rcv: %s\n", ErrStr[-err-1] ); + continue; + } + if (len < sizeof(struct bootp)) { + printf( "received short BOOTP packet (%d bytes)\n", len ); + continue; + } + + if (reply->bootp.xid == rancopy) + /* Ok, got the answer */ + break; + printf( "bootp: xid mismatch\n" ); + } + if (retry >= BOOTP_RETRYS) { + printf( "No response from a bootp server\n" ); + return( -1 ); + } + + ServerIPaddr = reply->bootp.siaddr; + memcpy( ServerHwaddr, reply->ether.src_addr, ETHADDRLEN ); + printf( "\nBoot server is " ); + if (strlen(reply->bootp.sname) > 0) + printf( "%s, IP ", reply->bootp.sname ); + print_ip( ServerIPaddr ); + printf( ", HW address " ); + print_hw( ServerHwaddr ); + printf( "\n" ); + + MyIPaddr = reply->bootp.yiaddr; + printf( "My IP address is " ); + print_ip( MyIPaddr ); + printf( "\n" ); + + strcpy( image_name, reply->bootp.file ); + return( 0 ); +} + + +/* --------------------------------------------------------------------- */ +/* TFTP Procedure */ + + +static int tftp( char *image_name ) + +{ TFTP spkt; + Packet _rpkt; + TFTP *rpkt = (TFTP *)&_rpkt; + unsigned short mytid, rtid = 0; + int blk, retries, i, wpos, err, len, datalen; + static char rotchar[4] = { '|', '/', '-', '\\' }; + + retries = 5; + /* Construct and send a read request */ + repeat_req: + spkt.tftp.req.opcode = TFTP_RRQ; + strcpy( spkt.tftp.req.name, image_name ); + strcpy( spkt.tftp.req.name + strlen(spkt.tftp.req.name) + 1, "octet" ); + mytid = _hz_200 & 0xffff; + + if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.req.opcode) + + strlen(image_name) + 1 + + strlen( "octect" ) +1, + mytid, UDP_TFTP )) < 0) { + printf( "TFTP RREQ: %s\n", ErrStr[-err-1] ); + if (--retries > 0) + goto repeat_req; + return( -1 ); + } + + retries = 5; + for( i = 0; i < KFILE_N_CHUNKS; ++i ) + KFile[i] = NULL; + wpos = 0; + printf( "Receiving kernel image %s:\n", image_name ); + + for( blk = 1; ; ++blk ) { + + repeat_data: + if ((err = udp_rcv( (UDP *)rpkt, &len, rtid, mytid )) < 0) { + printf( "TFTP rcv: %s\n", ErrStr[-err-1] ); + if (--retries > 0) + goto repeat_data; + goto err; + } + if (rtid == 0) + /* Store the remote port at the first packet received */ + rtid = rpkt->udp.src_port; + + if (rpkt->tftp.opcode == TFTP_ERROR) { + if (strlen(rpkt->tftp.error.str) > 0) + printf( "TFTP error: %s\n", rpkt->tftp.error.str ); + else + printf( "TFTP error #%d (no description)\n", + rpkt->tftp.error.errcode ); + goto err; + } + else if (rpkt->tftp.opcode != TFTP_DATA) { + printf( "Bad TFTP packet type: %d\n", rpkt->tftp.opcode ); + if (--retries > 0) + goto repeat_data; + goto err; + } + + if (rpkt->tftp.data.nr != blk) { + /* doubled data packet; ignore it */ + goto repeat_data; + } + datalen = len - sizeof(rpkt->tftp.data.opcode) - + sizeof(rpkt->tftp.data.nr); + + /* store data */ + if (datalen > 0) { + int chunk = wpos >> KFILE_CHUNK_BITS; + if (chunk >= KFILE_N_CHUNKS) { + printf( "TFTP: file too large! Aborting.\n" ); + out_of_mem: + spkt.tftp.error.opcode = TFTP_ERROR; + spkt.tftp.error.errcode = 3; + strcpy( spkt.tftp.error.str, "Out of memory" ); + udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack), mytid, rtid ); + goto err; + } + if (!KFile[chunk]) { + if (!(KFile[chunk] = malloc( KFILE_CHUNK_SIZE ))) { + printf( "TFTP: Out of memory for kernel image\n" ); + goto out_of_mem; + } + } + memcpy( KFile[chunk] + (wpos & KFILE_CHUNK_MASK), + rpkt->tftp.data.data, datalen ); + wpos += datalen; + +#define DISPLAY_BITS 13 + if ((wpos & ((1 << DISPLAY_BITS)-1)) == 0) { + printf( "\r %c %7d Bytes ", + rotchar[(wpos>>DISPLAY_BITS)&3], wpos ); + fflush( stdout ); + } + } + + /* Send ACK packet */ + repeat_ack: + spkt.tftp.ack.opcode = TFTP_ACK; + spkt.tftp.ack.nr = blk; + if ((err = udp_send( (UDP *)&spkt, sizeof(spkt.tftp.ack), + mytid, rtid )) < 0) { + printf( "TFTP ACK: %s\n", ErrStr[-err-1] ); + if (--retries > 0) + goto repeat_ack; + goto err; + } + + if (datalen < 512) { + /* This was the last packet */ + printf( "\r %7d Bytes done\n\n", wpos ); + break; + } + + retries = 5; + } + + KFileSize = wpos; + return( 0 ); + + err: + free_kfile(); + return( -1 ); +} + + + +/* --------------------------------------------------------------------- */ +/* UDP/IP Protocol Quick Hack Implementation */ + + +static int udp_send( UDP *pkt, int len, int fromport, int toport ) + +{ + /* UDP layer */ + pkt->udp.src_port = fromport; + pkt->udp.dst_port = toport; + pkt->udp.len = (len += sizeof(struct udphdr)); + pkt->udp.chksum = 0; /* Too lazy to calculate :-) */ + + /* IP layer */ + pkt->ip.version = 4; + pkt->ip.ihl = 5; + pkt->ip.tos = 0; + pkt->ip.tot_len = (len += sizeof(struct iphdr)); + pkt->ip.id = 0; + pkt->ip.frag_off = 0; + pkt->ip.ttl = 255; + pkt->ip.protocol = 17; /* UDP */ + pkt->ip.src_addr = MyIPaddr; + pkt->ip.dst_addr = ServerIPaddr; + pkt->ip.chksum = 0; + pkt->ip.chksum = ip_checksum( &pkt->ip ); + + /* Ethernet layer */ + memcpy( &pkt->ether.dst_addr, ServerHwaddr, ETHADDRLEN ); + memcpy( &pkt->ether.src_addr, MyHwaddr, ETHADDRLEN ); + pkt->ether.type = 0x0800; + len += sizeof(struct etherhdr); + + return( eth_send( (Packet *)pkt, len ) ); +} + + +static unsigned short ip_checksum( struct iphdr *buf ) + +{ unsigned long sum = 0, wlen = 5; + + __asm__ ("subqw #1,%2\n" + "1:\t" + "movel %1@+,%/d0\n\t" + "addxl %/d0,%0\n\t" + "dbra %2,1b\n\t" + "movel %0,%/d0\n\t" + "swap %/d0\n\t" + "addxw %/d0,%0\n\t" + "clrw %/d0\n\t" + "addxw %/d0,%0" + : "=d" (sum), "=a" (buf), "=d" (wlen) + : "0" (sum), "1" (buf), "2" (wlen) + : "d0"); + return( (~sum) & 0xffff ); +} + + +static int udp_rcv( UDP *pkt, int *len, int fromport, int atport ) + +{ int err; + + repeat: + if ((err = eth_rcv( (Packet *)pkt, len ))) + return( err ); + + /* Ethernet layer */ + if (pkt->ether.type == 0x0806) { + /* ARP */ + ARP *pk = (ARP *)pkt; + unsigned char *shw, *sip, *thw, *tip; + + if (pk->arp.hrd != 1 || pk->arp.pro != 0x0800 || + pk->arp.op != 1 || MyIPaddr == IP_Unknown_Addr) + /* Wrong hardware type or protocol; or reply -> ignore */ + goto repeat; + shw = pk->arp.addr; + sip = shw + pk->arp.hln; + thw = sip + pk->arp.pln; + tip = thw + pk->arp.hln; + + if (memcmp( tip, &MyIPaddr, pk->arp.pln ) == 0) { + memcpy( thw, shw, pk->arp.hln ); + memcpy( tip, sip, pk->arp.pln ); + memcpy( shw, &MyHwaddr, pk->arp.hln ); + memcpy( sip, &MyIPaddr, pk->arp.pln ); + + memcpy( &pk->ether.dst_addr, thw, ETHADDRLEN ); + memcpy( &pk->ether.src_addr, &MyHwaddr, ETHADDRLEN ); + eth_send( (Packet *)pk, *len ); + } + goto repeat; + } + else if (pkt->ether.type != 0x0800) { + printf( "Unknown Ethernet packet type %04x received\n", + pkt->ether.type ); + goto repeat; + } + + /* IP layer */ + if (MyIPaddr != IP_Unknown_Addr && pkt->ip.dst_addr != MyIPaddr) { + printf( "Received packet for wrong IP address\n" ); + goto repeat; + } + if (ServerIPaddr != IP_Unknown_Addr && + ServerIPaddr != IP_Broadcast_Addr && + pkt->ip.src_addr != ServerIPaddr) { + printf( "Received packet from wrong server\n" ); + goto repeat; + } + /* If IP header is longer than 5 longs, delete the options */ + if (pkt->ip.ihl > 5) { + char *udpstart = (char *)((long *)&pkt->ip + pkt->ip.ihl); + memmove( &pkt->udp, udpstart, *len - (udpstart-(char *)pkt) ); + } + + /* UDP layer */ + if (fromport != 0 && pkt->udp.src_port != fromport) { + printf( "Received packet from wrong port %d\n", pkt->udp.src_port ); + goto repeat; + } + if (pkt->udp.dst_port != atport) { + printf( "Received packet at wrong port %d\n", pkt->udp.dst_port ); + goto repeat; + } + + *len = pkt->udp.len - sizeof(struct udphdr); + return( 0 ); +} + + +/* --------------------------------------------------------------------- */ +/* Address Printing */ + + +static void print_ip( IPADDR addr ) + +{ + printf( "%ld.%ld.%ld.%ld", + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + addr & 0xff ); +} + + +static void print_hw( HWADDR addr ) + +{ + printf( "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); +} + + +/* --------------------------------------------------------------------- */ +/* Ethernet Interface Abstraction Layer */ + + +#ifdef ETHLL_LANCE +#include "ethlance.h" +#endif + +static ETHIF_SWITCH *PossibleInterfaces[] = { +#ifdef ETHLL_LANCE + &LanceSwitch, +#endif +}; + +#define N_PossibleInterfaces (sizeof(PossibleInterfaces)/sizeof(*PossibleInterfaces)) + +/* Detected interface */ +static ETHIF_SWITCH *Ethif = NULL; + + +static int check_ethif( void ) + +{ int i; + + /* Check for configured interfaces */ + Ethif = NULL; + for( i = 0; i < N_PossibleInterfaces; ++i ) { + if (PossibleInterfaces[i]->probe() >= 0) { + Ethif = PossibleInterfaces[i]; + break; + } + } + if (!Ethif) + return( -1 ); + + if (Ethif->init() < 0) { + printf( "Ethernet interface initialization failed\n" ); + return( -1 ); + } + Ethif->get_hwaddr( &MyHwaddr ); + return( 0 ); +} + + +static int eth_send( Packet *pkt, int len ) + +{ + return( Ethif->snd( pkt, len )); +} + + +static int eth_rcv( Packet *pkt, int *len ) + +{ + return( Ethif->rcv( pkt, len )); +} + + +#if 0 +static void dump_packet( UDP *pkt ) + +{ int i, l; + unsigned char *p; + + printf( "Packet dump:\n" ); + + printf( "Ethernet header:\n" ); + printf( " dst addr: " ); print_hw( pkt->ether.dst_addr ); printf( "\n" ); + printf( " src addr: " ); print_hw( pkt->ether.src_addr ); printf( "\n" ); + printf( " type: %04x\n", pkt->ether.type ); + + printf( "IP header:\n" ); + printf( " version: %d\n", pkt->ip.version ); + printf( " hdr len: %d\n", pkt->ip.ihl ); + printf( " tos: %d\n", pkt->ip.tos ); + printf( " tot_len: %d\n", pkt->ip.tot_len ); + printf( " id: %d\n", pkt->ip.id ); + printf( " frag_off: %d\n", pkt->ip.frag_off ); + printf( " ttl: %d\n", pkt->ip.ttl ); + printf( " prot: %d\n", pkt->ip.protocol ); + printf( " src addr: " ); print_ip( pkt->ip.src_addr ); printf( "\n" ); + printf( " dst addr: " ); print_ip( pkt->ip.dst_addr ); printf( "\n" ); + + printf( "UDP header:\n" ); + printf( " src port: %d\n", pkt->udp.src_port ); + printf( " dst port: %d\n", pkt->udp.dst_port ); + printf( " len: %d\n", pkt->udp.len ); + + printf( "Data:" ); + l = pkt->udp.len - sizeof(pkt->udp); + p = (unsigned char *)&pkt->udp + sizeof(pkt->udp); + for( i = 0; i < l; ++i ) { + if ((i % 32) == 0) + printf( "\n %04x ", i ); + printf( "%02x ", *p ); + } + printf( "\n" ); +} +#endif diff --git a/arch/m68k/boot/atari/bootp.h b/arch/m68k/boot/atari/bootp.h new file mode 100644 index 000000000..02988edaf --- /dev/null +++ b/arch/m68k/boot/atari/bootp.h @@ -0,0 +1,44 @@ +#ifndef _bootp_h +#define _bootp_h + +/* --------------------------------------------------------------------- */ +/* Ethernet Definitions */ + +#define PKTLEN 1544 +typedef unsigned char Packet[PKTLEN]; + +#define ETHADDRLEN 6 +typedef unsigned char HWADDR[ETHADDRLEN]; + +typedef struct { + int (*probe)( void ); + int (*init)( void ); + void (*get_hwaddr)( HWADDR *addr ); + int (*snd)( Packet *pkt, int len ); + int (*rcv)( Packet *pkt, int *len ); +} ETHIF_SWITCH; + + +/* error codes */ +#define ETIMEO -1 /* Timeout */ +#define ESEND -2 /* General send error (carrier, abort, ...) */ +#define ERCV -3 /* General receive error */ +#define EFRAM -4 /* Framing error */ +#define EOVERFL -5 /* Overflow (too long packet) */ +#define ECRC -6 /* CRC error */ + + +typedef unsigned long IPADDR; + + +/***************************** Prototypes *****************************/ + +int get_remote_kernel( const char *kname ); +int kread( int fd, void *buf, unsigned cnt ); +int klseek( int fd, int where, int whence ); +int kclose( int fd ); + +/************************* End of Prototypes **************************/ + +#endif /* _bootp_h */ + diff --git a/arch/m68k/boot/atari/bootstrap.c b/arch/m68k/boot/atari/bootstrap.c new file mode 100644 index 000000000..dbf5fada0 --- /dev/null +++ b/arch/m68k/boot/atari/bootstrap.c @@ -0,0 +1,1083 @@ +/* +** bootstrap.c -- Load and launch the Atari Linux kernel +** +** Copyright 1993 by Arjan Knor +** +** This file is subject to the terms and conditions of the GNU General Public +** License. See the file COPYING in the main directory of this archive +** for more details. +** +** History: +** 10 Dec 1995 BOOTP/TFTP support (Roman) +** 03 Oct 1995 Allow kernel to be loaded to TT ram again (Andreas) +** 11 Jul 1995 Add support for ELF format kernel (Andreas) +** 16 Jun 1995 Adapted to Linux 1.2: kernel always loaded into ST ram +** (Andreas) +** 14 Nov 1994 YANML (Yet Another New Memory Layout :-) kernel +** start address is KSTART_ADDR + PAGE_SIZE, this +** does not need the ugly kludge with +** -fwritable-strings (++andreas) +** 09 Sep 1994 Adapted to the new memory layout: All the boot_info entry +** mentions all ST-Ram and the mover is located somewhere +** in the middle of memory (roman) +** Added the default arguments file known from the other +** bootstrap version +** 19 Feb 1994 Changed everything so that it works? (rdv) +** 14 Mar 1994 New mini-copy routine used (rdv) +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <string.h> +#include <ctype.h> +#include "sysvars.h" +#include <osbind.h> +#include <sys/types.h> +#include <sys/file.h> + +/* linux specific include files */ +#include <linux/a.out.h> +#include <linux/elf.h> +#include <asm/page.h> + +#define _LINUX_TYPES_H /* Hack to prevent including <linux/types.h> */ +#include <asm/setup.h> + +/* Atari bootstrap include file */ +#include "bootstrap.h" + +#define MIN_RAMSIZE (3) /* 3 MB */ +#define TEMP_STACKSIZE 256 + +extern char *optarg; +extern int optind; +static void get_default_args( int *argc, char ***argv ); +/* This is missing in <unistd.h> */ +extern int sync (void); + +struct bootinfo bi; +u_long *cookiejar; +u_long userstk; + +/* getcookie -- function to get the value of the given cookie. */ +static int getcookie(char *cookie, u_long *value) +{ + int i = 0; + + while(cookiejar[i] != 0L) { + if(cookiejar[i] == *(u_long *)cookie) { + *value = cookiejar[i + 1]; + return 1; + } + i += 2; + } + return -1; +} + +static void usage(void) +{ + fprintf(stderr, "Usage:\n" + "\tbootstrap [-dst] [-k kernel_executable] [-r ramdisk_file]" + " [option...]\n"); + exit(EXIT_FAILURE); +} + +/* + * Copy the kernel and the ramdisk to their final resting places. + * + * I assume that the kernel data and the ramdisk reside somewhere + * in the middle of the memory. + * + * This program itself should be somewhere in the first 4096 bytes of memory + * where the kernel never will be. In this way it can never be overwritten + * by itself. + * + * At this point the registers have: + * a0: the start of the final kernel + * a1: the start of the current kernel + * a2: the end of the final ramdisk + * a3: the end of the current ramdisk + * d0: the kernel size + * d1: the ramdisk size + */ +asm (" +.text +.globl _copyall, _copyallend +_copyall: + + movel a0,a4 /* save the start of the kernel for booting */ + +1: movel a1@+,a0@+ /* copy the kernel starting at the beginning */ + subql #4,d0 + jcc 1b + + tstl d1 + beq 3f + +2: movel a3@-,a2@- /* copy the ramdisk starting at the end */ + subql #4,d1 + jcc 2b + +3: jmp a4@ /* jump to the start of the kernel */ +_copyallend: +"); + +extern char copyall, copyallend; + + +/* Test for a Medusa: This is the only machine on which address 0 is + * writeable! + * ...err! On the Afterburner040 (for the Falcon) it's the same... So we do + * another test with 0x00ff82fe, that gives a bus error on the Falcon, but is + * in the range where the Medusa always asserts DTACK. + */ + +int test_medusa( void ) + +{ int rv = 0; + + __asm__ __volatile__ + ( "movel 0x8,a0\n\t" + "movel sp,a1\n\t" + "moveb 0x0,d1\n\t" + "movel #Lberr,0x8\n\t" + "moveq #0,%0\n\t" + "clrb 0x0\n\t" + "nop \n\t" + "moveb d1,0x0\n\t" + "nop \n\t" + "tstb 0x00ff82fe\n\t" + "nop \n\t" + "moveq #1,%0\n" + "Lberr:\t" + "movel a1,sp\n\t" + "movel a0,0x8" + : "=d" (rv) + : /* no inputs */ + : "d1", "a0", "a1", "memory" ); + + return( rv ); +} + + +/* Test if FPU instructions are executed in hardware, or if they're + emulated in software. For this, the F-line vector is temporarily + replaced. */ + +int test_software_fpu(void) +{ + int rv = 0; + + __asm__ __volatile__ + ( "movel 0x2c,a0\n\t" + "movel sp,a1\n\t" + "movel #Lfline,0x2c\n\t" + "moveq #1,%0\n\t" + "fnop \n\t" + "nop \n\t" + "moveq #0,%0\n" + "Lfline:\t" + "movel a1,sp\n\t" + "movel a0,0x2c" + : "=d" (rv) + : /* no inputs */ + : "a0", "a1" ); + + return rv; +} + + +void get_medusa_bank_sizes( u_long *bank1, u_long *bank2 ) + +{ static u_long save_addr; + u_long test_base, saved_contents[16]; +#define TESTADDR(i) (*((u_long *)((char *)test_base + i*8*MB))) +#define TESTPAT 0x12345678 + unsigned short oldflags; + int i; + + /* This ensures at least that none of the test addresses conflicts + * with the test code itself */ + test_base = ((unsigned long)&save_addr & 0x007fffff) | 0x20000000; + *bank1 = *bank2 = 0; + + /* Interrupts must be disabled because arbitrary addresses may be + * temporarily overwritten, even code of an interrupt handler */ + __asm__ __volatile__ ( "movew sr,%0; oriw #0x700,sr" : "=g" (oldflags) : ); + disable_cache(); + + /* save contents of the test addresses */ + for( i = 0; i < 16; ++i ) + saved_contents[i] = TESTADDR(i); + + /* write 0s into all test addresses */ + for( i = 0; i < 16; ++i ) + TESTADDR(i) = 0; + + /* test for bank 1 */ +#if 0 + /* This is Freddi's original test, but it didn't work. */ + TESTADDR(0) = TESTADDR(1) = TESTPAT; + if (TESTADDR(1) == TESTPAT) { + if (TESTADDR(2) == TESTPAT) + *bank1 = 8*MB; + else if (TESTADDR(3) == TESTPAT) + *bank1 = 16*MB; + else + *bank1 = 32*MB; + } + else { + if (TESTADDR(2) == TESTPAT) + *bank1 = 0; + else + *bank1 = 16*MB; + } +#else + TESTADDR(0) = TESTPAT; + if (TESTADDR(1) == TESTPAT) + *bank1 = 8*MB; + else if (TESTADDR(2) == TESTPAT) + *bank1 = 16*MB; + else if (TESTADDR(4) == TESTPAT) + *bank1 = 32*MB; + else + *bank1 = 64*MB; +#endif + + /* test for bank2 */ + if (TESTADDR(8) != 0) + *bank2 = 0; + else { + TESTADDR(8) = TESTPAT; + if (TESTADDR(9) != 0) { + if (TESTADDR(10) == TESTPAT) + *bank2 = 8*MB; + else + *bank2 = 32*MB; + } + else { + TESTADDR(9) = TESTPAT; + if (TESTADDR(10) == TESTPAT) + *bank2 = 16*MB; + else + *bank2 = 64*MB; + } + } + + /* restore contents of the test addresses and restore interrupt mask */ + for( i = 0; i < 16; ++i ) + TESTADDR(i) = saved_contents[i]; + __asm__ __volatile__ ( "movew %0,sr" : : "g" (oldflags) ); +} + +#undef TESTADDR +#undef TESTPAT + + +static int check_bootinfo_version(char *memptr) +{ + struct bootversion *bv = (struct bootversion *)memptr; + unsigned long version = 0; + int i, kernel_major, kernel_minor, boots_major, boots_minor; + + printf( "\n" ); + if (bv->magic == BOOTINFOV_MAGIC) { + for( i = 0; bv->machversions[i].machtype != 0; ++i ) { + if (bv->machversions[i].machtype == MACH_ATARI) { + version = bv->machversions[i].version; + break; + } + } + } + if (!version) + printf("Kernel has no bootinfo version info, assuming 0.0\n"); + + kernel_major = BI_VERSION_MAJOR(version); + kernel_minor = BI_VERSION_MINOR(version); + boots_major = BI_VERSION_MAJOR(ATARI_BOOTI_VERSION); + boots_minor = BI_VERSION_MINOR(ATARI_BOOTI_VERSION); + printf("Bootstrap's bootinfo version: %d.%d\n", + boots_major, boots_minor); + printf("Kernel's bootinfo version : %d.%d\n", + kernel_major, kernel_minor); + + if (kernel_major != boots_major) { + printf("\nThis bootstrap is too %s for this kernel!\n", + boots_major < kernel_major ? "old" : "new"); + return 0; + } + if (kernel_minor > boots_minor) { + printf("Warning: Bootinfo version of bootstrap and kernel differ!\n"); + printf(" Certain features may not work.\n"); + } + return 1; +} + + +#ifdef USE_BOOTP +# include "bootp.h" +#else +# define kread read +# define klseek lseek +# define kclose close +#endif + + +/* ++andreas: this must be inline due to Super */ +static inline void boot_exit (int) __attribute__ ((noreturn)); +static inline void boot_exit(int status) +{ + /* first go back to user mode */ + (void)Super(userstk); + getchar(); + exit(status); +} + +int main(int argc, char *argv[]) +{ + int debugflag = 0, ch, kfd, rfd = -1, i, ignore_ttram = 0; + int load_to_stram = 0; + char *ramdisk_name, *kernel_name, *memptr; + u_long ST_ramsize, TT_ramsize, memreq; + u_long cpu_type, fpu_type, mch_type, mint; + struct exec kexec; + int elf_kernel = 0; + Elf32_Ehdr kexec_elf; + Elf32_Phdr *kernel_phdrs = NULL; + u_long start_mem, mem_size, rd_size, text_offset = 0, kernel_size; +#ifdef USE_BOOTP + int prefer_bootp = 1, kname_set = 0; +#endif + + ramdisk_name = NULL; + kernel_name = "vmlinux"; + + /* print the startup message */ + puts("\fLinux/68k Atari Bootstrap version 1.8" +#ifdef USE_BOOTP + " (with BOOTP)" +#endif + ); + puts("Copyright 1993,1994 by Arjan Knor, Robert de Vries, Roman Hodek, Andreas Schwab\n"); + + /* ++roman: If no arguments on the command line, read them from + * file */ + if (argc == 1) + get_default_args( &argc, &argv ); + + /* machine is Atari */ + bi.machtype = MACH_ATARI; + + /* check arguments */ +#ifdef USE_BOOTP + while ((ch = getopt(argc, argv, "bdtsk:r:")) != EOF) +#else + while ((ch = getopt(argc, argv, "dtsk:r:")) != EOF) +#endif + switch (ch) { + case 'd': + debugflag = 1; + break; + case 't': + ignore_ttram = 1; + break; + case 's': + load_to_stram = 1; + break; + case 'k': + kernel_name = optarg; +#ifdef USE_BOOTP + kname_set = 1; +#endif + break; + case 'r': + ramdisk_name = optarg; + break; +#ifdef USE_BOOTP + case 'b': + prefer_bootp = 0; + break; +#endif + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + /* We have to access some system variables to get + * the information we need, so we must switch to + * supervisor mode first. + */ + userstk = Super(0L); + + /* get the info we need from the cookie-jar */ + cookiejar = *_p_cookies; + if(cookiejar == 0L) { + /* if we find no cookies, it's probably an ST */ + fprintf(stderr, "Error: No cookiejar found. Is this an ST?\n"); + boot_exit(EXIT_FAILURE); + } + + /* Exit if MiNT/MultiTOS is running. */ + if(getcookie("MiNT", &mint) != -1) + { + puts("Warning: MiNT is running\n"); +#if 0 + puts("Linux cannot be started when MiNT is running. Aborting...\n"); + boot_exit(EXIT_FAILURE); +#endif + } + + /* get _CPU, _FPU and _MCH */ + getcookie("_CPU", &cpu_type); + getcookie("_FPU", &fpu_type); + getcookie("_MCH", &mch_type); + + /* check if we are on a 68030/40 with FPU */ + if ((cpu_type != 30 && cpu_type != 40 && cpu_type != 60)) + { + puts("Machine type currently not supported. Aborting..."); + boot_exit(EXIT_FAILURE); + } + + switch(cpu_type) { + case 0: + case 10: break; + case 20: bi.cputype = CPU_68020; break; + case 30: bi.cputype = CPU_68030; break; + case 40: bi.cputype = CPU_68040; break; + case 60: bi.cputype = CPU_68060; break; + default: + fprintf(stderr, "Error: Unknown CPU type. Aborting...\n"); + boot_exit(EXIT_FAILURE); + break; + } + + printf("CPU: %ld; ", cpu_type + 68000); + printf("FPU: "); + + /* check for FPU; in case of a '040 or '060, don't look at _FPU itself, + * some software may set it to wrong values (68882 or the like) */ + if (cpu_type == 40) { + bi.cputype |= FPU_68040; + puts( "68040\n" ); + } + else if (cpu_type == 60) { + bi.cputype |= FPU_68060; + puts( "68060\n" ); + } + else { + switch ((fpu_type >> 16) & 7) { + case 0: + puts("not present\n"); + break; + case 1: + puts("SFP004 not supported. Assuming no FPU."); + break; + case 2: + /* try to determine real type */ + if (fpu_idle_frame_size () != 0x18) + goto m68882; + /* fall through */ + case 4: + bi.cputype |= FPU_68881; + puts("68881\n"); + break; + case 6: + m68882: + bi.cputype |= FPU_68882; + puts("68882\n"); + break; + default: + puts("Unknown FPU type. Assuming no FPU."); + break; + } + } + /* ++roman: If an FPU was announced in the cookie, test + whether it is a real hardware FPU or a software emulator! */ + if (bi.cputype & FPU_MASK) { + if (test_software_fpu()) { + bi.cputype &= ~FPU_MASK; + puts("FPU: software emulated. Assuming no FPU."); + } + } + + memset(&bi.bi_atari.hw_present, 0, sizeof(bi.bi_atari.hw_present)); + + /* Get the amounts of ST- and TT-RAM. */ + /* The size must be a multiple of 1MB. */ + i = 0; + + if (!test_medusa()) { + struct { + unsigned short version; /* version - currently 1 */ + unsigned long fr_start; /* start addr FastRAM */ + unsigned long fr_len; /* length FastRAM */ + } *magn_cookie; + struct { + unsigned long version; + unsigned long fr_start; /* start addr */ + unsigned long fr_len; /* length */ + } *fx_cookie; + + TT_ramsize = 0; + if (!ignore_ttram) { + /* "Original" or properly emulated TT-Ram */ + if (*ramtop) { + /* the 'ramtop' variable at 0x05a4 is not + * officially documented. We use it anyway + * because it is the only way to get the TTram size. + * (It is zero if there is no TTram.) + */ + bi.memory[i].addr = TT_RAM_BASE; + bi.memory[i].size = (*ramtop - TT_RAM_BASE) & ~(MB - 1); + TT_ramsize = bi.memory[i].size / MB; + i++; + printf("TT-RAM: %ld Mb; ", TT_ramsize); + } + + /* test for MAGNUM alternate RAM + * added 26.9.1995 M. Schwingen, rincewind@discworld.oche.de + */ + if (getcookie("MAGN", (u_long *)&magn_cookie) != -1) { + bi.memory[i].addr = magn_cookie->fr_start; + bi.memory[i].size = magn_cookie->fr_len & ~(MB - 1); + TT_ramsize += bi.memory[i].size / MB; + printf("MAGNUM alternate RAM: %ld Mb; ", bi.memory[i].size/MB); + i++; + } + + /* BlowUps FX */ + if (getcookie("BPFX", (u_long *)&fx_cookie) != -1 && fx_cookie) { + /* if fx is set (cookie call above), + * we assume that BlowUps FX-card + * is installed. (Nat!) + */ + bi.memory[i].addr = fx_cookie->fr_start; + bi.memory[i].size = fx_cookie->fr_len & ~(MB - 1); + printf("FX alternate RAM: %ld Mb; ", bi.memory[i].size/MB); + i++; + } + } + + bi.memory[i].addr = 0; + bi.memory[i].size = *phystop & ~(MB - 1); + ST_ramsize = bi.memory[i].size / MB; + i++; + printf("ST-RAM: %ld Mb\n", ST_ramsize ); + + bi.num_memory = i; + + if (load_to_stram && i > 1) { + /* Put ST-RAM first in the list of mem blocks */ + struct mem_info temp = bi.memory[i - 1]; + bi.memory[i - 1] = bi.memory[0]; + bi.memory[0] = temp; + } + } + else { + u_long bank1, bank2, medusa_st_ram; + + get_medusa_bank_sizes( &bank1, &bank2 ); + medusa_st_ram = *phystop & ~(MB - 1); + bank1 -= medusa_st_ram; + TT_ramsize = 0; + + bi.memory[i].addr = 0; + bi.memory[i].size = medusa_st_ram; + ST_ramsize = bi.memory[i].size / MB; + i++; + printf("Medusa pseudo ST-RAM from bank 1: %ld Mb; ", ST_ramsize ); + + if (!ignore_ttram && bank1 > 0) { + bi.memory[i].addr = 0x20000000 + medusa_st_ram; + bi.memory[i].size = bank1; + TT_ramsize += bank1; + i++; + printf("TT-RAM bank 1: %ld Mb; ", bank1/MB ); + } + + if (!ignore_ttram && bank2 > 0) { + bi.memory[i].addr = 0x24000000; + bi.memory[i].size = bank2; + TT_ramsize += bank2; + i++; + printf("TT-RAM bank 2: %ld Mb; ", bank2/MB ); + } + + bi.num_memory = i; + printf("\n"); + } + + /* verify that there is enough RAM; ST- and TT-RAM combined */ + if (ST_ramsize + TT_ramsize < MIN_RAMSIZE) { + puts("Not enough RAM. Aborting..."); + boot_exit(10); + } + +#if 0 + /* Get language/keyboard info */ + /* TODO: do we need this ? */ + /* Could be used to auto-select keyboard map later on. (rdv) */ + if (getcookie("_AKP",&language) == -1) + { + /* Get the language info from the OS-header */ + os_header = *_sysbase; + os_header = os_header->os_beg; + lang = (os_header->os_conf) >> 1; + printf("Language: "); + switch(lang) { + case HOL: puts("Dutch"); break; /* Own country first :-) */ + case USA: puts("American"); break; + case SWG: puts("Switzerland (German)"); break; + case FRG: puts("German"); break; + case FRA: puts("French"); break; + case SWF: puts("Switzerland (French)"); break; + case UK: puts("English"); break; + case SPA: puts("Spanish"); break; + case ITA: puts("Italian"); break; + case SWE: puts("Swedish"); break; + case TUR: puts("Turkey"); break; + case FIN: puts("Finnish"); break; + case NOR: puts("Norwegian"); break; + case DEN: puts("Danish"); break; + case SAU: puts("Saudi-Arabian"); break; + default: puts("Unknown"); break; + } + } + else + { + printf("Language: "); + switch(language & 0x0F) + { + case 1: printf("German "); break; + case 2: printf("French "); break; + case 4: printf("Spanish "); break; + case 5: printf("Italian "); break; + case 7: printf("Swiss French "); break; + case 8: printf("Swiss German "); break; + default: printf("English "); + } + printf("Keyboard type :"); + switch(language >> 8) + { + case 1: printf("German "); break; + case 2: printf("French "); break; + case 4: printf("Spanish "); break; + case 5: printf("Italian "); break; + case 7: printf("Swiss French "); break; + case 8: printf("Swiss German "); break; + default: printf("English "); + } + printf("\n"); + } +#endif + + /* Pass contents of the _MCH cookie to the kernel */ + bi.bi_atari.mch_cookie = mch_type; + + /* + * Copy command line options into the kernel command line. + */ + i = 0; + while (argc--) { + if ((i+strlen(*argv)+1) < CL_SIZE) { + i += strlen(*argv) + 1; + if (bi.command_line[0]) + strcat (bi.command_line, " "); + strcat (bi.command_line, *argv++); + } + } + printf ("Command line is '%s'\n", bi.command_line); + + start_mem = bi.memory[0].addr; + mem_size = bi.memory[0].size; + + /* tell us where the kernel will go */ + printf("\nThe kernel will be located at 0x%08lx\n", start_mem); + +#ifdef TEST + /* + ** Temporary exit point for testing + */ + boot_exit(-1); +#endif /* TEST */ + +#ifdef USE_BOOTP + kfd = -1; + if (prefer_bootp) { + /* First try to get a remote kernel, then use a local kernel (if + * present) */ + if (get_remote_kernel( kname_set ? kernel_name : NULL ) < 0) { + printf( "\nremote boot failed; trying local kernel\n" ); + if ((kfd = open (kernel_name, O_RDONLY)) == -1) { + fprintf (stderr, "Unable to open kernel file %s\n", + kernel_name); + boot_exit (EXIT_FAILURE); + } + } + } + else { + /* Try BOOTP if local kernel cannot be opened */ + if ((kfd = open (kernel_name, O_RDONLY)) == -1) { + printf( "\nlocal kernel failed; trying remote boot\n" ); + if (get_remote_kernel( kname_set ? kernel_name : NULL ) < 0) { + fprintf (stderr, "Unable to remote boot and " + "to open kernel file %s\n", kernel_name); + boot_exit (EXIT_FAILURE); + } + } + } +#else + /* open kernel executable and read exec header */ + if ((kfd = open (kernel_name, O_RDONLY)) == -1) { + fprintf (stderr, "Unable to open kernel file %s\n", kernel_name); + boot_exit (EXIT_FAILURE); + } +#endif + + if (kread (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) + { + fprintf (stderr, "Unable to read exec header from %s\n", kernel_name); + boot_exit (EXIT_FAILURE); + } + + switch (N_MAGIC(kexec)) { + case ZMAGIC: + text_offset = N_TXTOFF(kexec); + break; + case QMAGIC: + text_offset = sizeof(kexec); + /* the text size includes the exec header; remove this */ + kexec.a_text -= sizeof(kexec); + break; + default: + /* Try to parse it as an ELF header */ + klseek (kfd, 0, SEEK_SET); + if (kread (kfd, (void *)&kexec_elf, sizeof (kexec_elf)) == sizeof (kexec_elf) + && memcmp (&kexec_elf.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) + { + elf_kernel = 1; + /* A few plausibility checks */ + if (kexec_elf.e_type != ET_EXEC || kexec_elf.e_machine != EM_68K + || kexec_elf.e_version != EV_CURRENT) + { + fprintf (stderr, "Invalid ELF header contents in kernel\n"); + boot_exit (EXIT_FAILURE); + } + /* Load the program headers */ + kernel_phdrs = (Elf32_Phdr *) Malloc (kexec_elf.e_phnum * sizeof (Elf32_Phdr)); + if (kernel_phdrs == NULL) + { + fprintf (stderr, "Unable to allocate memory for program headers\n"); + boot_exit (EXIT_FAILURE); + } + klseek (kfd, kexec_elf.e_phoff, SEEK_SET); + if (kread (kfd, (void *) kernel_phdrs, + kexec_elf.e_phnum * sizeof (*kernel_phdrs)) + != kexec_elf.e_phnum * sizeof (*kernel_phdrs)) + { + fprintf (stderr, "Unable to read program headers from %s\n", + kernel_name); + boot_exit (EXIT_FAILURE); + } + break; + } + fprintf (stderr, "Wrong magic number %lo in kernel header\n", + N_MAGIC(kexec)); + boot_exit (EXIT_FAILURE); + } + + /* Load the kernel one page after start of mem */ + start_mem += PAGE_SIZE; + mem_size -= PAGE_SIZE; + /* Align bss size to multiple of four */ + if (!elf_kernel) + kexec.a_bss = (kexec.a_bss + 3) & ~3; + + /* init ramdisk */ + if(ramdisk_name) { + if((rfd = open(ramdisk_name, O_RDONLY)) == -1) { + fprintf(stderr, "Unable to open ramdisk file %s\n", + ramdisk_name); + boot_exit(EXIT_FAILURE); + } + bi.ramdisk_size = (lseek(rfd, 0, SEEK_END) + 1023) / 1024; + } + else + bi.ramdisk_size = 0; + + rd_size = bi.ramdisk_size << 10; + if (mem_size - rd_size < MB && bi.num_memory > 1) + /* If running low on ST ram load ramdisk into alternate ram. */ + bi.ramdisk_addr = (u_long) bi.memory[1].addr + bi.memory[1].size - rd_size; + else + /* Else hopefully there is enough ST ram. */ + bi.ramdisk_addr = (u_long)start_mem + mem_size - rd_size; + + /* calculate the total required amount of memory */ + if (elf_kernel) + { + u_long min_addr = 0xffffffff, max_addr = 0; + for (i = 0; i < kexec_elf.e_phnum; i++) + { + if (min_addr > kernel_phdrs[i].p_vaddr) + min_addr = kernel_phdrs[i].p_vaddr; + if (max_addr < kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz) + max_addr = kernel_phdrs[i].p_vaddr + kernel_phdrs[i].p_memsz; + } + /* This is needed for newer linkers that include the header in + the first segment. */ + if (min_addr == 0) + { + min_addr = PAGE_SIZE; + kernel_phdrs[0].p_vaddr += PAGE_SIZE; + kernel_phdrs[0].p_offset += PAGE_SIZE; + kernel_phdrs[0].p_filesz -= PAGE_SIZE; + kernel_phdrs[0].p_memsz -= PAGE_SIZE; + } + kernel_size = max_addr - min_addr; + } + else + kernel_size = kexec.a_text + kexec.a_data + kexec.a_bss; + memreq = kernel_size + sizeof (bi); + /* align load address of ramdisk image, read() is sloooow on odd addr. */ + memreq = ((memreq + 3) & ~3) + rd_size; + + /* allocate RAM for the kernel */ + if (!(memptr = (char *)Malloc (memreq))) + { + fprintf (stderr, "Unable to allocate memory for kernel and ramdisk\n"); + boot_exit (EXIT_FAILURE); + } + else + fprintf(stderr, "kernel at address %lx\n", (u_long) memptr); + + (void)memset(memptr, 0, memreq); + + /* read the text and data segments from the kernel image */ + if (elf_kernel) + { + for (i = 0; i < kexec_elf.e_phnum; i++) + { + if (klseek (kfd, kernel_phdrs[i].p_offset, SEEK_SET) == -1) + { + fprintf (stderr, "Failed to seek to segment %d\n", i); + boot_exit (EXIT_FAILURE); + } + if (kread (kfd, memptr + kernel_phdrs[i].p_vaddr - PAGE_SIZE, + kernel_phdrs[i].p_filesz) + != kernel_phdrs[i].p_filesz) + { + fprintf (stderr, "Failed to read segment %d\n", i); + boot_exit (EXIT_FAILURE); + } + } + } + else + { + if (klseek (kfd, text_offset, SEEK_SET) == -1) + { + fprintf (stderr, "Failed to seek to text\n"); + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + + if (kread (kfd, memptr, kexec.a_text) != kexec.a_text) + { + fprintf (stderr, "Failed to read text\n"); + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + + /* data follows immediately after text */ + if (kread (kfd, memptr + kexec.a_text, kexec.a_data) != kexec.a_data) + { + fprintf (stderr, "Failed to read data\n"); + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + } + kclose (kfd); + + /* Check kernel's bootinfo version */ + if (!check_bootinfo_version(memptr)) { + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + + /* copy the boot_info struct to the end of the kernel image */ + memcpy ((void *)(memptr + kernel_size), + &bi, sizeof(bi)); + + /* read the ramdisk image */ + if (rfd != -1) + { + if (lseek (rfd, 0, SEEK_SET) == -1) + { + fprintf (stderr, "Failed to seek to beginning of ramdisk file\n"); + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + if (read (rfd, memptr + memreq - rd_size, + rd_size) != rd_size) + { + fprintf (stderr, "Failed to read ramdisk file\n"); + Mfree ((void *)memptr); + boot_exit (EXIT_FAILURE); + } + close (rfd); + } + + /* for those who want to debug */ + if (debugflag) + { + if (bi.ramdisk_size) + printf ("RAM disk at %#lx, size is %ldK\n", + (u_long)(memptr + memreq - rd_size), + bi.ramdisk_size); + + if (elf_kernel) + { + for (i = 0; i < kexec_elf.e_phnum; i++) + { + printf ("Kernel segment %d at %#lx, size %ld\n", i, + start_mem + kernel_phdrs[i].p_vaddr - PAGE_SIZE, + kernel_phdrs[i].p_memsz); + } + } + else + { + printf ("\nKernel text at %#lx, code size %d\n", + start_mem, kexec.a_text); + printf ("Kernel data at %#lx, data size %d\n", + start_mem + kexec.a_text, kexec.a_data ); + printf ("Kernel bss at %#lx, bss size %d\n", + start_mem + kexec.a_text + kexec.a_data, kexec.a_bss ); + } + printf ("\nboot_info is at %#lx\n", + start_mem + kernel_size); + printf ("\nKernel entry is %#lx\n", + elf_kernel ? kexec_elf.e_entry : kexec.a_entry); + printf ("ramdisk dest top is %#lx\n", bi.ramdisk_addr + rd_size); + printf ("ramdisk lower limit is %#lx\n", + (u_long)(memptr + memreq - rd_size)); + printf ("ramdisk src top is %#lx\n", (u_long)(memptr + memreq)); + + printf ("Type a key to continue the Linux boot..."); + fflush (stdout); + getchar(); + } + + printf("Booting Linux...\n"); + + sync (); + + /* turn off interrupts... */ + disable_interrupts(); + + /* turn off caches... */ + disable_cache(); + + /* ..and any MMU translation */ + disable_mmu(); + + /* ++guenther: allow reset if launched with MiNT */ + *(long*)0x426 = 0; + + /* copy mover code to a safe place if needed */ + memcpy ((void *) 0x400, ©all, ©allend - ©all); + + /* setup stack */ + change_stack ((void *) PAGE_SIZE); + + /* + * On the Atari you can have two situations: + * 1. One piece of contiguous RAM (Falcon) + * 2. Two pieces of contiguous RAM (TT) + * In case 2 you can load your program into ST-ram and load your data in + * any old RAM you have left. + * In case 1 you could overwrite your own program when copying the + * kernel and ramdisk to their final positions. + * To solve this the mover code is copied to a safe place first. + * Then this program jumps to the mover code. After the mover code + * has finished it jumps to the start of the kernel in its new position. + * I thought the memory just after the interrupt vector table was a safe + * place because it is used by TOS to store some system variables. + * This range goes from 0x400 to approx. 0x5B0. + * This is more than enough for the miniscule mover routine (16 bytes). + */ + + jump_to_mover((char *) start_mem, memptr, + (char *) bi.ramdisk_addr + rd_size, memptr + memreq, + kernel_size + sizeof (bi), + rd_size, + (void *) 0x400); + + for (;;); + /* NOTREACHED */ +} + + + +#define MAXARGS 30 + +static void get_default_args( int *argc, char ***argv ) + +{ FILE *f; + static char *nargv[MAXARGS]; + char arg[256], *p; + int c, quote, state; + + if (!(f = fopen( "bootargs", "r" ))) + return; + + *argc = 1; + if (***argv) + nargv[0] = **argv; + else + nargv[0] = "bootstrap"; + *argv = nargv; + + quote = state = 0; + p = arg; + while( (c = fgetc(f)) != EOF ) { + + if (state == 0) { + /* outside args, skip whitespace */ + if (!isspace(c)) { + state = 1; + p = arg; + } + } + + if (state) { + /* inside an arg: copy it into 'arg', obeying quoting */ + if (!quote && (c == '\'' || c == '"')) + quote = c; + else if (quote && c == quote) + quote = 0; + else if (!quote && isspace(c)) { + /* end of this arg */ + *p = 0; + nargv[(*argc)++] = strdup(arg); + state = 0; + } + else + *p++ = c; + } + } + if (state) { + /* last arg finished by EOF! */ + *p = 0; + nargv[(*argc)++] = strdup(arg); + } + fclose( f ); + + nargv[*argc] = 0; +} + diff --git a/arch/m68k/boot/atari/bootstrap.h b/arch/m68k/boot/atari/bootstrap.h new file mode 100644 index 000000000..5f8644f44 --- /dev/null +++ b/arch/m68k/boot/atari/bootstrap.h @@ -0,0 +1,147 @@ +/* +** bootstrap.h -- This file is a part of the Atari bootloader. +** +** Copyright 1993 by Arjan Knor +** +** Modified by Andreas Schwab +** - clear transparent translation registers +** +** This file is subject to the terms and conditions of the GNU General Public +** License. See the file COPYING in the main directory of this archive +** for more details. +** +*/ + +#ifndef BOOTSTRAP_H +#define BOOTSTRAP_H + +/* _MCH cookie values */ +#define MACH_ST 0 +#define MACH_STE 1 +#define MACH_TT 2 +#define MACH_FALCON 3 + +/* some constants for memory handling */ +#define ST_RAM 0 +#define TT_RAM 1 +#define TT_RAM_BASE (u_long)(0x01000000) +#define MB (1024 * 1024) +#define START_MEM (bi.memory[0].addr) +#define MEM_SIZE (bi.memory[0].size) + +/* the various CPU- and FPU-types */ +#define AFF_68000 (1) +#define AFF_68020 (2) +#define AFF_68030 (4) +#define AFF_68040 (8) +#define AFF_68881 (16) +#define AFF_68882 (32) + +/* the possible OS-languages */ +#define USA 0 +#define FRG 1 +#define FRA 2 +#define UK 3 +#define SPA 4 +#define ITA 5 +#define SWE 6 +#define SWF 7 +#define SWG 8 +#define TUR 9 +#define FIN 10 +#define NOR 11 +#define DEN 12 +#define SAU 13 +#define HOL 14 + +/* some inline functions */ + +static __inline int fpu_idle_frame_size (void) +{ + char fpu_frame[216]; + __asm__ __volatile__ ("fnop"::); + __asm__ __volatile__ ("fsave %0@" : : "a" (fpu_frame)); + return fpu_frame[1]; +} + +static __inline void change_stack (u_long *stackp) +{ + __asm__ volatile ("movel %0,sp\n\t" :: "g" (stackp) : "sp"); +} + +static __inline void disable_interrupts (void) +{ + __asm__ volatile ("orw #0x700,sr":); +} + +extern struct bootinfo bi; +static __inline void disable_cache (void) +{ + __asm__ volatile ("movec %0,cacr" :: "d" (0)); + if (bi.cputype & CPU_68060) { + /* '060: clear branch cache after disabling it; + * disable superscalar operation (and enable FPU) */ + __asm__ volatile ("movec %0,cacr" :: "d" (0x00400000)); + __asm__ volatile ("moveq #0,d0;" + ".long 0x4e7b0808" /* movec d0,pcr */ + : /* no outputs */ + : /* no inputs */ + : "d0"); + } +} + +static __inline void disable_mmu (void) +{ + if (bi.cputype & (CPU_68040|CPU_68060)) { + __asm__ volatile ("moveq #0,d0;" + ".long 0x4e7b0003;" /* movec d0,tc */ + ".long 0x4e7b0004;" /* movec d0,itt0 */ + ".long 0x4e7b0005;" /* movec d0,itt1 */ + ".long 0x4e7b0006;" /* movec d0,dtt0 */ + ".long 0x4e7b0007" /* movec d0,dtt1 */ + : /* no outputs */ + : /* no inputs */ + : "d0"); + } + else { + __asm__ volatile ("subl #4,sp\n\t" + "pmove tc,sp@\n\t" + "bclr #7,sp@\n\t" + "pmove sp@,tc\n\t" + "addl #4,sp"); + if (bi.cputype & CPU_68030) { + __asm__ volatile ("clrl sp@-\n\t" + ".long 0xf0170800\n\t" /* pmove sp@,tt0 */ + ".long 0xf0170c00\n\t" /* pmove sp@,tt1 */ + "addl #4,sp\n"); + } + } +} + +static __inline void jump_to_mover (void *, void *, void *, void *, int, int, + void *) __attribute__ ((noreturn)); +static __inline void jump_to_mover (void *kernel_start, void *mem_start, + void *ramdisk_end, void *mem_end, + int kernel_size, int ramdisk_size, + void *mover_addr) +{ + asm volatile ("movel %0,a0\n\t" + "movel %1,a1\n\t" + "movel %2,a2\n\t" + "movel %3,a3\n\t" + "movel %4,d0\n\t" + "movel %5,d1\n\t" + "jmp %6@\n" + : /* no outputs */ + : "g" (kernel_start), "g" (mem_start), + "g" (ramdisk_end), "g" (mem_end), + "g" (kernel_size), "g" (ramdisk_size), + "a" (mover_addr) + : "a0", "a1", "a2", "a3", "d0", "d1"); + + /* Avoid warning that function may return */ + for (;;) ; +} + +#endif /* BOOTSTRAP_H */ + diff --git a/arch/m68k/boot/atari/ethlance.c b/arch/m68k/boot/atari/ethlance.c new file mode 100644 index 000000000..3b248c4af --- /dev/null +++ b/arch/m68k/boot/atari/ethlance.c @@ -0,0 +1,435 @@ + +#include <stdio.h> +#include <string.h> + +#include "bootp.h" +#include "ethlance.h" + + +struct { + volatile unsigned short *memaddr; + volatile unsigned short *ioaddr; +} lance_addr_list[] = { + { (void *)0xfe010000, (void *)0xfe00fff0 }, /* RieblCard VME in TT */ + { (void *)0xfec10000, (void *)0xfec0fff0 }, /* RieblCard VME in MegaSTE + (highest byte stripped) */ + { (void *)0xfee00000, (void *)0xfeff7000 }, /* RieblCard in ST + (highest byte stripped) */ + { (void *)0xfecf0000, (void *)0xfecffff0 }, /* PAMCard VME in TT and MSTE + (highest byte stripped) */ +}; + +#define N_LANCE_ADDR (sizeof(lance_addr_list)/sizeof(*lance_addr_list)) + +#define TX_RING_SIZE 1 +#define TX_RING_LEN_BITS 0 + +#define RX_RING_SIZE 16 +#define RX_RING_LEN_BITS (4 << 5) + +#define offsetof(type,elt) ((unsigned long)(&(((type *)0)->elt))) + +/* The LANCE Rx and Tx ring descriptors. */ +struct lance_rx_head { + unsigned short base; /* Low word of base addr */ + volatile unsigned char flag; + unsigned char base_hi; /* High word of base addr (unused) */ + short buf_length; /* This length is 2s complement! */ + short msg_length; /* This length is "normal". */ +}; + +struct lance_tx_head { + unsigned short base; /* Low word of base addr */ + volatile unsigned char flag; + unsigned char base_hi; /* High word of base addr (unused) */ + short length; /* Length is 2s complement! */ + volatile short misc; +}; + +struct ringdesc { + unsigned short adr_lo; /* Low 16 bits of address */ + unsigned char len; /* Length bits */ + unsigned char adr_hi; /* High 8 bits of address (unused) */ +}; + +struct lance_packet { + volatile unsigned char data[PKTLEN]; +}; + +/* The LANCE initialization block, described in databook. */ +struct lance_init_block { + unsigned short mode; /* Pre-set mode */ + unsigned char hwaddr[6]; /* Physical ethernet address */ + unsigned filter[2]; /* Multicast filter (unused). */ + /* Receive and transmit ring base, along with length bits. */ + struct ringdesc rx_ring; + struct ringdesc tx_ring; +}; + +/* The whole layout of the Lance shared memory */ +struct lance_memory { + struct lance_init_block init; + struct lance_tx_head tx_head[TX_RING_SIZE]; + struct lance_rx_head rx_head[RX_RING_SIZE]; + struct lance_packet tx_packet[TX_RING_SIZE]; + struct lance_packet rx_packet[TX_RING_SIZE]; +}; + +#define RIEBL_MAGIC 0x09051990 +#define RIEBL_MAGIC_ADDR ((unsigned long *)(((char *)MEM) + 0xee8a)) +#define RIEBL_HWADDR_ADDR ((unsigned char *)(((char *)MEM) + 0xee8e)) +#define RIEBL_IVEC_ADDR ((unsigned short *)(((char *)MEM) + 0xfffe)) + +struct lance_ioreg { +/* base+0x0 */ volatile unsigned short data; +/* base+0x2 */ volatile unsigned short addr; + unsigned char _dummy1[3]; +/* base+0x7 */ volatile unsigned char ivec; + unsigned char _dummy2[5]; +/* base+0xd */ volatile unsigned char eeprom; + unsigned char _dummy3; +/* base+0xf */ volatile unsigned char mem; +}; + +enum lance_type { + OLD_RIEBL, /* old Riebl card without battery */ + NEW_RIEBL, /* new Riebl card with battery */ + PAM_CARD /* PAM card with EEPROM */ +} CardType; + +HWADDR dev_addr; + +/* This is a default address for the old RieblCards without a battery + * that have no ethernet address at boot time. 00:00:36:04 is the + * prefix for Riebl cards, the 00:00 at the end is arbitrary. + */ + +HWADDR OldRieblDefHwaddr = { + 0x00, 0x00, 0x36, 0x04, 0x00, 0x00 +}; + +struct lance_ioreg *IO; +struct lance_memory *MEM; + +#define DREG IO->data +#define AREG IO->addr +#define REGA(a) ( AREG = (a), DREG ) + +int CurRx; + + +/* Definitions for the Lance */ + +/* tx_head flags */ +#define TMD1_ENP 0x01 +#define TMD1_STP 0x02 +#define TMD1_DEF 0x04 +#define TMD1_ONE 0x08 +#define TMD1_MORE 0x10 +#define TMD1_ERR 0x40 +#define TMD1_OWN 0x80 + +#define TMD1_OWN_CHIP TMD1_OWN +#define TMD1_OWN_HOST 0 + +/* tx_head misc field */ +#define TMD3_TDR 0x03FF +#define TMD3_RTRY 0x0400 +#define TMD3_LCAR 0x0800 +#define TMD3_LCOL 0x1000 +#define TMD3_UFLO 0x4000 +#define TMD3_BUFF3 0x8000 + +/* rx_head flags */ +#define RMD1_ENP 0x01 +#define RMD1_STP 0x02 +#define RMD1_BUFF 0x04 +#define RMD1_CRC 0x08 +#define RMD1_OFLO 0x10 +#define RMD1_FRAM 0x20 +#define RMD1_ERR 0x40 +#define RMD1_OWN 0x80 + +#define RMD1_OWN_CHIP RMD1_OWN +#define RMD1_OWN_HOST 0 + +/* register names */ +#define CSR0 0 +#define CSR1 1 +#define CSR2 2 +#define CSR3 3 + +/* CSR0 */ +#define CSR0_INIT 0x0001 /* initialize */ +#define CSR0_STRT 0x0002 /* start */ +#define CSR0_STOP 0x0004 /* stop */ +#define CSR0_TDMD 0x0008 /* transmit demand */ +#define CSR0_TXON 0x0010 /* transmitter on */ +#define CSR0_RXON 0x0020 /* receiver on */ +#define CSR0_INEA 0x0040 /* interrupt enable */ +#define CSR0_INTR 0x0080 /* interrupt active */ +#define CSR0_IDON 0x0100 /* initialization done */ +#define CSR0_TINT 0x0200 /* transmitter interrupt */ +#define CSR0_RINT 0x0400 /* receiver interrupt */ +#define CSR0_MERR 0x0800 /* memory error */ +#define CSR0_MISS 0x1000 /* missed frame */ +#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) */ +#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits */ +#define CSR0_ERR 0x8000 /* error */ + +/* CSR3 */ +#define CSR3_BCON 0x0001 +#define CSR3_ACON 0x0002 +#define CSR3_BSWP 0x0004 + + +#define HZ 200 +#define _hz_200 (*(volatile unsigned long *)0x4ba) + + + + +/***************************** Prototypes *****************************/ + +static int lance_probe( void ); +static int addr_readable( volatile void *regp, int wordflag ); +static int lance_init( void ); +static void lance_get_hwaddr( HWADDR *addr ); +static int lance_snd( Packet *pkt, int len ); +static int lance_rcv( Packet *pkt, int *len ); + +/************************* End of Prototypes **************************/ + + + +ETHIF_SWITCH LanceSwitch = { + lance_probe, lance_init, lance_get_hwaddr, + lance_snd, lance_rcv +}; + + +static int lance_probe( void ) + +{ int i; + + for( i = 0; i < N_LANCE_ADDR; ++i ) { + if (addr_readable( lance_addr_list[i].memaddr, 1 ) && + (lance_addr_list[i].memaddr[0] = 1, + lance_addr_list[i].memaddr[0] == 1) && + (lance_addr_list[i].memaddr[0] = 0, + lance_addr_list[i].memaddr[0] == 0) && + addr_readable( lance_addr_list[i].ioaddr, 1 )) { + break; + } + } + if (i == N_LANCE_ADDR) return( -1 ); + + IO = (struct lance_ioreg *)lance_addr_list[i].ioaddr; + MEM = (struct lance_memory *)lance_addr_list[i].memaddr; + REGA( CSR0 ) = CSR0_STOP; + + return( 0 ); +} + + +static int addr_readable( volatile void *regp, int wordflag ) + +{ int ret; + long *vbr, save_berr; + + __asm__ __volatile__ ( "movec %/vbr,%0" : "=r" (vbr) : ); + save_berr = vbr[2]; + + __asm__ __volatile__ + ( "movel %/sp,%/d1\n\t" + "movel #Lberr,%2@\n\t" + "moveq #0,%0\n\t" + "tstl %3\n\t" + "bne 1f\n\t" + "tstb %1@\n\t" + "bra 2f\n" +"1: tstw %1@\n" +"2: moveq #1,%0\n" +"Lberr: movel %/d1,%/sp" + : "=&d" (ret) + : "a" (regp), "a" (&vbr[2]), "rm" (wordflag) + : "d1", "memory" + ); + + vbr[2] = save_berr; + + return( ret ); +} + + +static int lance_init( void ) + +{ int i; + + /* Now test for type: If the eeprom I/O port is readable, it is a + * PAM card */ + if (addr_readable( &(IO->eeprom), 0 )) { + /* Switch back to Ram */ + i = IO->mem; + CardType = PAM_CARD; + } + else if (*RIEBL_MAGIC_ADDR == RIEBL_MAGIC) { + CardType = NEW_RIEBL; + } + else + CardType = OLD_RIEBL; + + /* Get the ethernet address */ + switch( CardType ) { + case OLD_RIEBL: + /* No ethernet address! (Set some default address) */ + memcpy( dev_addr, OldRieblDefHwaddr, ETHADDRLEN ); + break; + case NEW_RIEBL: + memcpy( dev_addr, RIEBL_HWADDR_ADDR, ETHADDRLEN ); + break; + case PAM_CARD: + i = IO->eeprom; + for( i = 0; i < ETHADDRLEN; ++i ) + dev_addr[i] = + ((((unsigned short *)MEM)[i*2] & 0x0f) << 4) | + ((((unsigned short *)MEM)[i*2+1] & 0x0f)); + i = IO->mem; + break; + } + + MEM->init.mode = 0x0000; /* Disable Rx and Tx. */ + for( i = 0; i < ETHADDRLEN; i++ ) + MEM->init.hwaddr[i] = dev_addr[i^1]; /* <- 16 bit swap! */ + MEM->init.filter[0] = 0x00000000; + MEM->init.filter[1] = 0x00000000; + MEM->init.rx_ring.adr_lo = offsetof( struct lance_memory, rx_head ); + MEM->init.rx_ring.adr_hi = 0; + MEM->init.rx_ring.len = RX_RING_LEN_BITS; + MEM->init.tx_ring.adr_lo = offsetof( struct lance_memory, tx_head ); + MEM->init.tx_ring.adr_hi = 0; + MEM->init.tx_ring.len = TX_RING_LEN_BITS; + + REGA( CSR3 ) = CSR3_BSWP | (CardType == PAM_CARD ? CSR3_ACON : 0); + REGA( CSR2 ) = 0; + REGA( CSR1 ) = 0; + REGA( CSR0 ) = CSR0_INIT | CSR0_STRT; + + i = 1000000; + while( i-- > 0 ) + if (DREG & CSR0_IDON) + break; + if (i < 0 || (DREG & CSR0_ERR)) { + DREG = CSR0_STOP; + return( -1 ); + } + DREG = CSR0_IDON; + + for (i = 0; i < TX_RING_SIZE; i++) { + MEM->tx_head[i].base = offsetof( struct lance_memory, tx_packet[i] ); + MEM->tx_head[i].flag = TMD1_OWN_HOST; + MEM->tx_head[i].base_hi = 0; + MEM->tx_head[i].length = 0; + MEM->tx_head[i].misc = 0; + } + + for (i = 0; i < RX_RING_SIZE; i++) { + MEM->rx_head[i].base = offsetof( struct lance_memory, rx_packet[i] ); + MEM->rx_head[i].flag = TMD1_OWN_CHIP; + MEM->rx_head[i].base_hi = 0; + MEM->rx_head[i].buf_length = -PKTLEN; + MEM->rx_head[i].msg_length = 0; + } + CurRx = 0; + + return( 0 ); +} + + +static void lance_get_hwaddr( HWADDR *addr ) + +{ + memcpy( addr, dev_addr, ETHADDRLEN ); +} + + +static int lance_snd( Packet *pkt, int len ) + +{ unsigned long timeout; + + /* The old LANCE chips doesn't automatically pad buffers to min. size. */ + len = (len < 60) ? 60 : len; + /* PAM-Card has a bug: Can only send packets with even number of bytes! */ + if (CardType == PAM_CARD && (len & 1)) + ++len; + + MEM->tx_head[0].length = -len; + MEM->tx_head[0].misc = 0; + memcpy( (void *)&MEM->tx_packet[0].data, pkt, len ); + MEM->tx_head[0].base = offsetof(struct lance_memory, tx_packet[0]); + MEM->tx_head[0].base_hi = 0; + MEM->tx_head[0].flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP; + + /* Trigger an immediate send poll. */ + REGA( CSR0 ) = CSR0_TDMD; + + /* Wait for packet being sent */ + timeout = _hz_200 + 3*HZ; + while( (MEM->tx_head[0].flag & TMD1_OWN_CHIP) && + !MEM->tx_head[0].misc && + _hz_200 < timeout ) + ; + + if ((MEM->tx_head[0].flag & TMD1_OWN) == TMD1_OWN_HOST && + !(MEM->tx_head[0].misc & TMD1_ERR)) + /* sent ok */ + return( 0 ); + + /* failure */ + if (_hz_200 >= timeout) + return( ETIMEO ); + if (MEM->tx_head[0].misc & TMD3_UFLO) { + /* On FIFO errors, must re-turn on TX! */ + DREG = CSR0_STRT; + } + + return( ESEND ); +} + + +static int lance_rcv( Packet *pkt, int *len ) + +{ unsigned long timeout; + int stat; + + /* Wait for a packet */ + timeout = _hz_200 + 4*HZ; + while( (MEM->rx_head[CurRx].flag & TMD1_OWN_CHIP) && + _hz_200 < timeout ) + ; + /* Not ours -> was a timeout */ + if (((stat = MEM->rx_head[CurRx].flag) & TMD1_OWN) == TMD1_OWN_CHIP) + return( ETIMEO ); + + /* Check for errors */ + if (stat != (RMD1_ENP|RMD1_STP)) { + MEM->rx_head[CurRx].flag &= (RMD1_ENP|RMD1_STP); + if (stat & RMD1_FRAM) return( EFRAM ); + if (stat & RMD1_OFLO) return( EOVERFL ); + if (stat & RMD1_CRC) return( ECRC ); + return( ERCV ); + } + + /* Get the packet */ + *len = MEM->rx_head[CurRx].msg_length & 0xfff; + memcpy( pkt, (void *)&MEM->rx_packet[CurRx].data, *len ); + + /* Give the buffer back to the chip */ + MEM->rx_head[CurRx].buf_length = -PKTLEN; + MEM->rx_head[CurRx].flag |= RMD1_OWN_CHIP; + CurRx = (CurRx + 1) % RX_RING_SIZE; + + return( 0 ); +} + + diff --git a/arch/m68k/boot/atari/ethlance.h b/arch/m68k/boot/atari/ethlance.h new file mode 100644 index 000000000..4f9c90241 --- /dev/null +++ b/arch/m68k/boot/atari/ethlance.h @@ -0,0 +1,7 @@ + +#ifndef _ethlance_h +#define _ethlance_h + +extern ETHIF_SWITCH LanceSwitch; + +#endif /* _ethlance_h */ diff --git a/arch/m68k/boot/atari/sysvars.h b/arch/m68k/boot/atari/sysvars.h new file mode 100644 index 000000000..087d9f695 --- /dev/null +++ b/arch/m68k/boot/atari/sysvars.h @@ -0,0 +1,22 @@ +typedef struct _osheader +{ + unsigned short os_entry; + unsigned short os_version; + void *reseth; + struct _osheader *os_beg; + void *os_end; + long os_rsv1; + void *os_magic; + long os_date; + unsigned short os_conf; + unsigned short os_dosdate; + char **p_root; + unsigned char **pkbshift; + void **p_run; + char *p_rsv2; +} OSHEADER; + +#define phystop ((unsigned long *)0x42e) +#define _sysbase ((OSHEADER **)0x4f2) +#define _p_cookies ((unsigned long **)0x5a0) +#define ramtop ((unsigned long *)0x5a4) |