/*	$NetBSD$	*/

/*
 * Copyright (c) 1994-1997 Mark Brinicombe.
 * Copyright (c) 1994-1997 Brini.
 * All rights reserved.
 *
 * This code is derived from software written for Brini by Mark Brinicombe
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Mark Brinicombe.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * RiscBSD kernel project
 *
 * bsdbooter.c
 *
 * RiscBSD boot loader
 *
 * Created      : 12/09/94
 * Last updated : 23/09/99
 *
 * September 1999	DD
 *   Modified to run under RISCOS 4.0
 *
 * Based on kate/boot/boot.c
 *
 *    $Id$
 */

/* Include standard header files */

#include <stdarg.h>

/* Include local headers */

#include "katelib.h"
#include "swiv.h"
#include "swis.h"
#include "arm.h"
#include "stand.h"

#include "bootconfig.h"

/*
 * Declare global variables
 */

#define VERSION "2.89"

/* #define USE_MODULEAREA */

#define KERNAREA (-1)
#define KERNBASE 0xf0000000

#define TABLEAREA (-1)
#define LOADAREA (-1)

#define SCRATCHSIZE 0xc000

#ifndef OS_DynamicArea
#define OS_Byte 6
#define OS_SetMemMapEntries 0x53
#define OS_DynamicArea 0x66
#define OS_Memory      0x68
#define OS_MMUControl  0x6b
#endif

/*
** Addresses of the page translation table under RISCOS 3.5-3.7 and RISCOS 4.
** These are undocumented
*/
#define RO3XPAGE_BASE 0x02c00000
#define RO40PAGE_BASE 0x08000000

#define PAD16K 16384

/* Layout of a page table entry used by OS_Memory */

typedef struct {
  unsigned int physical_page;
  unsigned int logical_addr;
  unsigned int physical_addr;
} osmem_pte;

/* Layout of page table entry used by OS_SetMemMapEntries */

typedef struct {
  unsigned int memmap_page;
  unsigned int memmap_addr;
  unsigned int memmap_protection;
} memmap_pte;

/* Flags for the OS_Memory calls used in this code */

#define OSMEM_LTOP 0x2200	/* logical address to physical address */
#define OSMEM_PTOL 0x1400	/* Physical address to logical address */
#define OSMEM_PNTOP 0x2100	/* Page number to physical address */
#define OSMEM_PTOPN 0x0C00	/* Physical address to page number */

/* Values returned by OS_Byte 129 to identify OS version */

#define RISCOS_40 0xA8
#define RISCOS_37 0xA7
#define RISCOS_36 0xA6
#define RISCOS_35 0xA5

#if 0
#define FASTBOOT_FILENAME "<BtRiscBSD$Dir>.booter.fastboot"
#endif

/*
 * Declare external variables
 */

extern char *__cli;

/* Pregrow handler used when creating kernel dynamic area with RISCOS 4 */

extern void pregrow(void);

/*
 * Local vars. Some of these have to be global because they are used after
 * the processor has been switches to SVC mode local variables would be
 * lost as they would be on the USR mode stack.
 */

BootConfig bootconfig;
int in[3], out[3];
char kernelname[1024];
unsigned char *buffer;
static aout_t aout;
unsigned int stringtablesize;
unsigned int kernelsize;
unsigned int logical;
unsigned int physical;
unsigned int filesize;
unsigned int copysize;
unsigned int ptr;

osmem_pte osmem_block;
memmap_pte *memmap_base, *memmap_next;
unsigned int memmap_size;
unsigned int pagetable_base;
unsigned int os_version;
unsigned int first_page;
void (*handler)(void) = pregrow;

/*
 * Local function prototypes
 */

void fatal(struct Error *error);
void fatal1(int error, char *string);
unsigned char *locate_memory_blocks(void);
void _bsdboot(BootConfig *bootconfig, unsigned int address);
int kill_area(char *wanted);

/* Now for the main code */

extern int main (int, char **);

void __exit(int);

void _main(void)
  {
    __exit (main (0, (char **)0));	/* ... ignition */

/* not reached */
  }

/* The main booter code */

