blob: c3ac97aefaa8bf4c52cbab3025273b71070e44b7 [file] [log] [blame]
#include <string.h>
#include <stddef.h>
#include "hwrpb.h"
#include "osf.h"
#include "uart.h"
#define PAGE_SHIFT 13
#define PAGE_SIZE (1ul << PAGE_SHIFT)
#define PAGE_OFFSET 0xfffffc0000000000UL
#define HZ 1024
struct pcb_struct pal_pcb __attribute__((section(".sbss")));
unsigned long page_dir[1024] __attribute__((aligned(PAGE_SIZE)));
unsigned long page_L1[1024] __attribute__((aligned(PAGE_SIZE)));
unsigned long page_L2[1024][1024] __attribute__((aligned(PAGE_SIZE)));
struct hwrpb_combine {
struct hwrpb_struct hwrpb;
struct percpu_struct processor;
struct memdesc_struct md;
struct memclust_struct mc[2];
} hwrpb __attribute__((section(".data.hwrpb"))) = {
/* This is HWRPB\0\0\0. */
.hwrpb.id = 0x4857525042000000,
.hwrpb.size = sizeof(struct hwrpb_struct),
.hwrpb.pagesize = PAGE_SIZE,
.hwrpb.ssn = "QEMU",
/* ??? This should match TARGET_PHYS_ADDR_SPACE_BITS from qemu. */
.hwrpb.pa_bits = 44,
/* ??? Should we be truethful and say 1 asn, or simply pretend we
have ASNs but ignore them? */
.hwrpb.max_asn = 127,
/* For now, hard-code emulation of sx164. */
.hwrpb.cpuid = PCA56_CPU,
.processor.type = PCA56_CPU,
.hwrpb.sys_type = ST_DEC_EB164,
.hwrpb.sys_variation = 15 << 10,
.hwrpb.sys_revision = 0,
.hwrpb.intr_freq = HZ * 4096,
/* ??? What the hell should we put here. Measure like the kernel will? */
.hwrpb.cycle_freq = 400000000,
.hwrpb.vptb = 0x200000000UL,
.hwrpb.nr_processors = 1,
.hwrpb.processor_size = sizeof(struct percpu_struct),
.hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor),
.hwrpb.mddt_offset = offsetof(struct hwrpb_combine, md),
.md.numclusters = 2,
.mc[0].usage = 2
};
static unsigned long
init_cpuid (void)
{
unsigned long implver, amask;
__asm ("implver %0" : "=r"(implver));
__asm ("amask %1,%0" : "=r"(amask) : "r"(-1));
amask = ~amask;
switch (implver)
{
case 0: /* EV4 */
return EV4_CPU;
case 1: /* EV5 */
if ((amask & 0x101) == 0x101) /* MAX + BWX */
return PCA56_CPU;
if (amask & 1) /* BWX */
return EV56_CPU;
return EV5_CPU;
case 2: /* EV6 */
if (amask & 4) /* CIX */
return EV67_CPU;
return EV6_CPU;
}
return 0;
}
static void
hwrpb_update_checksum (void)
{
unsigned long sum = 0, *l;
for (l = (unsigned long *) &hwrpb.hwrpb; l < &hwrpb.hwrpb.chksum; ++l)
sum += *l;
hwrpb.hwrpb.chksum = sum;
}
static void
init_hwrpb (unsigned long memsize)
{
unsigned long end, pal_pages;
hwrpb.hwrpb.phys_addr = (unsigned int)(unsigned long)&hwrpb;
/* ??? For some reason GCC wants to use a LITERAL relocation for
_end instead of gp-relative relocations. */
__asm ("ldah %0,_end($gp) !gprelhigh\n\tlda %0,_end(%0) !gprellow"
: "=r"(end));
pal_pages = ((unsigned int)end + PAGE_SIZE - 1) >> PAGE_SHIFT;
hwrpb.mc[0].numpages = pal_pages;
hwrpb.mc[1].start_pfn = pal_pages;
hwrpb.mc[1].numpages = (memsize >> PAGE_SHIFT) - pal_pages;
hwrpb_update_checksum ();
}
static void
init_pcb (void)
{
pal_pcb.ptbr = (unsigned int)(unsigned long)page_dir;
pal_pcb.flags = 1;
}
static inline unsigned long
build_pte (void *page)
{
unsigned long bits;
bits = ((unsigned long)page - PAGE_OFFSET) << (32 - PAGE_SHIFT);
bits |= _PAGE_VALID;
return bits;
}
static inline void *
pte_page (unsigned long pte)
{
return (void *)((pte >> 32 << PAGE_SHIFT) + PAGE_OFFSET);
}
static void
set_pte (unsigned long addr, void *page)
{
unsigned long *pt = page_dir;
unsigned long index;
index = (addr >> (PAGE_SHIFT+20)) % 1024;
if (pt[index] != 0)
pt = pte_page (pt[index]);
else
__builtin_trap();
index = (addr >> (PAGE_SHIFT+10)) % 1024;
if (pt[index] != 0)
pt = pte_page (pt[index]);
else
__builtin_trap();
index = (addr >> PAGE_SHIFT) % 1024;
pt[index] = build_pte (page);
}
static void
init_page_table (unsigned long memsize, unsigned long pal_pages)
{
unsigned long i, addr, max_addr, page;
page_dir[0] = build_pte (page_L1);
for (i = 0; i < 1024; ++i)
page_L1[i] = build_pte (page_L2[i]);
set_pte ((unsigned long)INIT_HWRPB, &hwrpb);
addr = 0x20000000ul;
max_addr = 1ul << (PAGE_SHIFT + 20);
page = pal_pages << PAGE_SHIFT;
while (addr < max_addr && page < memsize)
{
set_pte (addr, (void *)(PAGE_OFFSET + page));
addr += PAGE_SIZE;
page += PAGE_SIZE;
}
/* SRM places the self-map for the VPTBR in the second entry. */
page_dir[1] = build_pte (page_dir);
/* Write the SRM vptptr. */
{
register unsigned long a0 __asm__("$16") = max_addr;
__asm ("call_pal 0x2d" : : "r"(a0));
}
}
void
do_start(unsigned long memsize)
{
init_hwrpb (memsize);
init_pcb ();
init_page_table (memsize, hwrpb.mc[0].numpages);
uart_init ();
uart_puts (COM1, "Hello, World!\n");
while (1) ;
}