Files
openbios/arch/sparc64/openbios.c

781 lines
18 KiB
C
Raw Normal View History

/* tag: openbios forth environment, executable code
*
* Copyright (C) 2003 Patrick Mauritz, Stefan Reinauer
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "openbios/config.h"
#include "openbios/bindings.h"
#include "openbios/drivers.h"
#include "asm/types.h"
#include "dict.h"
#include "openbios/kernel.h"
#include "openbios/stack.h"
#include "openbios/nvram.h"
#include "sys_info.h"
#include "openbios.h"
#include "libc/byteorder.h"
#define cpu_to_be16(x) __cpu_to_be16(x)
#include "openbios/firmware_abi.h"
#include "boot.h"
#include "../../drivers/timer.h" // XXX
#include "asi.h"
#include "spitfire.h"
#include "libc/vsprintf.h"
#define REGISTER_NAMED_NODE( name, path ) do { \
bind_new_node( name##_flags_, name##_size_, \
path, name##_m, sizeof(name##_m)/sizeof(method_t)); \
} while(0)
#define REGISTER_NODE_METHODS( name, path ) do { \
const char *paths[1]; \
\
paths[0] = path; \
bind_node( name##_flags_, name##_size_, \
paths, 1, name##_m, sizeof(name##_m)/sizeof(method_t)); \
} while(0)
static unsigned char intdict[256 * 1024];
// XXX
#define NVRAM_SIZE 0x2000
#define NVRAM_IDPROM 0x1fd0
#define NVRAM_OB_START (sizeof(ohwcfg_v3_t) + sizeof(struct sparc_arch_cfg))
#define NVRAM_OB_SIZE ((NVRAM_IDPROM - NVRAM_OB_START) & ~15)
ohwcfg_v3_t nv_info;
#define OBIO_CMDLINE_MAX 256
static char obio_cmdline[OBIO_CMDLINE_MAX];
static uint8_t idprom[32];
struct cpudef {
unsigned long iu_version;
const char *name;
};
#define PAGE_SIZE_4M (4 * 1024 * 1024)
#define PAGE_SIZE_512K (512 * 1024)
#define PAGE_SIZE_64K (64 * 1024)
#define PAGE_SIZE_8K (8 * 1024)
#define PAGE_MASK_4M (4 * 1024 * 1024 - 1)
#define PAGE_MASK_512K (512 * 1024 - 1)
#define PAGE_MASK_64K (64 * 1024 - 1)
#define PAGE_MASK_8K (8 * 1024 - 1)
static void
mmu_open(void)
{
RET(-1);
}
static void
mmu_close(void)
{
}
/*
3.6.5 translate
( virt -- false | phys.lo ... phys.hi mode true )
*/
static void
mmu_translate(void)
{
unsigned long virt, phys, tag, data, mask;
unsigned int i;
virt = POP();
for (i = 0; i < 64; i++) {
data = spitfire_get_dtlb_data(i);
if (data & 0x8000000000000000) { // Valid entry?
switch ((data >> 61) & 3) {
default:
case 0x0: // 8k
mask = 0xffffffffffffe000ULL;
break;
case 0x1: // 64k
mask = 0xffffffffffff0000ULL;
break;
case 0x2: // 512k
mask = 0xfffffffffff80000ULL;
break;
case 0x3: // 4M
mask = 0xffffffffffc00000ULL;
break;
}
tag = spitfire_get_dtlb_tag(i);
if ((virt & mask) == (tag & mask)) {
phys = tag & mask & 0x000001fffffff000;
phys |= virt & ~mask;
PUSH(phys & 0xffffffff);
PUSH(phys >> 32);
PUSH(data & 0xfff);
PUSH(-1);
return;
}
}
}
PUSH(0);
}
static void
dtlb_load2(unsigned long vaddr, unsigned long tte_data)
{
asm("stxa %0, [%1] %2\n"
"stxa %3, [%%g0] %4\n"
: : "r" (vaddr), "r" (48), "i" (ASI_DMMU),
"r" (tte_data), "i" (ASI_DTLB_DATA_IN));
}
/*
( index tte_data vaddr -- ? )
*/
static void
dtlb_load(void)
{
unsigned long vaddr, tte_data, idx;
vaddr = POP();
tte_data = POP();
idx = POP();
dtlb_load2(vaddr, tte_data);
}
static void
itlb_load2(unsigned long vaddr, unsigned long tte_data)
{
asm("stxa %0, [%1] %2\n"
"stxa %3, [%%g0] %4\n"
: : "r" (vaddr), "r" (48), "i" (ASI_IMMU),
"r" (tte_data), "i" (ASI_ITLB_DATA_IN));
}
/*
( index tte_data vaddr -- ? )
*/
static void
itlb_load(void)
{
unsigned long vaddr, tte_data, idx;
vaddr = POP();
tte_data = POP();
idx = POP();
itlb_load2(vaddr, tte_data);
}
static void
map_pages(unsigned long virt, unsigned long size, unsigned long phys)
{
unsigned long tte_data, currsize;
size = (size + PAGE_MASK_8K) & ~PAGE_MASK_8K;
while (size >= PAGE_SIZE_8K) {
currsize = size;
if (currsize >= PAGE_SIZE_4M &&
(virt & PAGE_MASK_4M) == 0 &&
(phys & PAGE_MASK_4M) == 0) {
currsize = PAGE_SIZE_4M;
tte_data = 6ULL << 60;
} else if (currsize >= PAGE_SIZE_512K &&
(virt & PAGE_MASK_512K) == 0 &&
(phys & PAGE_MASK_512K) == 0) {
currsize = PAGE_SIZE_512K;
tte_data = 4ULL << 60;
} else if (currsize >= PAGE_SIZE_64K &&
(virt & PAGE_MASK_64K) == 0 &&
(phys & PAGE_MASK_64K) == 0) {
currsize = PAGE_SIZE_64K;
tte_data = 2ULL << 60;
} else {
currsize = PAGE_SIZE_8K;
tte_data = 0;
}
tte_data |= phys | 0x8000000000000036ULL;
dtlb_load2(virt, tte_data);
itlb_load2(virt, tte_data);
size -= currsize;
phys += currsize;
virt += currsize;
}
}
/*
3.6.5 map
( phys.lo ... phys.hi virt size mode -- )
*/
static void
mmu_map(void)
{
unsigned long virt, size, mode, phys;
mode = POP();
size = POP();
virt = POP();
phys = POP();
phys <<= 32;
phys |= POP();
map_pages(virt, size, phys);
}
/*
3.6.5 unmap
( virt size -- )
*/
static void
mmu_unmap(void)
{
unsigned long virt, size;
size = POP();
virt = POP();
//unmap_pages(virt, size);
}
/*
3.6.5 claim
( virt size align -- base )
*/
static void
mmu_claim(void)
{
unsigned long virt, size, align;
align = POP();
size = POP();
virt = POP();
PUSH(virt); // XXX
}
/*
3.6.5 release
( virt size -- )
*/
static void
mmu_release(void)
{
unsigned long virt, size;
size = POP();
virt = POP();
// XXX
}
DECLARE_UNNAMED_NODE(mmu, INSTALL_OPEN, 0);
NODE_METHODS(mmu) = {
{ "open", mmu_open },
{ "close", mmu_close },
{ "translate", mmu_translate },
{ "SUNW,dtlb-load", dtlb_load },
{ "SUNW,itlb-load", itlb_load },
{ "map", mmu_map },
{ "unmap", mmu_unmap },
{ "claim", mmu_claim },
{ "release", mmu_release },
};
/*
( addr -- ? )
*/
static void
set_trap_table(void)
{
unsigned long addr;
addr = POP();
asm("wrpr %0, %%tba\n"
: : "r" (addr));
}
static void cpu_generic_init(const struct cpudef *cpu)
{
unsigned long iu_version;
char nodebuff[256];
push_str("/");
fword("find-device");
fword("new-device");
push_str(cpu->name);
fword("device-name");
push_str("cpu");
fword("device-type");
asm("rdpr %%ver, %0\n"
: "=r"(iu_version) :);
PUSH((iu_version >> 48) & 0xff);
fword("encode-int");
push_str("manufacturer#");
fword("property");
PUSH((iu_version >> 32) & 0xff);
fword("encode-int");
push_str("implementation#");
fword("property");
PUSH((iu_version >> 24) & 0xff);
fword("encode-int");
push_str("mask#");
fword("property");
PUSH(9);
fword("encode-int");
push_str("sparc-version");
fword("property");
fword("finish-device");
// MMU node
sprintf(nodebuff, "/%s", cpu->name);
push_str(nodebuff);
fword("find-device");
fword("new-device");
push_str("mmu");
fword("device-name");
fword("finish-device");
sprintf(nodebuff, "/%s/mmu", cpu->name);
REGISTER_NODE_METHODS(mmu, nodebuff);
push_str("/chosen");
fword("find-device");
push_str(nodebuff);
fword("open-dev");
fword("encode-int");
push_str("mmu");
fword("property");
// Trap table
push_str("/packages/client-iface");
fword("find-device");
bind_func("SUNW,set-trap-table", set_trap_table);
}
static const struct cpudef sparc_defs[] = {
{
.iu_version = (0x04ULL << 48) | (0x02ULL << 32),
.name = "FJSV,GP",
},
{
.iu_version = (0x04ULL << 48) | (0x03ULL << 32),
.name = "FJSV,GPUSK",
},
{
.iu_version = (0x04ULL << 48) | (0x04ULL << 32),
.name = "FJSV,GPUSC",
},
{
.iu_version = (0x04ULL << 48) | (0x05ULL << 32),
.name = "FJSV,GPUZC",
},
{
.iu_version = (0x17ULL << 48) | (0x10ULL << 32),
.name = "SUNW,UltraSPARC",
},
{
.iu_version = (0x17ULL << 48) | (0x11ULL << 32),
.name = "SUNW,UltraSPARC-II",
},
{
.iu_version = (0x17ULL << 48) | (0x12ULL << 32),
.name = "SUNW,UltraSPARC-IIi",
},
{
.iu_version = (0x17ULL << 48) | (0x13ULL << 32),
.name = "SUNW,UltraSPARC-IIe",
},
{
.iu_version = (0x3eULL << 48) | (0x14ULL << 32),
.name = "SUNW,UltraSPARC-III",
},
{
.iu_version = (0x3eULL << 48) | (0x15ULL << 32),
.name = "SUNW,UltraSPARC-III+",
},
{
.iu_version = (0x3eULL << 48) | (0x16ULL << 32),
.name = "SUNW,UltraSPARC-IIIi",
},
{
.iu_version = (0x3eULL << 48) | (0x18ULL << 32),
.name = "SUNW,UltraSPARC-IV",
},
{
.iu_version = (0x3eULL << 48) | (0x19ULL << 32),
.name = "SUNW,UltraSPARC-IV+",
},
{
.iu_version = (0x3eULL << 48) | (0x22ULL << 32),
.name = "SUNW,UltraSPARC-IIIi+",
},
{
.iu_version = (0x3eULL << 48) | (0x23ULL << 32),
.name = "SUNW,UltraSPARC-T1",
},
{
.iu_version = (0x3eULL << 48) | (0x24ULL << 32),
.name = "SUNW,UltraSPARC-T2",
},
{
.iu_version = (0x22ULL << 48) | (0x10ULL << 32),
.name = "SUNW,UltraSPARC",
},
};
static const struct cpudef *
id_cpu(void)
{
unsigned long iu_version;
unsigned int i;
asm("rdpr %%ver, %0\n"
: "=r"(iu_version) :);
iu_version &= 0xffffffff00000000ULL;
for (i = 0; i < sizeof(sparc_defs)/sizeof(struct cpudef); i++) {
if (iu_version == sparc_defs[i].iu_version)
return &sparc_defs[i];
}
printk("Unknown cpu (psr %lx), freezing!\n", iu_version);
for (;;);
}
void arch_nvram_get(char *data)
{
unsigned short i;
unsigned char *nvptr = (unsigned char *)&nv_info;
uint32_t size;
const struct cpudef *cpu;
const char *bootpath;
for (i = 0; i < sizeof(ohwcfg_v3_t); i++) {
outb(i & 0xff, 0x74);
outb(i >> 8, 0x75);
*nvptr++ = inb(0x77);
}
printk("Nvram id %s, version %d\n", nv_info.struct_ident,
nv_info.struct_version);
if (strcmp(nv_info.struct_ident, "QEMU_BIOS") ||
nv_info.struct_version != 3 ||
OHW_compute_crc(&nv_info, 0x00, 0xF8) != nv_info.crc) {
printk("Unknown nvram, freezing!\n");
for (;;);
}
kernel_image = nv_info.kernel_image;
kernel_size = nv_info.kernel_size;
size = nv_info.cmdline_size;
if (size > OBIO_CMDLINE_MAX - 1)
size = OBIO_CMDLINE_MAX - 1;
memcpy(&obio_cmdline, (void *)(long)nv_info.cmdline, size);
obio_cmdline[size] = '\0';
qemu_cmdline = (uint64_t)obio_cmdline;
cmdline_size = size;
boot_device = nv_info.boot_devices[0];
printk("kernel addr %lx size %lx\n", kernel_image, kernel_size);
if (size)
printk("kernel cmdline %s\n", obio_cmdline);
for (i = 0; i < NVRAM_OB_SIZE; i++) {
outb((i + NVRAM_OB_START) & 0xff, 0x74);
outb((i + NVRAM_OB_START) >> 8, 0x75);
data[i] = inb(0x77);
}
printk("CPUs: %x", nv_info.nb_cpus);
cpu = id_cpu();
//cpu->initfn();
cpu_generic_init(cpu);
printk(" x %s\n", cpu->name);
// Add /idprom
push_str("/");
fword("find-device");
for (i = 0; i < 32; i++) {
outb((i + 0x1fd8) & 0xff, 0x74);
outb((i + 0x1fd8) >> 8, 0x75);
idprom[i] = inb(0x77);
}
PUSH((long)&idprom);
PUSH(32);
fword("encode-bytes");
push_str("idprom");
fword("property");
PUSH(500 * 1000 * 1000);
fword("encode-int");
push_str("clock-frequency");
fword("property");
push_str("/memory");
fword("find-device");
// All memory: 0 to RAM_size
PUSH(0);
fword("encode-int");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((int)(nv_info.RAM0_size >> 32));
fword("encode-int");
fword("encode+");
PUSH((int)(nv_info.RAM0_size & 0xffffffff));
fword("encode-int");
fword("encode+");
push_str("reg");
fword("property");
// Available memory: 0 to va2pa(_start)
PUSH(0);
fword("encode-int");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((va2pa((unsigned long)&_data) - 8192) >> 32);
fword("encode-int");
fword("encode+");
PUSH((va2pa((unsigned long)&_data) - 8192) & 0xffffffff);
fword("encode-int");
fword("encode+");
push_str("available");
fword("property");
// XXX
// Translations
push_str("/virtual-memory");
fword("find-device");
// 0 to 16M: 1:1
PUSH(0);
fword("encode-int");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(16 * 1024 * 1024);
fword("encode-int");
fword("encode+");
PUSH(0x80000000);
fword("encode-int");
fword("encode+");
PUSH(0x00000036);
fword("encode-int");
fword("encode+");
// _start to _data: ROM used
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((unsigned long)&_start);
fword("encode-int");
fword("encode+");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((unsigned long)&_data - (unsigned long)&_start);
fword("encode-int");
fword("encode+");
PUSH(0x800001ff);
fword("encode-int");
fword("encode+");
PUSH(0xf0000074);
fword("encode-int");
fword("encode+");
// _data to _end: end of RAM
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((unsigned long)&_data);
fword("encode-int");
fword("encode+");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH((unsigned long)&_data - (unsigned long)&_start);
fword("encode-int");
fword("encode+");
PUSH(((va2pa((unsigned long)&_data) - 8192) >> 32) | 0x80000000);
fword("encode-int");
fword("encode+");
PUSH(((va2pa((unsigned long)&_data) - 8192) & 0xffffffff) | 0x36);
fword("encode-int");
fword("encode+");
// VGA buffer (128k): 1:1
PUSH(0x1ff);
fword("encode-int");
fword("encode+");
PUSH(0x004a0000);
fword("encode-int");
fword("encode+");
PUSH(0);
fword("encode-int");
fword("encode+");
PUSH(128 * 1024);
fword("encode-int");
fword("encode+");
PUSH(0x800001ff);
fword("encode-int");
fword("encode+");
PUSH(0x004a0076);
fword("encode-int");
fword("encode+");
push_str("translations");
fword("property");
push_str("/chosen");
fword("find-device");
if (nv_info.boot_devices[0] == 'c')
bootpath = "/pci/isa/ide0/disk@0,0:a";
else
bootpath = "/pci/isa/ide1/cdrom@0,0:a";
push_str(bootpath);
fword("encode-string");
push_str("bootpath");
fword("property");
push_str(obio_cmdline);
fword("encode-string");
push_str("bootargs");
fword("property");
}
void arch_nvram_put(char *data)
{
unsigned short i;
for (i = 0; i < NVRAM_OB_SIZE; i++) {
outb((i + NVRAM_OB_START) & 0xff, 0x74);
outb((i + NVRAM_OB_START) >> 8, 0x75);
outb(data[i], 0x77);
}
}
int arch_nvram_size(void)
{
return NVRAM_OB_SIZE;
}
void setup_timers(void)
{
}
void udelay(unsigned int usecs)
{
}
static void init_memory(void)
{
/* push start and end of available memory to the stack
* so that the forth word QUIT can initialize memory
* management. For now we use hardcoded memory between
* 0x10000 and 0x9ffff (576k). If we need more memory
* than that we have serious bloat.
*/
PUSH((ucell)&_heap);
PUSH((ucell)&_eheap);
}
static void
arch_init( void )
{
modules_init();
#ifdef CONFIG_DRIVER_PCI
ob_pci_init();
#endif
#ifdef CONFIG_DRIVER_IDE
setup_timers();
ob_ide_init();
#endif
#ifdef CONFIG_DRIVER_FLOPPY
ob_floppy_init();
#endif
#ifdef CONFIG_DEBUG_CONSOLE_VIDEO
init_video();
#endif
nvram_init();
ob_su_init(0x1fe02000000ULL, 0x3f8ULL, 0);
device_end();
bind_func("platform-boot", boot );
printk("\n"); // XXX needed for boot, why?
}
int openbios(void)
{
#ifdef CONFIG_DEBUG_CONSOLE
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
uart_init(CONFIG_SERIAL_PORT, CONFIG_SERIAL_SPEED);
#endif
#ifdef CONFIG_DEBUG_CONSOLE_VGA
video_init();
#endif
/* Clear the screen. */
cls();
printk("OpenBIOS for Sparc64\n");
#endif
collect_sys_info(&sys_info);
dict=intdict;
load_dictionary((char *)sys_info.dict_start,
(unsigned long)sys_info.dict_end
- (unsigned long)sys_info.dict_start);
#ifdef CONFIG_DEBUG_BOOT
printk("forth started.\n");
printk("initializing memory...");
#endif
init_memory();
#ifdef CONFIG_DEBUG_BOOT
printk("done\n");
#endif
PUSH_xt( bind_noname_func(arch_init) );
fword("PREPOST-initializer");
PC = (ucell)findword("initialize-of");
if (!PC) {
printk("panic: no dictionary entry point.\n");
return -1;
}
#ifdef CONFIG_DEBUG_DICTIONARY
printk("done (%d bytes).\n", dicthead);
printk("Jumping to dictionary...\n");
#endif
enterforth((xt_t)PC);
printk("falling off...\n");
return 0;
}