/*****************************************************************************

       Copyright © 1995, 1996 Digital Equipment Corporation,
                       Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, provided  
that the copyright notice and this permission notice appear in all copies  
of software and supporting documentation, and that the name of Digital not  
be used in advertising or publicity pertaining to distribution of the software 
without specific, written prior permission. Digital grants this permission 
provided that you prominently mark, as not part of the original, any 
modifications made to this software or documentation.

Digital Equipment Corporation disclaims all warranties and/or guarantees  
with regard to this software, including all implied warranties of fitness for 
a particular purpose and merchantability, and makes no representations 
regarding the use of, or the results of the use of, the software and 
documentation in terms of correctness, accuracy, reliability, currentness or
otherwise; and you rely on the software, documentation and results solely at 
your own risk. 

******************************************************************************/
/*
 *  Main routine for the miniloader.
 *
 *  david.rusling@reo.mts.dec.com
 */
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/pci.h>
#include <linux/bios32.h>
#include <linux/version.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/console.h>
#include <asm/hwrpb.h>
#include <asm/page.h>
#include <asm/pgtable.h>

#include <stdarg.h>

#include "milo.h"
#include "osfboot.h"
#include "impure.h"
#include "uart.h"
#include "fs.h"
#include "smc.h"

#ifndef NULL
#define NULL ((void *)0)
#endif

/*****************************************************************************
 *  Build Controlling Definitions.                                           *
 *****************************************************************************/
/*
 *  These are not in config.in as they'd clutter things up too much.  So, they
 *  are here.
 */
/* DEBUG_MINIBOOT: this turns on much more printk() statements.  Defining this
 * *may* help you find a problem */
#undef DEBUG_MINIBOOT 

/* DEBUG_USE_DBM: this allows you to use the Evaluation Board minidebugger to
 * step through the code. */
#undef DEBUG_USE_DBM 

/* FLOPPY_HACK: this means that code will explicitly go and set the floppy to a 
   known state before anything else happens */
#if !defined(CONFIG_ALPHA_NONAME) && !defined(CONFIG_ALPHA_AVANTI)
#define FLOPPY_HACK 1
#endif

#if 0
/* DEFAULT_MEMORY_SIZE: define this and we don't go look in the PALcode impure
   area to figure out the memory size */
/* hack trying to figure out if this is a Universal Desktop box */
#if defined(CONFIG_ALPHA_NONAME) && defined(MINI_TGA) 
#define DEFAULT_MEMORY_SIZE 0x1800000
#else
#define DEFAULT_MEMORY_SIZE 0x1000000
#endif
#endif

/*****************************************************************************
 *  Global variables.                                                        *
 *****************************************************************************/
/*
 *  For device drivers and so on.
 */
#ifndef MINI_EMBED_LINUX
int ramdisk_size;
#endif
/*
 *  memory allocation
 */
U64 milo_memory_size = 0;

/*
 *  memory mapping
 */
unsigned int ptbr = 0;
U64 vptbr = 0;
struct pcb_struct boot_pcb;

unsigned long high_memory = 0 ;

/*
 *  Someplace to put the arguments to the kernel.
 */
U64 *pkernel_args;
U64 kernel_at;
U64 kernel_entry;
U64 kernel_pages;

/*
 *  Counting stuff.
 */
unsigned int num_l2_pages = 0;
unsigned int num_l3_pages = 0;

struct hae hae = {
  0, 
  (unsigned long *) HAE_ADDRESS
  };


struct kernel_stat kstat;

/*
 *  What type of alpha system is this *realy*
 */
int alpha_sys_type;

/*****************************************************************************
 *  Macros.                                                                  *
 *****************************************************************************/
#ifndef TRUE 
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define PASS1_LCA_TYPE	       0x100000004ULL
					/* Pass 1 LCA chip detect */

/*
 *  External stuff.
 */
extern void __start_again(void);

/*
 *  Forward declarations.
 */
static int check_image_header(char *header);
void __main(void);
void boot_main_cont(void);

#ifdef CONFIG_BLK_DEV_FD

#ifdef FLOPPY_HACK

/* fda */
#define FDADCR	0x03F2
#define FDAMSR	0x03F4
#define FDADR	0x03F5
#define FDADRR	0x03F7
#define FDADCB	0x03F7

/* dcr */
#define DCRSELA	0x00
#define DCRSELB	0x01
#define DCRSELC	0x02
#define DCRSELD	0x03
#define DCRNRES	0x04
#define DCRENAB	0x08
#define DCRMTRA	0x10
#define DCRMTRB	0x20
#define DCRMTRC	0x40
#define DCRMTRD	0x80