int main(int argc, char *argv[])
  {
    char *cliptr;
    int loop;
    int filehandle;
    int filetype;
    unsigned char *arrangementtable;

/*
** First, figure out what version of RISCOS this program is running under.
** I'm tempted to put in a check for the OS version and to flag an error
** if it is not RISCOS 3.5, 3.6, 3.7 or 4.0
*/

  fatal(swix(OS_Byte, IN(R0|R1|R2)|OUT(R1), 129, 0, 255, &os_version));

/* Analyse the command line */

    cliptr = __cli;

/* Skip the command name */

    while (*cliptr != ' ' && *cliptr != 0)
      ++cliptr;

/* Skip any spaces */

    while (*cliptr == ' ')
      ++cliptr;

/* Check for another parameter */

    if (*cliptr != 0)
      {
        for (loop = 0; *cliptr != ' ' && *cliptr != 0; ++loop,++cliptr)
          {
            kernelname[loop] = *cliptr;
          }
        kernelname[loop] = 0;
      }
    else
      strcpy(kernelname, "netbsd");

    strcpy(bootconfig.kernelname, kernelname);

#if 0
/*
 * This code is no longer needed since the Wimp front end application
 * will create the fastboot file.
 */

/*
 * Write the command line used to a fastboot file. Execing or Obeying
 * this file will boot RiscBSD. This can be used during the RiscOS bootup
 * to enable a fast boot.
 */

/*
 * Open the autoboot file. Just skip if file cannot be opened.
 */

    if (strstr(__cli, "nofb") == 0)
      {
        struct Error *error;

        error = swix(OS_Find, IN(R0|R1)|OUT(R0), 0x80, FASTBOOT_FILENAME,
          &filehandle);
        if (error == NULL && filehandle != 0)
          {
            swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 2, filehandle, __cli,
              strlen(__cli), 0);
            swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 2, filehandle, "\n", 1, 0);

	    /* Close the file */

            swi(OS_Find, IN(R0|R1), 0, filehandle);

            swi(OS_File, IN(R0|R1|R2), 18, FASTBOOT_FILENAME, 0xfeb);
          }
        else
          {
            printf("Warning: Cannot write fastboot file %s\n",
              FASTBOOT_FILENAME);
          }
      }
#endif

/* Set the screen mode ... */

/* I know this is messy. It is currently just a hack to try things out
 * Why didn't Acorn add a SWI call to interpret the mode string and return
 * a mode specifer ?
 */

