mirror of
https://gitlab.com/qemu-project/openbios.git
synced 2024-02-13 08:34:06 +08:00
Use firmware configuration device for boot device, kernel, initrd and kernel command line parameters on PPC, Sparc32 and Sparc64. git-svn-id: svn://coreboot.org/openbios/trunk/openbios-devel@479 f158a5a8-5612-0410-a976-696ce0be7e32
535 lines
12 KiB
C
535 lines
12 KiB
C
/* 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 "dict.h"
|
|
#include "openbios/nvram.h"
|
|
#include "sys_info.h"
|
|
#include "openbios.h"
|
|
#include "openbios/pci.h"
|
|
#include "asm/pci.h"
|
|
#include "boot.h"
|
|
#include "../../drivers/timer.h" // XXX
|
|
#define NO_QEMU_PROTOS
|
|
#include "openbios/fw_cfg.h"
|
|
#include "video_subr.h"
|
|
|
|
#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
|
|
|
|
#define NVRAM_ADDR_LO 0x74
|
|
#define NVRAM_ADDR_HI 0x75
|
|
#define NVRAM_DATA 0x77
|
|
|
|
#define APB_SPECIAL_BASE 0x1fe00000000ULL
|
|
#define APB_MEM_BASE 0x1ff00000000ULL
|
|
|
|
#define MEMORY_SIZE (512*1024) /* 512K ram for hosted system */
|
|
#define DICTIONARY_SIZE (256*1024) /* 256K for the dictionary */
|
|
|
|
static ucell *memory;
|
|
|
|
// XXX
|
|
#define NVRAM_SIZE 0x2000
|
|
#define NVRAM_IDPROM 0x1fd8
|
|
#define NVRAM_IDPROM_SIZE 32
|
|
#define NVRAM_OB_START (0)
|
|
#define NVRAM_OB_SIZE ((0x1fd0 - NVRAM_OB_START) & ~15)
|
|
|
|
#define OBIO_CMDLINE_MAX 256
|
|
static char obio_cmdline[OBIO_CMDLINE_MAX];
|
|
|
|
static uint8_t idprom[NVRAM_IDPROM_SIZE];
|
|
|
|
struct hwdef {
|
|
pci_arch_t pci;
|
|
uint16_t machine_id_low, machine_id_high;
|
|
};
|
|
|
|
static const struct hwdef hwdefs[] = {
|
|
{
|
|
.pci = {
|
|
.name = "SUNW,sabre",
|
|
.vendor_id = PCI_VENDOR_ID_SUN,
|
|
.device_id = PCI_DEVICE_ID_SUN_SABRE,
|
|
.cfg_addr = APB_SPECIAL_BASE + 0x1000000ULL,
|
|
.cfg_data = APB_MEM_BASE,
|
|
.cfg_base = 0x80000000ULL,
|
|
.cfg_len = 0,
|
|
.mem_base = APB_MEM_BASE + 0x400000ULL,
|
|
.mem_len = 0x10000000,
|
|
.io_base = APB_SPECIAL_BASE + 0x2000000ULL,
|
|
.io_len = 0x10000,
|
|
.irqs = { 0, 1, 2, 3 },
|
|
},
|
|
.machine_id_low = 0,
|
|
.machine_id_high = 255,
|
|
},
|
|
};
|
|
|
|
struct cpudef {
|
|
unsigned long iu_version;
|
|
const char *name;
|
|
};
|
|
|
|
/*
|
|
( 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, uint32_t clock_frequency)
|
|
{
|
|
unsigned long iu_version;
|
|
|
|
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");
|
|
|
|
PUSH(0);
|
|
fword("encode-int");
|
|
push_str("cpuid");
|
|
fword("property");
|
|
|
|
PUSH(clock_frequency);
|
|
fword("encode-int");
|
|
push_str("clock-frequency");
|
|
fword("property");
|
|
|
|
fword("finish-device");
|
|
|
|
// Trap table
|
|
push_str("/openprom/client-services");
|
|
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 (;;);
|
|
}
|
|
|
|
static uint8_t nvram_read_byte(uint16_t offset)
|
|
{
|
|
outb(offset & 0xff, NVRAM_ADDR_LO);
|
|
outb(offset >> 8, NVRAM_ADDR_HI);
|
|
return inb(NVRAM_DATA);
|
|
}
|
|
|
|
static void nvram_read(uint16_t offset, char *buf, unsigned int nbytes)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < nbytes; i++)
|
|
buf[i] = nvram_read_byte(offset + i);
|
|
}
|
|
|
|
static void nvram_write_byte(uint16_t offset, uint8_t val)
|
|
{
|
|
outb(offset & 0xff, NVRAM_ADDR_LO);
|
|
outb(offset >> 8, NVRAM_ADDR_HI);
|
|
outb(val, NVRAM_DATA);
|
|
}
|
|
|
|
static void nvram_write(uint16_t offset, const char *buf, unsigned int nbytes)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < nbytes; i++)
|
|
nvram_write_byte(offset + i, buf[i]);
|
|
}
|
|
|
|
static uint8_t qemu_uuid[16];
|
|
|
|
void arch_nvram_get(char *data)
|
|
{
|
|
uint32_t size = 0;
|
|
const struct cpudef *cpu;
|
|
const char *bootpath;
|
|
char buf[256];
|
|
uint32_t temp;
|
|
uint64_t ram_size;
|
|
uint32_t clock_frequency;
|
|
uint16_t machine_id;
|
|
const char *stdin_path, *stdout_path;
|
|
const char *kernel_cmdline;
|
|
|
|
fw_cfg_init();
|
|
|
|
fw_cfg_read(FW_CFG_SIGNATURE, buf, 4);
|
|
buf[4] = '\0';
|
|
|
|
printk("Configuration device id %s", buf);
|
|
|
|
temp = fw_cfg_read_i32(FW_CFG_ID);
|
|
machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID);
|
|
|
|
printk(" version %d machine id %d\n", temp, machine_id);
|
|
|
|
if (temp != 1) {
|
|
printk("Incompatible configuration device version, freezing\n");
|
|
for(;;);
|
|
}
|
|
|
|
kernel_size = fw_cfg_read_i32(FW_CFG_KERNEL_SIZE);
|
|
if (kernel_size)
|
|
kernel_image = fw_cfg_read_i64(FW_CFG_KERNEL_ADDR);
|
|
kernel_cmdline = (const char *) fw_cfg_read_i64(FW_CFG_KERNEL_CMDLINE);
|
|
if (kernel_cmdline) {
|
|
size = strlen(kernel_cmdline);
|
|
if (size > OBIO_CMDLINE_MAX - 1)
|
|
size = OBIO_CMDLINE_MAX - 1;
|
|
memcpy(&obio_cmdline, kernel_cmdline, size);
|
|
}
|
|
obio_cmdline[size] = '\0';
|
|
qemu_cmdline = (uint64_t)obio_cmdline;
|
|
cmdline_size = size;
|
|
boot_device = fw_cfg_read_i16(FW_CFG_BOOT_DEVICE);
|
|
|
|
if (kernel_size)
|
|
printk("kernel addr %llx size %llx\n", kernel_image, kernel_size);
|
|
if (size)
|
|
printk("kernel cmdline %s\n", obio_cmdline);
|
|
|
|
nvram_read(NVRAM_OB_START, data, NVRAM_OB_SIZE);
|
|
|
|
temp = fw_cfg_read_i32(FW_CFG_NB_CPUS);
|
|
|
|
printk("CPUs: %x", temp);
|
|
|
|
clock_frequency = 100000000;
|
|
|
|
cpu = id_cpu();
|
|
//cpu->initfn();
|
|
cpu_generic_init(cpu, clock_frequency);
|
|
printk(" x %s\n", cpu->name);
|
|
|
|
// Add /uuid
|
|
fw_cfg_read(FW_CFG_UUID, (char *)qemu_uuid, 16);
|
|
|
|
printk("UUID: " UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1], qemu_uuid[2],
|
|
qemu_uuid[3], qemu_uuid[4], qemu_uuid[5], qemu_uuid[6],
|
|
qemu_uuid[7], qemu_uuid[8], qemu_uuid[9], qemu_uuid[10],
|
|
qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], qemu_uuid[14],
|
|
qemu_uuid[15]);
|
|
|
|
push_str("/");
|
|
fword("find-device");
|
|
|
|
PUSH((long)&qemu_uuid);
|
|
PUSH(16);
|
|
fword("encode-bytes");
|
|
push_str("uuid");
|
|
fword("property");
|
|
|
|
// Add /idprom
|
|
nvram_read(NVRAM_IDPROM, (char *)idprom, NVRAM_IDPROM_SIZE);
|
|
|
|
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");
|
|
|
|
ram_size = fw_cfg_read_i64(FW_CFG_RAM_SIZE);
|
|
|
|
ob_mmu_init(cpu->name, ram_size);
|
|
|
|
push_str("/chosen");
|
|
fword("find-device");
|
|
|
|
if (boot_device == 'c')
|
|
bootpath = "disk:a";
|
|
else
|
|
bootpath = "cdrom:a";
|
|
push_str(bootpath);
|
|
fword("encode-string");
|
|
push_str("bootpath");
|
|
fword("property");
|
|
|
|
push_str(obio_cmdline);
|
|
fword("encode-string");
|
|
push_str("bootargs");
|
|
fword("property");
|
|
|
|
if (fw_cfg_read_i16(FW_CFG_NOGRAPHIC)) {
|
|
stdin_path = stdout_path = "/pci/pci/pci/ebus/su";
|
|
} else {
|
|
stdin_path = "/pci/pci/pci/ebus/kb_ps2";
|
|
stdout_path = "/pci/pci/pci/QEMU,VGA";
|
|
}
|
|
|
|
push_str(stdin_path);
|
|
fword("open-dev");
|
|
fword("encode-int");
|
|
push_str("stdin");
|
|
fword("property");
|
|
|
|
push_str(stdout_path);
|
|
fword("open-dev");
|
|
fword("encode-int");
|
|
push_str("stdout");
|
|
fword("property");
|
|
|
|
push_str(stdin_path);
|
|
push_str("input-device");
|
|
fword("$setenv");
|
|
|
|
push_str(stdout_path);
|
|
push_str("output-device");
|
|
fword("$setenv");
|
|
|
|
push_str(stdin_path);
|
|
fword("input");
|
|
|
|
push_str(stdout_path);
|
|
fword("output");
|
|
}
|
|
|
|
void arch_nvram_put(char *data)
|
|
{
|
|
nvram_write(0, data, NVRAM_OB_SIZE);
|
|
}
|
|
|
|
int arch_nvram_size(void)
|
|
{
|
|
return NVRAM_OB_SIZE;
|
|
}
|
|
|
|
void setup_timers(void)
|
|
{
|
|
}
|
|
|
|
void udelay(unsigned int usecs)
|
|
{
|
|
}
|
|
|
|
static void init_memory(void)
|
|
{
|
|
memory = malloc(MEMORY_SIZE);
|
|
if (!memory)
|
|
printk("panic: not enough memory on host system.\n");
|
|
|
|
/* we push start and end of memory to the stack
|
|
* so that it can be used by the forth word QUIT
|
|
* to initialize the memory allocator
|
|
*/
|
|
|
|
PUSH((ucell)memory);
|
|
PUSH((ucell)memory + (ucell)MEMORY_SIZE);
|
|
}
|
|
|
|
static void
|
|
arch_init( void )
|
|
{
|
|
modules_init();
|
|
#ifdef CONFIG_DRIVER_PCI
|
|
ob_pci_init();
|
|
#endif
|
|
nvconf_init();
|
|
device_end();
|
|
|
|
bind_func("platform-boot", boot );
|
|
}
|
|
|
|
unsigned long isa_io_base;
|
|
|
|
int openbios(void)
|
|
{
|
|
unsigned int i;
|
|
uint16_t machine_id;
|
|
const struct hwdef *hwdef = NULL;
|
|
|
|
|
|
for (i = 0; i < sizeof(hwdefs) / sizeof(struct hwdef); i++) {
|
|
isa_io_base = hwdefs[i].pci.io_base;
|
|
machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID);
|
|
if (hwdefs[i].machine_id_low <= machine_id &&
|
|
hwdefs[i].machine_id_high >= machine_id) {
|
|
hwdef = &hwdefs[i];
|
|
arch = &hwdefs[i].pci;
|
|
break;
|
|
}
|
|
}
|
|
if (!hwdef)
|
|
for(;;); // Internal inconsistency, hang
|
|
|
|
#ifdef CONFIG_DEBUG_CONSOLE
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
uart_init(CONFIG_SERIAL_PORT, CONFIG_SERIAL_SPEED);
|
|
#endif
|
|
/* Clear the screen. */
|
|
cls();
|
|
printk("OpenBIOS for Sparc64\n");
|
|
#endif
|
|
|
|
collect_sys_info(&sys_info);
|
|
|
|
dict = malloc(DICTIONARY_SIZE);
|
|
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");
|
|
free(dict);
|
|
return 0;
|
|
}
|