/* msr */
#define MSRDIO	0x40
#define MSRRQM	0x80

/* drr */
#define DRR500	0x00
#define DRR250	0x02

/* dcb */
#define DCBNCHG	0x80

/* st0 */
#define ST0IC	0xC0
#define ST0NT	0x00
#define ST0NR	0x08

/* st1 */
#define ST1NW	0x02

/* cmd */
#define NREAD	0x66
#define NWRITE	0x45
#define NRECAL	0x07
#define NSEEK	0x0F
#define NSINTS	0x08
#define NSPEC	0x03

#define FDTOMEM	0
#define MEMTOFD	1

#define NCMD	9
#define NSTS	7

#define UNITNUM	0
#define UNITSEL	(DCRSELA)
#define UNITMTR	(DCRMTRA)

#define lSRT	0xE0			/* 4 mS                          */
#define lHUT	0x08			/* 256 mS                        */

#define hSRT	0xC0			/* 4 mS                          */
#define hHUT	0x0F			/* 256 mS                        */

#define HLT	0x02			/* 4 mS                          */
#define ND	0x00			/* Use DMA                       */

#define SRT	0xE0			/* 4 mS                          */
#define HUT	0x08			/* 256 mS                        */
#define HLT	0x02			/* 4 mS                          */
#define ND	0x00			/* Use DMA                       */

static void fdasleep(int nmsec)
{
    int i;

    while (nmsec--) {
	i = 50000;
	do {
	} while (--i);
    }
}

static int fdasts(void) 
{
    int nsts;
    int byte;
    char sts[NSTS];

#ifdef DEBUG_MINIBOOT
    int i;

#endif

    nsts = 0;
    for (;;) {
	while (((byte = inb(FDAMSR)) & MSRRQM) == 0)
	    ;
	if ((byte & MSRDIO) == 0)
	    break;
	byte = inb(FDADR);
	if (nsts < NSTS)
	    sts[nsts++] = byte;
    }

#if defined(DEBUG_MINIBOOT)
    printk("sts:");
    for (i = 0; i < nsts; ++i)
	printk(" %02x", sts[i] & 0xFF);
    printk("\n");
#endif

    if ((sts[0] & ST0IC) != ST0NT) {
	return (0);
    }
    return (1);
}

static void fdacmd(char cmd[], int ncmd)
{
    int i;

#if defined(DEBUG_MINIBOOT)
    printk("cmd:");
    for (i = 0; i < ncmd; ++i)
	printk(" %02x", cmd[i] & 0xFF);
    printk("\n");
#endif

    for (i = 0; i < ncmd; ++i) {
	while ((inb(FDAMSR) & (MSRRQM | MSRDIO)) != MSRRQM)
	    ;
	outb(cmd[i], FDADR);
    }
}

static void floppy_init_hack(void) 
{
    char cmd[1];

#ifdef MINI_SERIAL_ECHO
    printk("hack alert! Initialising the floppy\n");
#endif

    outb(DCRENAB | DCRNRES, FDADCR);
    fdasleep(50);
    outb(DRR250, FDADRR);
    cmd[0] = NSINTS;
    fdacmd(cmd, 1);
    (void) fdasts();
    cmd[0] = NSPEC;
    cmd[1] = SRT | HUT;
    cmd[2] = HLT | ND;
    fdacmd(cmd, 3);
}
#endif

#endif

/*****************************************************************************
 *  Swap to a new PALcode                                                    *
 *****************************************************************************/
void swap_to_palcode(void *palbase, void *pc, void *ksp, U64 new_ptbr, 
  void *new_vptbr)
{
    extern void swppal(void *, void *, struct pcb_struct *, void *);
/*
 *  Set up the initial pcb.
 */

    boot_pcb.ksp = (U64) ksp;
    boot_pcb.usp = 0;
    boot_pcb.ptbr = new_ptbr;
    boot_pcb.pcc = 0;
    boot_pcb.asn = 0;
    boot_pcb.unique = 0;
    boot_pcb.flags = 1;

/*
 *  Call swppal.  The arguments are:
 *
 *  a0:      PAL address
 *  a1:      PC
 *  a2:      pcb
 *  a3:      vptbr
 */
#if 0
    halt();
#else
    swppal(palbase, pc, &boot_pcb, new_vptbr);
#endif
}

/*****************************************************************************
 *  Kernel Loading code                                                      *
 *****************************************************************************/