/* Also this is temporary as eventually the console driver will set the VIDC
 * up as required.
 * It sort of expects the screenmode= options to be at the end of the string.
 */

    {
      char *modeptr;
      int modespec[6];
      int done;

      modeptr = strstr(__cli, "screenmode=");
      if (modeptr)
        {
          modeptr += 11;
          modespec[0] = 0x00000001;
          modespec[1] = 0x00000000;
          modespec[2] = 0x00000000;
          modespec[3] = 0x00000000;
          modespec[4] = 0x00000000;
          modespec[5] = -1;
          done = 0;

          while (*modeptr)
            {
              switch (*(modeptr++))
                {
                  case 'X':
                  case 'x':
                    if (done & 1) break;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[1] = (modespec[1] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    done |= 1;
                    break;

                  case 'Y':
                  case 'y':
                    if (done & 2) break;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[2] = (modespec[2] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    done |= 2;
                    break;

                  case 'C':
                  case 'c':
                  case 'G':
                  case 'g':
                    if (done & 4) break;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[3] = (modespec[3] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    if (modespec[3] == 1)
                      modespec[3] = 2;
                    if (modespec[3] > 0)
                      {
                        /* Calc bits per pixel */
                        loop = 0;
                        --modespec[3];
                        while (modespec[3])
                          {
                            ++loop;
                            modespec[3] = modespec[3] >> 1;
                          }
                        /* Calc log2 bits per pixel */
                        --loop;
                        modespec[3] = 0;
                        while (loop)
                          {
                            ++modespec[3];
                            loop = loop >> 1;
                          }
                      }
                      done |= 4;
                    break;

                  case 'F':
                  case 'f':
                    if (done & 8) break;
                    while (*modeptr >= '0' && *modeptr <= '9')
                      {
                        modespec[4] = (modespec[4] * 10) + (*modeptr - '0');
                        ++modeptr;
                      }
                    done |= 8;
                    break;

                  default:
                    break;
                }
            }
          if (modespec[4] == 0) modespec[4] = -1;
          if (strstr(__cli, "kto") == 0)
            modespec[3] = 0x00000003;
/*          printf("x=%d y=%d c=%d f=%d\n", modespec[1], modespec[2],
            modespec[3], modespec[4]);*/
          fatal(swix(Wimp_SetMode, IN(R0), &modespec));
          bootconfig.framerate = modespec[4];
        }
      else
        bootconfig.framerate = 0;
    }

/* Announcement time .. */

    if (strstr(__cli, "verbose") != 0)
      printf("RiscBSD BootLoader " VERSION " " __DATE__ "\n");

/* A bit of info */

    if (strstr(__cli, "verbose") != 0) {
      printf("Kernel: %s\n\r", kernelname);
      printf("Args: %s\n\r", __cli);
    }

/* Get the machine id */

    fatal(swix(OS_ReadSysInfo, IN(R0)|OUT(R3), 2, &bootconfig.machine_id));

    bootconfig.magic = BOOTCONFIG_MAGIC;

/* Get the display variables. Failure on any of these will abort the boot */

    in[0] = 149;
    in[1] = 150;
    in[2] = -1;

    fatal(swix(OS_ReadVduVariables, IN(R0|R1), &in, &out));

    bootconfig.display_start = out[0];
    bootconfig.display_size = out[1];

    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 9,
      &bootconfig.bitsperpixel));
    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 11,
      &bootconfig.width));
    fatal(swix(OS_ReadModeVariable, IN(R0|R1) | OUT(R2), -1, 12,
      &bootconfig.height));

    {
      	int pageblock[3];

	pageblock[1] = bootconfig.display_start;
	fatal(swix(OS_Memory, IN(R0|R1|R2) | OUT(R1), 0x2200,
	  pageblock, 1, pageblock));
	bootconfig.vdram_phys = pageblock[2];
   }

/* Override sanity checks if we are kernel team only */

    if (strstr(__cli, "kto") == 0)
      {

/* Will the kernel support this mode ? */

        if (bootconfig.bitsperpixel != 3)
          {
            printf("Error: Current kernels only support 8 bpp for booting.\n");
            return(1);
          }

/* Will the kernel support this mode ? */

        if (bootconfig.bitsperpixel > 3)
          {
            printf("Error: Only 1, 2, 4 or 8 bpp modes are currently supported\n");
            return(1);
          }
      }

/* Get the arrangement table for the memory */

    arrangementtable = locate_memory_blocks();

/* Provide DRAM bank munging */

    if (strstr(__cli, "dbmunge=") != 0)
      {
        char *ptr;
        int loop;
        int loop1;

        ptr = strstr(__cli, "dbmunge=") + 8;
        for (loop = 0; loop < 4; ++loop)
          {
            if (ptr[loop] == 'Z')
              {
                bootconfig.dram[loop].address = 0;
                bootconfig.dram[loop].pages = 0;
              }
            else if (ptr[loop] == 'I')
              {
                for (loop1 = loop; loop1 < (bootconfig.dramblocks - 1); ++loop1)
                  {
                    bootconfig.dram[loop1] = bootconfig.dram[loop1 + 1];
                  }
                --bootconfig.dramblocks;
                bootconfig.dram[bootconfig.dramblocks].address = 0;
                bootconfig.dram[bootconfig.dramblocks].pages = 0;
              }
          }
      }

/*
 * Ok we support a.out files. This means that we need to
 * identify the type.
 */

/* Get the size of the file */

    filesize = 0;
    fatal(swix(OS_File, IN(R0|R1)|OUT(R0|R4), 5, kernelname, &filetype,
      &filesize));

    if (filetype != 1)
      {
        printf("%s is not a object that can be booted\n", kernelname);
        return(1);
      }

/* Verbose info to the user. This is mainly debugging */

    if (strstr(__cli, "verbose") != 0)
      printf("filesize = %08x\n", filesize);

    if (filesize == 0) {
      printf("Error: Kernel image too small\n");
      return(1);
    }

#ifdef USE_MODULEAREA