/*
 *  checks that the kernel header passed is valid.  Note that this code assumes
 *  that all sections of the header are in memory and contiguous with each other.
 */
static int check_image_header(char *header)
{
    FILEHDR *fh;
    AOUTHDR *ah;
    SCNHDR *sh;

    fh = (FILEHDR *) header;
    ah = (AOUTHDR *) (header + sizeof(FILEHDR));
    sh = (SCNHDR *) (header + sizeof(FILEHDR) + sizeof(AOUTHDR));

#ifdef DEBUG_MINIBOOT
    printk("...filehdr @0x%p, fh->f_opthdr = 0x%x, aouthdr @0x%p\n", 
      (void *) fh, fh->f_opthdr, (void *) ah);
    printk("   f_magic: 0x%x, f_nscns: 0x%x, f_timdat: 0x%x\n", 
      fh->f_magic, fh->f_nscns, fh->f_timdat);
    printk("   f_symptr: 0x%X, f_nsyms: %d\n", fh->f_symptr, fh->f_nsyms);
    printk("   f_opthdr: 0x%x, f_flags: 0x%x\n", fh->f_opthdr, fh->f_flags);

    printk("...aouthdr fields:\n");
    printk("   magic: 0%o, vstamp: 0x%x, bldrev: 0x%x, padcell: 0x%x\n", 
      ah->magic, ah->vstamp, ah->bldrev, ah->padcell);
    printk("   text_start: %x, ", ah->text_start);
    printk("   tsize: %p\n", ah->tsize);
    printk("   data_start: %p, ", ah->data_start);
    printk("   dsize: %p\n", ah->dsize);
    printk("   bss_start: %p, ", ah->bss_start);
    printk("   bsize: %p\n", ah->bsize);
    printk("   entry: %p\n", ah->entry);
    printk("   gprmask: 0x%x, fprmask: 0x%x, gp_value: 0x%p\n", 
      ah->gprmask, ah->fprmask, 
      ah->gp_value);
#endif

    /* Do some sanity checks... */
    if (fh->f_magic != ALPHAMAGIC) {
	printk("Bad filehdr magic number 0x%x should be 0x%x\n", 
	  fh->f_magic, ALPHAMAGIC);
	return -1;
    }
    if ((ah->magic != OMAGIC) && (ah->magic != ZMAGIC)) {
	printk("Bad aouthdr magic number 0x%x should be 0x%x\n", 
	  ah->magic, OMAGIC);
	return -1;
    }
    if (ah->text_start != START_ADDR) {
	printk("Kernel must start at virtual address 0x%lx, not 0x%Lx\n", 
	  START_ADDR, ah->text_start);
	return -1;
    }

    return 0;
}
/*
 *  Returns the entry point of the kernel, or zero if kernel fails to load.
 */