/* Allocate memory in module area to hold the data we are loading */

    fatal(swix(OS_Module, IN(R0|R3)|OUT(R2), 6, filesize, &buffer));

#else

/* Allocate memory to hold the data we are loading */

    kill_area("Kate Data");

    fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8)|OUT(R3), 0,
      LOADAREA, filesize, -1, 0x80, filesize, 0, 0, "Kate Data", &buffer));

#endif

/*
 * Open the file.
 */

    swi(OS_Find, IN(R0|R1)|OUT(R0), 0x40, kernelname, &filehandle);
    if (filehandle == 0)
      {
        printf("Error: Cannot read kernel file %s\n", kernelname);
        return(1);
      }

/* Load the file into memory */

    swi(OS_GBPB, IN(R0|R1|R2|R3|R4), 3, filehandle, buffer, filesize, 0);

/* Close the file */

    fatal(swix(OS_Find, IN(R0|R1), 0, filehandle));

    if (strstr(__cli, "verbose") != 0) printf("Kernel read into memory at %08x\n", buffer);

/* Take a copy of the possible a.out header */

    aout = *((aout_t *)buffer);

/* Do we have an a.out file ? */

    switch(aout.a_magic & MAGICMASK) {
      case NMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is NMAGIC a.out format\n");
        aout.a_syms = 0;
        stringtablesize = 0;
	break;
      case OMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is OMAGIC a.out format\n");
        aout.a_syms = 0;
        stringtablesize = 0;
	break;
      case ZMAGIC:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is ZMAGIC a.out format\n");
        stringtablesize = filesize - (unsigned int)(aout.a_text +
          aout.a_data + aout.a_syms);
        if (aout.a_entry < KERNBASE || aout.a_entry > KERNBASE + 0x00100000) {
          printf("Error: Kernel entry point out of range\n");
          return(0);
        }
	break;
      default:
        if (strstr(__cli, "verbose") != 0)
          printf("Kernel binary is unrecognised, guessing it is AIF format\n");
	aout.a_text = filesize;
	aout.a_data = 0;
	aout.a_bss = 0;
        aout.a_syms = 0;
        stringtablesize = 0;
        break;
    }

    kernelsize = (unsigned int)(aout.a_text + aout.a_data + aout.a_bss);

/* Add symbol table size if required. */

    if (strstr(__cli, "symtab") != 0) {
      kernelsize += (unsigned int)aout.a_syms + stringtablesize;
    } else {
      aout.a_syms = 0;
      stringtablesize = 0;
    }

    if (strstr(__cli, "verbose") != 0)
      {
        printf("a_text = %08x\n", aout.a_text);
        printf("a_data = %08x\n", aout.a_data);
        printf("a_bss  = %08x\n", aout.a_bss);
        printf("a_syms = %08x\n", aout.a_syms);
        printf("snames = %08x\n", stringtablesize);
      }

/* Give ourselves 16K of spare space and round off to a page */

    kernelsize = (kernelsize + PAD16K) & ~(bootconfig.pagesize-1);

/* Set the virtual address of the kernel in the bootconfig structure */

    bootconfig.kernvirtualbase = KERNBASE;
    bootconfig.kernsize = kernelsize;
    bootconfig.argvirtualbase = bootconfig.kernvirtualbase
                              + bootconfig.kernsize;
    bootconfig.argsize = bootconfig.pagesize;
    kernelsize += bootconfig.argsize;
 /*
 ** The address of the scratch space has to be on a 16K boundary so round
 ** the kernel size up to the next multiple of 16K bytes. Pre RISCOS 4
 ** this would happen automagically due to the way the memory for the
 ** kernel is acquired but we have to do it explicitly for RISCOS 4.
 */
    kernelsize = (kernelsize + PAD16K) & ~(PAD16K-1);

    bootconfig.scratchvirtualbase = bootconfig.kernvirtualbase+kernelsize;
    bootconfig.scratchsize = SCRATCHSIZE;
    kernelsize += bootconfig.scratchsize;