U64 load_image_from_memory(char *address, unsigned int pages)
{
    FILEHDR *fh;
    AOUTHDR *ah;
    SCNHDR *sh;

    int offset;

    int i;

    unsigned char *ptr;

#ifdef DEBUG_MINIBOOT
    printk("Loading image from memory (at 0x%p)\n", address);
#endif
/*
 *  Get pointers to the sections - all in the first buffer read.
 */
    fh = (FILEHDR *) address;
    ah = (AOUTHDR *) (address + sizeof(FILEHDR));
    sh = (SCNHDR *) (address + sizeof(FILEHDR) + sizeof(AOUTHDR));

    /* check the kernel header */
    if (check_image_header((char *) address) < 0) return 0;

    /* save the entry point */
    kernel_at = ah->text_start;
    kernel_at |= IDENT_ADDR;
    kernel_entry = ah->entry;
    kernel_entry |= IDENT_ADDR;

    /* How many pages does the kernel take up?  Don't forget to include the
     * pages between KERNEL_START and START_ADDR */
    kernel_pages = 
      (ah->tsize + ah->dsize + ah->bsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
    kernel_pages += 
      ((kernel_entry - KERNEL_START + PAGE_SIZE - 1) >> PAGE_SHIFT);

#ifdef DEBUG_MINIBOOT
    printk("Kernel uses %d page(s)\n", kernel_pages);
    printk("   Text: 0x%x - 0x%x\n", ah->text_start, 
      ah->text_start + ah->tsize - 1);
    printk("   Data: 0x%x - 0x%x\n", ah->data_start, 
      ah->data_start + ah->dsize - 1);
    printk("   BSS: 0x%x - 0x%x\n", ah->bss_start, 
      ah->bss_start + ah->bsize - 1);
#endif

/*
 *  Copy the kernel.
 */
#ifdef DEBUG_MINIBOOT
    printk("...copying the Kernel into physical memory @ 0x%p\n", kernel_at);
#endif

    offset = sh->s_scnptr;
    memcpy((char *) kernel_at, address + offset, ah->tsize + ah->dsize);
/*
 *  Zero the bss part of the image.
 */
#ifdef DEBUG_MINIBOOT
    printk("...clearing bss memory between 0x%p and 0x%p\n", ah->bss_start, 
      (char *) ah->bss_start + ah->bsize);
#endif

    /* for some reason, memset does not seem to work in this case */
#if 0
    memset(ah->bss_start, 0, (char *) ah->bss_start + ah->bsize);
#else
    ptr = (unsigned char *) ah->bss_start;
    for (i = 0; i < ah->bsize; i++)
	*ptr++ = 0;
#endif

    return kernel_at;
}
#ifndef MINI_EMBED_LINUX
#define IOBUF_SIZE 1024
/*
 *  Returns the entry point of the kernel, or zero if kernel fails to load.
 */
U64 load_image_from_device(char *fs_type, char *device, char *file)
{
    FILEHDR *fh;
    AOUTHDR *ah;
    SCNHDR *sh;

    int offset;
    int count;

    int channel;
    int fd;
    int blkno;

    char *iobuf;
    char *to;

    unsigned int text_pages;

    unsigned int *ptr;
    char *cptr;

    int zipped;

/*
 * We must read an image from a device
 */

#ifdef BOOT_VERBOSE
    printk("Searching for %s on device %s\n", file, device);
#endif

    channel = device_mount(device, fs_type);
    if (channel)
	return 0;

/*
 *  Look for the file
 */
    fd = __open(file);
#ifdef BOOT_VERBOSE
    printk("...%s\n", fd >= 0 ? "found it" : "failed to find it");
#endif

    if (fd < 0)
	return 0;

/*
 *  Is it a zipped image (.gz suffix)?
 */
    zipped = FALSE;
    cptr = file + strlen(file) - 3;
    if (strcmp((const char *) cptr, (const char *) ".gz") == 0)
	zipped = TRUE;
    else {
	if (strcmp((const char *) cptr, (const char *) ".GZ") == 0)
	    zipped = TRUE;
    }
    if (zipped) {
	char *where;
/*
 *  Uncompress it into some temporary memory.
 */
	where =
	  (char *) kmalloc(START_SIZE + 512, 0) + 512;
#ifdef DEBUG_MINIBOOT
	printk("..uncompressing image into 0x%p\n", where);
#endif
	uncompress_kernel(fd, where);
	kernel_entry = load_image_from_memory((char *) where,
	  _PHYSICAL_TO_PFN(START_SIZE));
    } else {
/*
 *  Allocate an iobuf for the transfers and read the first block of the
 *  file into it (the section headers).  Make it 512 bytes aligned.
 */
	iobuf = kmalloc(IOBUF_SIZE + 512, 0) + 512;
	iobuf = (char *) ((unsigned long) iobuf & (~(512 - 1)));
#ifdef DEBUG_MINIBOOT
	printk("...allocated iobuf at 0x%p\n", iobuf);
#endif
	blkno = 0;			/* logical block number */
	if (__bread(fd, blkno++, iobuf) < 0) {
	  printk("MILO: error reading image (block number = 0)\n");
	  return 0;
	}

/*
 *  Get pointers to the sections - all in the first buffer read.
 */
	fh = (FILEHDR *) iobuf;
	ah = (AOUTHDR *) (iobuf + sizeof(FILEHDR));
	sh = (SCNHDR *) (iobuf + sizeof(FILEHDR) + sizeof(AOUTHDR));

/*
 *  Check out the header
 */
	/* check the kernel header */
	if (check_image_header((char *) iobuf) < 0) return 0;

	text_pages = (ah->tsize + PAGE_SIZE - 1) >> PAGE_SHIFT;

	/* save the entry point */
	kernel_entry = ah->entry;
	kernel_entry |= IDENT_ADDR;
	kernel_at = ah->text_start;
	kernel_at |= IDENT_ADDR;

	/* How many pages does the kernel take up?  Don't forget to include the
	 * pages between KERNEL_START and START_ADDR */
	kernel_pages = 
	  (ah->tsize + ah->dsize + ah->bsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
	kernel_pages += ((kernel_entry - KERNEL_START) >> PAGE_SHIFT);


#ifdef DEBUG_MINIBOOT
	printk("First block loaded; kernel uses %d page(s)\n", kernel_pages);
	printk("   Text: 0x%x - 0x%x\n", ah->text_start, 
	  ah->text_start + ah->tsize - 1);
	printk("   Data: 0x%x - 0x%x\n", ah->data_start, 
	  ah->data_start + ah->dsize - 1);
	printk("   BSS: 0x%x - 0x%x\n", ah->bss_start, 
	  ah->bss_start + ah->bsize - 1);
#endif

/*
 *  Copy the kernel bit by bit from the file into memory.
 */
#ifdef BOOT_VERBOSE
	printk("...copying %s into memory (block size = 0x%X)", 
	  file, IOBUF_SIZE);
	printk(" at physical address 0x%Lx\n", ah->text_start);
#endif
	offset = sh->s_scnptr;
#ifdef BOOT_VERBOSE
	printk("...Text segment offset @0x%x (%d)\n", offset, offset);
#endif
/*
 *  We may have the first part of the image in this block.  Check before
 *  we embark on a reading frenzy.
 */
	count = 0;
	to = (char *) ah->text_start;

	/* read the first part of the image from the iobuf */
	if (offset < IOBUF_SIZE) {
	    int size;

	    size = IOBUF_SIZE - offset;
	    memcpy((char *) kernel_at, iobuf + offset, size);
	    printk("#");

	    count += size;
	    to += size;
	    offset = IOBUF_SIZE;
	}

	/* read the rest of the blocks directly where we want them */
	while (count < (ah->tsize + ah->dsize)) {
	    if (__bread(fd, blkno, to) < 0) {
	      printk("MILO: error reading image (block number = %d)\n", blkno);
	      return 0;
	    }
#if 1
	    printk("#");
#else
	    printk("%08X\r", to);
#endif
	    blkno++ ;
	    to += IOBUF_SIZE;
	    count += IOBUF_SIZE;
	}
	printk("\n");
/*
 *  Zero the bss part of the image.
 */
#ifdef DEBUG_MINIBOOT
	printk("...clearing bss memory between 0x%p and 0x%p\n", 
	  ah->bss_start, 
	  (char *) ah->bss_start + ah->bsize);
#endif

	/* for some reason, memset does not seem to work in this case */
#if 0
	memset(ah->bss_start, 0, (char *) ah->bss_start + ah->bsize);
#endif
	ptr = (unsigned int *) ah->bss_start;
	for (count = 0; count < ah->bsize / sizeof(unsigned int); count++)
	    *ptr++ = 0;

	__close(channel);

    }
#ifdef BOOT_VERBOSE
    printk("...Image loaded into memory, entry @ 0x%Lx\n", kernel_entry);
#endif
    return kernel_entry;
}

#endif

/*****************************************************************************
 *  Virtual address mapping (page table) code.                               *
 *****************************************************************************/
/*
 *  Given all of the information, build a valid PTE.
 */
static pte_t build_pte(unsigned long pfn, pgprot_t protection)
{
    pte_t pte;

    pte_val(pte) = pte_val(mk_pte(_PFN_TO_PHYSICAL(pfn), protection));
    pte_val(pte) = pte_val(pte) | _PAGE_VALID;

#ifdef DEBUG_MINIBOOT
    printk("build_pte: pte = 0x%X,0x%X\n", pte_val(pte) >> 32, pte_val(pte));
#endif
    return pte;
}

/*
 *  Given a VA, make a set of page table entries for it (if they
 *  don't already exist).
 *
 *  Args:
 *
 *  va            virtual address to add page table entries for.
 *  vpfn          the PFN that the virtual address is to be mapped to.
 *  protection    the protection to set the L3 page table entry to (all L1 and
 *                L2 pages are marked as Kernel read/write).
 */
void add_VA(U64 va, unsigned int vpfn, unsigned int protection)
{
    U64 l1, l2, l3;
    unsigned int pfn, newpfn;
    U64 pa;
    pte_t pte;

/*
 *  Dismantle the VA.
 */

    l1 = 0;
    l2 = (va & 0xff800000) >> 20;
    l3 = (va & 0x007fe000) >> 10;

#ifdef DEBUG_MINIBOOT
    printk("...Adding PTEs for virtual address 0x%X, pfn = %X\n", va, vpfn);
    printk("...L1 offset = 0x%x, L2 offset = 0x%x, L3 offset = 0x%x\n", 
      l1, l2, l3);
#endif
/*
 *  Now build a set of entries for it (if they do not already
 *  exist).  In the next blocks of code, pfn is always the pfn of
 *  the current entry and newpfn is the pfn that is referenced from
 *  it.
 */
/*
 *  L1.  The L1 PT PFN is held in the global integer ptbr. 
 */
    pa = _PFN_TO_PHYSICAL(ptbr) + l1;
    if (ReadQ(pa) == 0) {
	newpfn = _ALLOCATE_PFN();	/* for L2 */
	num_l2_pages++;
#ifdef DEBUG_MINIBOOT
	printk("\tAllocated L2 page table at PFN 0x%X (physical = 0x%X)\n", 
	  newpfn, _PFN_TO_PHYSICAL(newpfn));
#endif
	zeropage_phys(newpfn);
	pte = build_pte(newpfn, __pgprot(_PAGE_KWE | _PAGE_KRE));
	WriteQ(pa, pte_val(pte));
    } else
	pte_val(pte) = ReadQ(pa);
#ifdef DEBUG_MINIBOOT
    printk("L1 PTE at PFN(0x%x) + 0x%04X = 0x%X\n", 
      ptbr, l1, ReadQ(pa));
#endif
    pfn = _PHYSICAL_TO_PFN(pte_page(pte));

/*
 *  L2, pfn inherited from L1 entry (which may have been allocated
 *  above).
 */
    pa = _PFN_TO_PHYSICAL(pfn) + l2;
    if (ReadQ(pa) == 0) {
	num_l3_pages++;
	newpfn = _ALLOCATE_PFN();
#ifdef DEBUG_MINIBOOT
	printk("\tAllocated L3 page table at PFN 0x%08X (physical = 0x%X)\n", 
	  newpfn, _PFN_TO_PHYSICAL(newpfn));
#endif
	zeropage_phys(newpfn);
	pte = build_pte(newpfn, __pgprot(_PAGE_KWE | _PAGE_KRE));
	WriteQ(pa, pte_val(pte));
    } else
	pte_val(pte) = ReadQ(pa);
#ifdef DEBUG_MINIBOOT
    printk("L2 PTE at PFN(0x%x) + 0x%04X = 0x%08X\n", 
      pfn, l2, ReadQ(pa));
#endif
    pfn = _PHYSICAL_TO_PFN(pte_page((pte_t) pte));

/*
 *  L3, pfn inherited from L2 entry above.  vpfn contains the page frame
 *  number of the real thing whose PTEs we have just invented above.
 */
    pa = _PFN_TO_PHYSICAL(pfn) + l3;
    if (ReadQ(pa) == 0) {
	pte = build_pte(vpfn, __pgprot(protection));
	WriteQ(pa, pte_val(pte));
    } else
	pte_val(pte) = ReadQ(pa);
#ifdef DEBUG_MINIBOOT
    printk("L3 PTE at PFN(0x%x) + 0x%04X = 0x%08X\n", 
      pfn, l3, ReadQ(pa));
#endif
}

/*
 *  System specific routine that returns the top of memory (ie the
 *  amount of physical memory in the system).
 */
#define CSERVE_K_RD_IMPURE     0x0B

static U64 memory_size(void) 
{
    U64 size = 0 ;
#ifndef DEFAULT_MEMORY_SIZE
    U64 PalImpureBase ;
#endif

#ifdef DEBUG_MINIBOOT
    printk("memory_size(), called\n");
#endif

/* 
 * The MEMORY_SIZE environment variable will override all else 
 */
    size = atoi(getenv("MEMORY_SIZE"));
    if(size > 0) {
      /* convert from Mbytes to bytes */
      size = size*1024*1024;
    }

#ifdef DEFAULT_MEMORY_SIZE
/*
 *  Hardwire the size to something.
 */
    if (size != 0) size = DEFAULT_MEMORY_SIZE;
#else
/*
 *  Try and work out the size from the PALcode impure area.
 */
/*
 *  HACK ALERT! EB66 mem size is at 368 and the EB64+ is at 350
 */
#ifdef CONFIG_ALPHA_EB64
    size = 0x2000000;
#else
    PalImpureBase = (U64)(CNS_Q_BASE + cServe(0, 0, CSERVE_K_RD_IMPURE));
    if ((ReadQ(PalImpureBase + CNS_Q_SIGNATURE) & 0xffffffffUL) 
	== IMPURE_SIGNATURE) {
      size = ReadQ(PalImpureBase + CNS_Q_MEM_SIZE);
    }
#endif

    if (size == 0) {
#if defined(CONFIG_ALPHA_NONAME) 
      printk("Failed determine memory size---using size of 24MB\n");
      size = 0x1800000;
#else
      printk("Failed determine memory size---using size of 32MB\n");
      size = 0x2000000;
#endif
    }

#ifdef DEBUG_MINIBOOT
    printk("memory_size(), returning %Ld\n", size);
#endif
    return size;
#endif /* DEFAULT_MEMORY_SIZE */
}

/*****************************************************************************
 *  Build the Page Tables                                                    *
 *****************************************************************************/
/*
 *  We are running with mapping 1-to-1 physical.  Before we can turn on memory
 *  mapping we must set up the PTEs that we need.
 *
 *  We need memory allocation/mapping set up before we can do this.
 */
static void build_mm(void) 
{
    pte_t pte;

#if defined(BOOT_VERBOSE)
    printk("Building the Page Tables\n");
#endif

    /* use the same pfn that Linux will use later (SWAPPER_PGD (0x300000)).
     * This is the first page in the kernel (KERNEL_START).  It saves us
     * allocating a page and then telling Linux that it isn't available. */

    ptbr = _PHYSICAL_TO_PFN(SWAPPER_PGD);
    zeropage_phys(ptbr);
#if defined(BOOT_VERBOSE)
    printk("...L1 PT (ptbr) at PFN 0x%X, physical address 0x%lx\n", 
	     ptbr, _PFN_TO_PHYSICAL(ptbr));
#endif
/*
 *  The virtual page table base pointer is used as a shortcut to get to the
 *  L3 entry for any given faulting address.  It is a special entry in the L1
 *  page table.  This part is important.   Linux 1.2 sets up the vptbr so that
 *  the *last* entry in the L1 PT contains the L1 pfn.  I honour that here.
 *  Change this at your peril.
 */
#if defined(BOOT_VERBOSE)
    printk("...setting up the virtual page table base register\n");
#endif
    vptbr = 0xfffffffe00000000UL;
    pte = build_pte(ptbr, __pgprot(_PAGE_KWE | _PAGE_KRE));
    WriteQ(_PFN_TO_PHYSICAL(ptbr) + (sizeof(pte) * 0x3ff), pte_val(pte));

/*
 *  Just for now, add the debug monitor and this code in as virtual addresses
 *  matching their physical addresses.
 */
#if 0
    do {
	unsigned long va;
	int pfn;

	printk("...Temporarily mapping in debug monitor\n");
	va = 0;
	pfn = 0;
	while (va < ((LOADER_AT + LOADER_SIZE) & 0xffffffff)) {
	    add_VA(va, pfn, (_PAGE_KWE | _PAGE_KRE));
	    pfn++;
	    va += PAGE_SIZE;
	}
    } while (0);
#endif
}

/*****************************************************************************
 *  Utility routines                                                         *
 *****************************************************************************/
#define NAMES 15
static char *devnames[NAMES] = {"hda", "hdb", "hdc", "hdd", "sda", "sdb", 
  "sdc", "sdd", "sde", "fd", "xda", "xdb", "sr", "scd", NULL};
static int devnums[NAMES] = {0x300, 0x340, 0x1600, 0x1640, 0x800, 0x810, 
  0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0x0B00, 0x0B00, 0x0};

void device_number_to_name(unsigned int device, char *name)
{
    int i;
    int number;

#ifdef DEBUG_MINIBOOT
    printk("parse_device: device is 0x%04x\n", device);
#endif

    /* drop of the trailing number */
    number = device & 0xf;
    device = device & 0xfff0;

    /* look for the device */
    for (i = 0; i < NAMES; i++) {
	if (device == devnums[i]) {

	    /* we've found it */
	    strcpy(name, devnames[i]);

	    if (number != 0) {
		int len = strlen(name);

		name[len] = '0' + number;
		name[len + 1] = '\0';
	    }

#ifdef DEBUG_MINIBOOT
	    printk("...returning %s\n", name);
#endif
	    return;
	}
    }

    strcpy(name, "sda0");
#ifdef DEBUG_MINIBOOT
    printk("...returning %s\n", name);
#endif
}

int device_name_to_number(const char *name)
{
    int i;
    int number, len, dev;

    /* get the true length of the name */
    len = strlen(name);
    while (len > 0 && ('0' <= name[len-1] && name[len-1] <= '9'))
      --len;
    if (name[len] != 0)
      number = atoi(name+len);
    else
      number = -1;

    /* compare it to the list of devices */
    for (i = 0; i < NAMES; i++) {
	if (strncmp(devnames[i], name, len) == 0) {

	    /* we've found it */
	    dev = devnums[i];
	    if (number != -1)
		dev += number;
#ifdef DEBUG_MINIBOOT
	    printk("parse_device_name()...returning 0x%04x\n", dev);
#endif
	    return dev;
	}
    }
    return -1;
}

/*****************************************************************************
 *   Main entry point for system primary bootstrap code                      *
 *****************************************************************************/
/* 
 *
 * At this point we have been loaded into memory at LOADER_AT and we are running in Kernel
 * mode.  The Kernal stack base is at the top of memory (wherever that is).  Assume that it
 * takes up two 8K pages.  We are running 1-to-1 memory mapping (ie physical).
 */
void __main(void) 
{

/*****************************************************************************
 *  Initialize.                                                              *
 *****************************************************************************/
/*
 *  First initialize the Evaulation Board environment.
 */

    /* let's get explicit about some variables we care about. */
    num_l2_pages = num_l3_pages = 0;
    set_hae(hae.cache);

#ifdef CONFIG_ALPHA_PC164
/*
 *  The PC164 employs the SMC FDC37C93X Ultra I/O Controller.
 *  After reset all of the devices are disabled. SMCInit() will
 *  autodetect the FDC37C93X and enable each of the devices.
 */
    SMCInit();
#endif

    /* we can only output to the serial port *after* we initialise it */
    uart_init();

#ifdef FLOPPY_HACK

    /* hack */
    floppy_init_hack();
#endif

    /* change the sp to be under the palcode minus a quad */
    wrsp(PALCODE_AT - 1024);
    wrfen();

    milo_memory_size = high_memory = memory_size();
#if defined(BOOT_VERBOSE)
    printk("...memory size is 0x%Lx\n", milo_memory_size);
#endif
/*
 *  Raise the IPL so that no interrupts can occur
 */
#if defined(BOOT_VERBOSE) 
    printk("...raising ipl from %d to 7\n", ipl(7));
#else
    ipl(7);
#endif

/*****************************************************************************
 *  Build the basic HWRPB (needed by lots of stuff)                          *
 *****************************************************************************/
    init_HWRPB();

/*****************************************************************************
 *  Initialise the memory map                                                *
 *****************************************************************************/
#if defined(BOOT_VERBOSE)
    printk("Calling init_mem()\n");
#endif
    init_mem(milo_memory_size);

/*****************************************************************************
 *  Building the page tables.                                                *
 *****************************************************************************/

    /* we cannot do this until we've set up the memory mapping and are able to 
     * allocate memory for page tables */
    build_mm();

/*****************************************************************************
 *  Swap to the new PALcode                                                  *
 *****************************************************************************/

    /* if we're trying to debug the code, then we cannot swap to a new PALcode.
     *   So, we just carry straight on.  We'll swap to the new PALcode when we 
     * start up Linux */

#ifdef DEBUG_USE_DBM
    boot_main_cont();
#else

    /* swap to the PALcode in this image, but stay in this image and make it
     * 1-to-1 virtual to physical address mapping */
#if defined(BOOT_VERBOSE)
    printk("Swapping to the new PALcode (1-to-1)\n");
#endif

    swap_to_palcode((void *) PALCODE_AT, (void *) __start_again, 
      (void *) (INIT_STACK + PAGE_SIZE), 
      0, (void *) 1);
#endif
}

/*****************************************************************************
 *   Secondary entry point for system primary bootstrap code                 *
 *****************************************************************************/
/* 
 *
 *  At this point we have set up the memory map and swapped to the new PALcode.
 *  VGA is initialised and ipl is at 7.
 */
void boot_main_cont(void) 
{
/*****************************************************************************
 *  Do any CPU specific initialisation required                              *
 *****************************************************************************/

    /* we need the hwrpb set up before we can do this */
    /* This gets us keyboard and VGA (I hope) */
    milo_cpu_init();

/*****************************************************************************
 *  Startup any device drivers required                                      *
 *****************************************************************************/

    /* we need the hwrpb set up before we can do this */
    device_init();

/*****************************************************************************
 *  Initialise the environment variable stuff                                *
 *****************************************************************************/
    env_init();

/*****************************************************************************
 *  Startup the keyboard so that we can get some input.                      *
 *****************************************************************************/
    milo();

/*
 *  If we ever get to this point, something has gone seriously wrong!!!
 */
    printk("If you can read this, something went VERY wrong!\n");
#ifdef NEVER
    while (1) ;
#endif

}