/* Verbose info to the user. This is mainly debugging */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("filesize = %08x\n", filesize);
        printf("bootconfig.kernvirtualbase = %08x\n",
          bootconfig.kernvirtualbase);
        printf("bootconfig.kernsize = %08x\n", bootconfig.kernsize);
        printf("bootconfig.argvirtualbase = %08x\n",
          bootconfig.argvirtualbase);
        printf("bootconfig.argsize = %08x\n", bootconfig.argsize);
        printf("bootconfig.scratchvirtualbase = %08x\n",
          bootconfig.scratchvirtualbase);
        printf("bootconfig.scratchsize = %08x\n", bootconfig.scratchsize);
        printf("kernelsize = %08x\n", kernelsize);
	printf("display_start = %08x\n", bootconfig.display_start);
	printf("display_size = %08x\n", bootconfig.display_size);
	printf("physical dstart = %08x\n", bootconfig.vdram_phys);
	printf("display log2bpp = %d\n", bootconfig.bitsperpixel);
      }

    kill_area("Kate Kernel");

/*
** Now acquire memory for the kernel. The code for RISCOS 3.7 and below just
** grabs memory and assumes it can be used. The RISCOS 4 code is more polite
** and uses a new RISCOS 4 OS_Memory call to request the memory it needs
*/
    if (os_version<RISCOS_40) {
      fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8), 0,
        KERNAREA, 0, KERNBASE, 0x80, 0x1000, 0, 0, "Kate Kernel"));
    }
    else {	/* RISCOS 4 */
/*
** This code requests a block of 'kernelsize' bytes of contiguous physical
** memory aligned on a 4 megabyte boundary. The page number of the first page
** is returned in 'first_page'. When the dynamic area is created, RISCOS
** calls our pregrow handler which has to allocate pages from this block to
** the dynamic area. Pages will be in ascending order, which is just what is
** needed for the kernel.
** The reason for acquiring memory on a such a strange boundary is that
** the OS_Memory call appears to allocate memory from the bottom end of the
** physical memory. OTOH, the kernel initialisation code assumes that it
** is at the top end and can do what it likes to the bottom end and it all
** ends in tears when the kernel corrupts itself. Aligning the memory on a
** four meg boundary should ensure that the kernel will not trash itself.
** There is a chance that the memory will not be available but the attempt
** to boot NetBSD will at least fail gracefully with a SWI error.
*/
#define BOUND4M 22
#define BOUND2M 21

      fatal(swix(OS_Memory, IN(R0|R1|R2)|OUT(R3), 12, kernelsize, BOUND4M, &first_page));
      physical = first_page;
      fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8), 0,
        KERNAREA, kernelsize, KERNBASE, 0x100, kernelsize, handler, -1, "Kate Kernel"));

/* Work out the physical address corresponding to the first page number */

      osmem_block.physical_page = physical;
      fatal(swix(OS_Memory, IN(R0|R1|R2), 0+OSMEM_PNTOP, &osmem_block, 1));
      physical = osmem_block.physical_addr;
      if (strstr(__cli, "verbose") != 0)
        printf("Page number of start of page range for kernel = %x\r\n", first_page);
/*
** Clear the memory. The kernel appears to reference uninitialised storage
** which was making booting kernels with RISCOS 4 very unreliable
*/
      memset((char *)KERNBASE, 0, kernelsize);
    }

/* Shutdown RiscOS cleanly ... */

/* Close all open files and shutdown filing systems */

    if (strstr(__cli, "noboot") == 0)
      {
        swix(OS_FSControl, IN(R0), 23);

/* Issue a pre-reset service call to reset the podules */

        swix(OS_ServiceCall, IN(R1), 0x45);

/* Kill the etherH module to avoid locks up on reboot */

/*        swix(OS_Module, IN(R0|R1), 4, "EtherH");*/
      }

/* More user information describing the memory found */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("%d DRAM blocks, %d VRAM blocks\n", bootconfig.dramblocks,
          bootconfig.vramblocks);
        printf("DRAM bank 0a = %08x %08x\n", bootconfig.dram[0].address,
          bootconfig.dram[0].pages * bootconfig.pagesize);
        printf("DRAM bank 0b = %08x %08x\n", bootconfig.dram[1].address,
          bootconfig.dram[1].pages * bootconfig.pagesize);
        printf("DRAM bank 1a = %08x %08x\n", bootconfig.dram[2].address,
          bootconfig.dram[2].pages * bootconfig.pagesize);
        printf("DRAM bank 1b = %08x %08x\n", bootconfig.dram[3].address,
          bootconfig.dram[3].pages * bootconfig.pagesize);
        printf("VRAM bank 0  = %08x %08x\n", bootconfig.vram[0].address,
          bootconfig.vram[0].pages * bootconfig.pagesize);
      }

/*
 * Find the number of the upper most bank of DRAM available
 * that is big enough
 */

    loop = 3;
    while ((bootconfig.dram[loop].address == 0
     || bootconfig.dram[loop].pages < 512) && loop >= 0)
      --loop;

    if (loop == -1)
      {
        printf("Error: No suitable DRAM block available for kernel\n");
        return(1);
      }

/*
** For releases of RISCOS prior to 4.0, grab the physical memory where the
** kernel will go. There is a sanity check in here to ensure that the memory
** is not in use but it is only carried out if the option 'checkphysical'
** is supplied. I think it would be useful to make this the default.
*/
    if (os_version<RISCOS_40) {
      unsigned int phys;
      physical = bootconfig.dram[loop].address - kernelsize
             + bootconfig.dram[loop].pages * bootconfig.pagesize;

      if (strstr(__cli, "checkphysical") != 0) {
        if (strstr(__cli, "verbose") != 0) printf("Checking that memory for kernel is not in use\n\r");
        for (phys = physical; phys<physical+kernelsize; phys+=bootconfig.pagesize) {
          osmem_block.physical_addr = phys;
          if (swix(OS_Memory, IN(R0|R1|R2), 0+OSMEM_PTOL, &osmem_block, 1)==NULL) {	/* No error - address is in use  */
            printf("Error: Memory required for RiscBSD kernel is already in use\n\r");
            return(0);
          }
        }
        if (strstr(__cli, "verbose") != 0) printf("Memory for kernel is unassigned\n\r");
      }
    }

/* Jump to SVC26 mode - remember we have no local vars now ! */

    EnterOS();

/* Allocate the physical addresses for the kernel in this bank */

    bootconfig.kernphysicalbase = physical;
    bootconfig.argphysicalbase = physical+(bootconfig.argvirtualbase-bootconfig.kernvirtualbase);
    bootconfig.scratchphysicalbase = physical+(bootconfig.scratchvirtualbase-bootconfig.kernvirtualbase);
/* Yet more debugging info */

    if (strstr(__cli, "verbose") != 0)
      {
        printf("buffer = %08x\n", buffer);
        printf("physical = %08x\n", physical);
        printf("bootconfig.kernphysicalbase = %08x\n",
          bootconfig.kernphysicalbase);
        printf("bootconfig.argphysicalbase = %08x\n",
          bootconfig.argphysicalbase);
        printf("bootconfig.scratchphysicalbase = %08x\n",
          bootconfig.scratchphysicalbase);
      }

/* Get out clause */

    if (strstr(__cli, "noboot") != 0)
      {
        ExitOS();
#ifdef USE_MODULEAREA

/* Free memory in module area to hold the data we were loading */

        fatal(swix(OS_Module, IN(R0|R2), 7, buffer));

#endif
        return(0);
      }

/*
** Okay, for RISCOS versions prior to 4.0 the next step is to map the
** physical memory that was procured by somewhat dubious means to the
** virtual address range occupied by the kernel. A table of all of the
** page numbers and logical addresses is built and then the whole lot
** passed to OS_SetMemMapEntries. This will create the necessary level 2
** page table entries with the right permission bits. This step is not
** needed for RISCOS 4 as the page table would have been updated when the
** dynamic area for the kernel was created
*/
    if (os_version<RISCOS_40) {
      memmap_size = (kernelsize/bootconfig.pagesize+1)*sizeof(memmap_pte);
      kill_area("Kate Memmap");
      fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8)|OUT(R3), 0,
       -1, memmap_size, -1, 0x80, memmap_size, 0, 0, "Kate Memmap", &memmap_base));
      if (strstr(__cli, "verbose") != 0)
       printf("Mapping memory for kernel. Map is at %08x, size=%08x\n\r", memmap_base, memmap_size);

      memmap_next = memmap_base;
      for (logical = KERNBASE; logical < KERNBASE + kernelsize; logical += bootconfig.pagesize) {
        osmem_block.physical_addr = physical;
        fatal(swix(OS_Memory, IN(R0|R1|R2), 0+OSMEM_PTOPN, &osmem_block, 1));
        memmap_next->memmap_page = osmem_block.physical_page;
        memmap_next->memmap_addr = logical;
        memmap_next->memmap_protection = 0;
        physical+=bootconfig.pagesize;
        memmap_next++;
      }
      memmap_next->memmap_page = -1;	/* -1 in last entry = end of list */
      fatal(swix(OS_SetMemMapEntries, IN(R0), memmap_base));
    }

/*
** Map the IO up high so we can get at it.
**
** There is no OS call provided to do this so the only option is to go in and
** update the page translate table directly. The only snag is that the address
** of this is undocumented and varies with the version of RISCOS.
*/
    if (strstr(__cli, "verbose") != 0) printf("Mapping I/O\n\r");

    if (os_version>=RISCOS_40)
      pagetable_base = RO40PAGE_BASE;
    else
      pagetable_base = RO3XPAGE_BASE;

    WriteWord(pagetable_base+0xc000 + (0xf6000000 >> 18) & 0xfffffffc,
      0x00000412 | (0x03200000 & 0xfffff000));		/* IOMD */
    WriteWord(pagetable_base+0xc000 + (0xf6100000 >> 18) & 0xfffffffc,
      0x00000412 | (0x03400000 & 0xfffff000));		/* VIDC */

    memset((char *)bootconfig.display_start, 0xcc, 0x4000);

/* Disable IRQ and FIQ interrupts */

    SetCPSR(I32_bit | F32_bit, I32_bit | F32_bit);

    memset((char *)bootconfig.display_start + 0x4000, 0x55, 0x4000);

    memcpy((char *)bootconfig.argvirtualbase, __cli, bootconfig.argsize);

    memset((char *)bootconfig.display_start + 0x8000, 0x80, 0x4000);

    memset((char *)bootconfig.argvirtualbase, SCRATCHSIZE, 0);

    memset((char *)bootconfig.display_start + 0xC000, 0xbb, 0x4000);

/* Locate the start of the text area */

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
        buffer += sizeof(aout_t);
	break;
      case ZMAGIC:
      default:
        break;
    }

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
      case ZMAGIC:
        if (aout.a_syms == 0)
          ((aout_t *)buffer)->a_syms = 0;
        break;
      default:
        break;
    }

    memcpy((char *)bootconfig.kernvirtualbase, buffer, (int)aout.a_text);

    memset((char *)(bootconfig.display_start + 0x10000), 0xee, 0x4000);

    memcpy((char *)(bootconfig.kernvirtualbase + aout.a_text),
      (buffer + aout.a_text), (int)aout.a_data);

    memset((char *)(bootconfig.display_start + 0x14000), 0x66, 0x4000);

    if (aout.a_syms != 0) {
      ptr = bootconfig.kernvirtualbase + (u_int)(aout.a_text
        + aout.a_data + aout.a_bss);
      *((int *)ptr) = (int)aout.a_syms;
      ptr += sizeof(int);
      memcpy((char *)ptr, (buffer + aout.a_text
        + aout.a_data), (int)aout.a_syms);
      ptr += (int)aout.a_syms;
      memcpy((char *)ptr, (buffer + aout.a_text
        + aout.a_data + aout.a_syms), stringtablesize);
    }

/*
** After all of our messing about, let's make sure that the memory and
** caches are in sync. This might not be needed but it won't do any harm
*/
    swix(OS_MMUControl, IN(R0|R1), 1+0xC0000000, 0);

    memset((char *)bootconfig.display_start + 0x18000, 0xaa, 0x4000);

/* Real late debugging get out clause */

    if (strstr(__cli, "nearboot") != 0)
      {
        SetCPSR(I32_bit | F32_bit, 0);
        ExitOS();
        return(0);
      }

/* Punch into SVC32 mode */

    memset((char *)bootconfig.display_start + 0x1C000, 0xff, 0x4000);

    SVC32();

/* Point of no return */

    switch (aout.a_magic & MAGICMASK) {
      case OMAGIC:
      case NMAGIC:
      case ZMAGIC:
        _bsdboot(&bootconfig, (unsigned int)aout.a_entry);
        break;
      default:
        _bsdboot(&bootconfig, KERNBASE);
        break;
    }

    return(0);
  }


/* Report an error */

void fatal(struct Error *error)
  {
    if (error)
        swi(OS_GenerateError, IN(R0), error);
  }


/* Report an error */

void fatal1(int error, char *string)
  {
    int err[64];

    err[0] = error;
    strcpy((char *)&err[1], string);
    swi(OS_GenerateError, IN(R0), err);
  }


/* Locate all the blocks of memory in the system */

unsigned char *locate_memory_blocks(void)
  {
    int loop;
    int page;
    int currentpage;
    int currentpages;
    int currentaddr;
    unsigned char *table;
    unsigned int pagesize;
    unsigned int tablesize;
    int dramblocks = 0;
    int vramblocks = 0;

/* Get table size and page size */

    fatal(swix(OS_Memory, IN(R0)|OUT(R1|R2), 6, &tablesize, &pagesize));

/* Allocate memory for table */

/*#ifdef USE_MODULEAREA*/

    fatal(swix(OS_Module, IN(R0|R3)|OUT(R2), 6, tablesize, &table));

/*#else*/

/* Allocate memory to hold the data we are loading */

/*    swix(OS_DynamicArea, IN(R0|R1), 1, TABLEAREA);

    fatal(swix(OS_DynamicArea, IN(R0|R1|R2|R3|R4|R5|R6|R7|R8)|OUT(R3), 0,
      TABLEAREA, tablesize, -1, 0x80, tablesize, 0, 0, "Kate Table", &table));

#endif*/


/* read the table */

    fatal(swix(OS_Memory, IN(R0|R1), 7, table));

/* Loop round locating all the valid blocks of memory */

    currentpage = -1;

    for (loop = 0; loop < tablesize * 2; ++loop)
      {
        page = table[loop / 2];
        if (loop % 2)
          page = page >> 4;

        page = page & 0x07;

        if (page != currentpage)
          {
            switch (currentpage)
              {
                case 1:
                  bootconfig.dram[dramblocks].address = currentaddr * pagesize;
                  bootconfig.dram[dramblocks].pages = currentpages;
                  ++dramblocks;
                  break;

                case 2:
                  bootconfig.vram[vramblocks].address = currentaddr * pagesize;
                  bootconfig.vram[vramblocks].pages = currentpages;
                  ++vramblocks;
                  break;

                default :
                  break;
              }

            currentpage = page;
            currentaddr = loop;
            currentpages = 0;
          }
        ++currentpages;
      }

/* Get the number of dram and vram pages */

    fatal(swix(OS_Memory, IN(R0)|OUT(R1), 0x00000108, &bootconfig.drampages));
    fatal(swix(OS_Memory, IN(R0)|OUT(R1), 0x00000208, &bootconfig.vrampages));

/* Fill in more bootconfig parameters */

    bootconfig.pagesize = pagesize;

    bootconfig.dramblocks = dramblocks;
    bootconfig.vramblocks = vramblocks;

    return(table);
  }


void putchar(int c)
  {
    if (c == '\n')
      swi(OS_WriteC, IN(R0), '\r');

    swi(OS_WriteC, IN(R0), c);
  }


/* Kill off an existing version of a dynamic area */

int kill_area(char *wanted) {
  int areano;
  char *areaname;
  areano = -1;
  do {
    swix(OS_DynamicArea, IN(R0|R1) | OUT(R1), 3, areano, &areano);
    if (areano<0) return 0;	    /* Last area */
    swix(OS_DynamicArea, IN(R0|R1) | OUT(R8), 2, areano, &areaname);
    if (strncmp(areaname, wanted, 99)==0) {
      if (strstr(__cli, "verbose") != 0)  printf("Removing %s, dynamic area number = %x\n", areaname, areano);
      swix(OS_DynamicArea, IN(R0|R1), 1, areano);
      return 1;
    }
  } while(areano>=0);
  return 0;	/* Should never get this far */
}


/* End of bsdbooter.c */
