blob: 9a2ade4c22ad0f93cc964784e750a69a1912230f [file] [log] [blame]
// Glue code for parisc architecture
//
// Copyright (C) 2017-2023 Helge Deller <deller@gmx.de>
// Copyright (C) 2019 Sven Schnelle <svens@stackframe.org>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // GET_BDA
#include "bregs.h" // struct bregs
#include "hw/pic.h" // enable_hwirq
#include "output.h" // debug_enter
#include "stacks.h" // call16_int
#include "string.h" // memset
#include "util.h" // serial_setup
#include "malloc.h" // malloc
#include "hw/serialio.h" // qemu_debug_port
#include "hw/pcidevice.h" // foreachpci
#include "hw/pci.h" // pci_config_readl
#include "hw/pci_ids.h" // PCI IDs
#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0
#include "hw/ata.h"
#include "hw/blockcmd.h" // scsi_is_ready()
#include "hw/rtc.h"
#include "fw/paravirt.h" // PlatformRunningOn
#include "vgahw.h"
#include "parisc/hppa_hardware.h" // DINO_UART_BASE
#include "parisc/pdc.h"
#include "parisc/b160l.h"
#include "parisc/sticore.h"
#include "parisc/lasips2.h"
#include "vgabios.h"
/*
* Various variables which are needed by x86 code.
* Defined here to be able to link seabios.
*/
int HaveRunPost;
u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] __aligned(8);
u8 *StackPos;
u8 __VISIBLE parisc_stack[32*1024] __aligned(64);
volatile int num_online_cpus;
int __VISIBLE toc_lock = 1;
union {
struct pdc_toc_pim_11 pim11;
struct pdc_toc_pim_20 pim20;
} pim_toc_data[HPPA_MAX_CPUS] __VISIBLE __aligned(8);
#define is_64bit() 0 /* we only support 32-bit PDC for now. */
u8 BiosChecksum;
char zonefseg_start, zonefseg_end; // SYMBOLS
char varlow_start, varlow_end, final_varlow_start;
char final_readonly_start;
char code32flat_start, code32flat_end;
char zonelow_base;
struct bios_data_area_s __VISIBLE bios_data_area;
struct vga_bda_s __VISIBLE vga_bios_data_area;
struct floppy_dbt_s diskette_param_table;
struct bregs regs;
unsigned long parisc_vga_mem;
unsigned long parisc_vga_mmio;
struct segoff_s ivt_table[256];
void mtrr_setup(void) { }
void mouse_init(void) { }
void pnp_init(void) { }
u16 get_pnp_offset(void) { return 0; }
void mathcp_setup(void) { }
void smp_setup(void) { }
void bios32_init(void) { }
void yield_toirq(void) { }
void farcall16(struct bregs *callregs) { }
void farcall16big(struct bregs *callregs) { }
void mutex_lock(struct mutex_s *mutex) { }
void mutex_unlock(struct mutex_s *mutex) { }
void start_preempt(void) { }
void finish_preempt(void) { }
int wait_preempt(void) { return 0; }
void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx)
{
*eax = *ebx = *ecx = *edx = 0;
}
void wrmsr_smp(u32 index, u64 val) { }
/********************************************************
* PA-RISC specific constants and functions.
********************************************************/
/* variables provided by qemu - boot_args starting at pad0[10] */
#define ram_size PAGE0->pad0[10]
#define linux_kernel_entry PAGE0->pad0[11]
#define cmdline PAGE0->pad0[12]
#define initrd_start PAGE0->pad0[13]
#define initrd_end PAGE0->pad0[14]
#define smp_cpus PAGE0->pad0[15]
#define pdc_debug PAGE0->pad0[16]
#define fw_cfg_port PAGE0->pad0[17]
/* flags for pdc_debug */
#define DEBUG_PDC 0x01
#define DEBUG_IODC 0x02
#define DEBUG_BOOT_IO 0x04
#define DEBUG_CHASSIS 0x08
int pdc_console;
/* flags for pdc_console */
#define CONSOLE_DEFAULT 0x0000
#define CONSOLE_SERIAL 0x0001
#define CONSOLE_GRAPHICS 0x0002
int sti_font;
/* Want PDC boot menu? Enable via qemu "-boot menu=on" option. */
unsigned int show_boot_menu;
unsigned int interact_ipl;
unsigned long PORT_QEMU_CFG_CTL;
unsigned int tlb_entries = 256;
unsigned int btlb_entries = 8;
#define PARISC_SERIAL_CONSOLE PORT_SERIAL1
extern char pdc_entry;
extern char pdc_entry_table[12];
extern char iodc_entry[512];
extern char iodc_entry_table[14*4];
/* args as handed over for firmware calls */
#define ARG0 arg[7-0]
#define ARG1 arg[7-1]
#define ARG2 arg[7-2]
#define ARG3 arg[7-3]
#define ARG4 arg[7-4]
#define ARG5 arg[7-5]
#define ARG6 arg[7-6]
#define ARG7 arg[7-7]
/* size of I/O block used in HP firmware */
#define FW_BLOCKSIZE 2048
#define MIN_RAM_SIZE (16*1024*1024) // 16 MB
#define MEM_PDC_ENTRY 0x4800 /* as in a B160L */
#define CPU_HPA_IDX(i) (CPU_HPA + (i)*0x1000) /* CPU_HPA of CPU#i */
static int index_of_CPU_HPA(unsigned long hpa) {
int i;
for (i = 0; i < smp_cpus; i++) {
if (hpa == CPU_HPA_IDX(i))
return i;
}
return -1;
}
static unsigned long GoldenMemory = MIN_RAM_SIZE;
static unsigned int chassis_code = 0;
/*
* Emulate the power switch button flag in head section of firmware.
* Bit 31 (the lowest bit) is the status of the power switch.
* This bit is "1" if the button is NOT pressed.
*/
int powersw_nop;
int *powersw_ptr;
void __VISIBLE __noreturn hlt(void)
{
if (pdc_debug)
printf("HALT initiated from %p\n", __builtin_return_address(0));
printf("SeaBIOS wants SYSTEM HALT.\n\n");
/* call qemu artificial "halt" asm instruction */
asm volatile("\t.word 0xfffdead0": : :"memory");
while (1);
}
static void check_powersw_button(void)
{
/* halt immediately if power button was pressed. */
if ((*powersw_ptr & 1) == 0) {
printf("SeaBIOS machine power switch was pressed.\n");
hlt();
}
}
void __VISIBLE __noreturn reset(void)
{
if (pdc_debug)
printf("RESET initiated from %p\n", __builtin_return_address(0));
printf("SeaBIOS wants SYSTEM RESET.\n"
"***************************\n");
check_powersw_button();
PAGE0->imm_soft_boot = 1;
/* call qemu artificial "reset" asm instruction */
asm volatile("\t.word 0xfffdead1": : :"memory");
while (1);
}
#undef BUG_ON
#define BUG_ON(cond) \
if (unlikely(cond)) \
{ printf("ERROR in %s:%d\n", __FUNCTION__, __LINE__); hlt(); }
void flush_data_cache(char *start, size_t length)
{
char *end = start + length;
do
{
asm volatile("fdc 0(%0)" : : "r" (start));
asm volatile("fic 0(%%sr0,%0)" : : "r" (start));
start += 16;
} while (start < end);
asm volatile("fdc 0(%0)" : : "r" (end));
asm ("sync");
}
void memdump(void *mem, unsigned long len)
{
printf("memdump @ 0x%x : ", (unsigned int) mem);
while (len--) {
printf("0x%x ", (unsigned int) *(unsigned char *)mem);
mem++;
}
printf("\n");
}
/********************************************************
* Boot drives
********************************************************/
static struct drive_s *boot_drive; // really currently booted drive
static struct drive_s *parisc_boot_harddisc; // first hard disc
static struct drive_s *parisc_boot_cdrom; // first DVD or CD-ROM
static struct pdc_module_path mod_path_emulated_drives = {
.path = { .flags = 0x0, .bc = { 0xff, 0xff, 0xff, 0x8, 0x0, 0x0 }, .mod = 0x0 },
.layers = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } // first two layer entries get replaced
};
/********************************************************
* FIRMWARE IO Dependent Code (IODC) HANDLER
********************************************************/
typedef struct {
unsigned long hpa;
struct pdc_iodc *iodc;
struct pdc_system_map_mod_info *mod_info;
struct pdc_module_path *mod_path;
int num_addr;
int add_addr[5];
} hppa_device_t;
static hppa_device_t parisc_devices[HPPA_MAX_CPUS+16] = { PARISC_DEVICE_LIST };
#define PARISC_KEEP_LIST \
GSC_HPA,\
DINO_HPA,\
DINO_UART_HPA,\
/* DINO_SCSI_HPA, */ \
LASI_HPA, \
LASI_UART_HPA, \
LASI_LAN_HPA, \
LASI_LPT_HPA, \
CPU_HPA,\
MEMORY_HPA,\
LASI_GFX_HPA,\
LASI_PS2KBD_HPA, \
LASI_PS2MOU_HPA, \
0
static const char *hpa_name(unsigned long hpa)
{
struct pci_device *pci;
int i;
#define DO(x) if (hpa == x) return #x;
DO(GSC_HPA)
DO(DINO_HPA)
DO(DINO_UART_HPA)
DO(DINO_SCSI_HPA)
DO(CPU_HPA)
DO(MEMORY_HPA)
DO(IDE_HPA)
DO(LASI_HPA)
DO(LASI_UART_HPA)
DO(LASI_SCSI_HPA)
DO(LASI_LAN_HPA)
DO(LASI_LPT_HPA)
DO(LASI_AUDIO_HPA)
DO(LASI_PS2KBD_HPA)
DO(LASI_PS2MOU_HPA)
DO(LASI_GFX_HPA)
#undef DO
/* could be one of the SMP CPUs */
for (i = 1; i < smp_cpus; i++) {
static char CPU_TXT[] = "CPU_HPA_x";
if (hpa == CPU_HPA_IDX(i)) {
CPU_TXT[8] = '0'+i;
return CPU_TXT;
}
}
/* could be a PCI device */
foreachpci(pci) {
unsigned long mem, mmio;
mem = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0);
mem &= PCI_BASE_ADDRESS_MEM_MASK;
if (hpa == mem)
return "HPA_PCI_CARD_MEM";
mmio = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2);
mmio &= PCI_BASE_ADDRESS_MEM_MASK;
if (hpa == mem)
return "HPA_PCI_CARD_MMIO";
}
return "UNKNOWN HPA";
}
int HPA_is_serial_device(unsigned long hpa)
{
return (hpa == DINO_UART_HPA) || (hpa == LASI_UART_HPA);
}
int HPA_is_storage_device(unsigned long hpa)
{
return (hpa == DINO_SCSI_HPA) || (hpa == IDE_HPA) || (hpa == LASI_SCSI_HPA);
}
int HPA_is_keyboard_device(unsigned long hpa)
{
return (hpa == LASI_PS2KBD_HPA);
}
#define GFX_NUM_PAGES 0x2000
int HPA_is_graphics_device(unsigned long hpa)
{
return (hpa == LASI_GFX_HPA) || (hpa == 0xf4000000) ||
(hpa == 0xf8000000) || (hpa == 0xfa000000);
}
static const char *hpa_device_name(unsigned long hpa, int output)
{
return HPA_is_graphics_device(hpa) ? "GRAPHICS(1)" :
HPA_is_keyboard_device(hpa) ? "PS2" :
((hpa + 0x800) == PORT_SERIAL1) ?
"SERIAL_1.9600.8.none" : "SERIAL_2.9600.8.none";
}
static unsigned long keep_list[] = { PARISC_KEEP_LIST };
static void remove_from_keep_list(unsigned long hpa)
{
int i = 0;
while (keep_list[i] && keep_list[i] != hpa)
i++;
while (keep_list[i]) {
++i;
keep_list[i-1] = keep_list[i];
}
}
static int keep_this_hpa(unsigned long hpa)
{
int i = 0;
while (keep_list[i]) {
if (keep_list[i] == hpa)
return 1;
i++;
}
return 0;
}
/* Rebuild hardware list and drop all devices which are not listed in
* PARISC_KEEP_LIST. Generate num_cpus CPUs. */
static void remove_parisc_devices(unsigned int num_cpus)
{
static struct pdc_system_map_mod_info modinfo[HPPA_MAX_CPUS] = { {1,}, };
static struct pdc_module_path modpath[HPPA_MAX_CPUS] = { {{1,}} };
hppa_device_t *cpu_dev = NULL;
unsigned long hpa;
int i, p, t;
/* already initialized? */
static int uninitialized = 1;
if (!uninitialized)
return;
uninitialized = 0;
/* check if qemu emulates LASI chip (LASI_IAR exists) */
if (*(unsigned long *)(LASI_HPA+16) == 0) {
remove_from_keep_list(LASI_UART_HPA);
remove_from_keep_list(LASI_LAN_HPA);
remove_from_keep_list(LASI_LPT_HPA);
} else {
/* check if qemu emulates LASI i82596 LAN card */
if (*(unsigned long *)(LASI_LAN_HPA+12) != 0xBEEFBABE)
remove_from_keep_list(LASI_LAN_HPA);
}
p = t = 0;
while ((hpa = parisc_devices[p].hpa) != 0) {
if (keep_this_hpa(hpa)) {
parisc_devices[t] = parisc_devices[p];
if (hpa == CPU_HPA)
cpu_dev = &parisc_devices[t];
t++;
}
p++;
}
/* Fix monarch CPU */
BUG_ON(!cpu_dev);
cpu_dev->mod_info->mod_addr = CPU_HPA;
cpu_dev->mod_path->path.mod = (CPU_HPA - DINO_HPA) / 0x1000;
/* Generate other CPU devices */
for (i = 1; i < num_cpus; i++) {
unsigned long hpa = CPU_HPA_IDX(i);
parisc_devices[t] = *cpu_dev;
parisc_devices[t].hpa = hpa;
modinfo[i] = *cpu_dev->mod_info;
modinfo[i].mod_addr = hpa;
parisc_devices[t].mod_info = &modinfo[i];
modpath[i] = *cpu_dev->mod_path;
modpath[i].path.mod = (hpa - DINO_HPA) / 0x1000;
parisc_devices[t].mod_path = &modpath[i];
t++;
}
BUG_ON(t > ARRAY_SIZE(parisc_devices));
while (t < ARRAY_SIZE(parisc_devices)) {
memset(&parisc_devices[t], 0, sizeof(parisc_devices[0]));
t++;
}
}
static int find_hpa_index(unsigned long hpa)
{
int i;
if (!hpa)
return -1;
for (i = 0; i < (ARRAY_SIZE(parisc_devices)-1); i++) {
if (hpa == parisc_devices[i].hpa)
return i;
if (!parisc_devices[i].hpa)
return -1;
}
return -1;
}
static int compare_module_path(struct pdc_module_path *path,
struct pdc_module_path *search,
int check_layers)
{
int i;
if (path->path.mod != search->path.mod)
return -1;
for(i = 0; i < ARRAY_SIZE(path->path.bc); i++) {
if (path->path.bc[i] != search->path.bc[i])
return -1;
}
if (check_layers) {
for(i = 0; i < ARRAY_SIZE(path->layers); i++) {
if (path->layers[i] != search->layers[i])
return -1;
}
}
return 0;
}
static hppa_device_t *find_hppa_device_by_path(struct pdc_module_path *search,
unsigned long *index, int check_layers)
{
hppa_device_t *dev;
int i;
for (i = 0; i < (ARRAY_SIZE(parisc_devices)-1); i++) {
dev = parisc_devices + i;
if (!dev->hpa)
continue;
if (!compare_module_path(dev->mod_path, search, check_layers)) {
*index = i;
return dev;
}
}
return NULL;
}
#define SERIAL_TIMEOUT 20
static unsigned long parisc_serial_in(char *c, unsigned long maxchars)
{
portaddr_t addr = PAGE0->mem_kbd.hpa + 0x800; /* PARISC_SERIAL_CONSOLE */
unsigned long end = timer_calc(SERIAL_TIMEOUT);
unsigned long count = 0;
while (count < maxchars) {
u8 lsr = inb(addr+SEROFF_LSR);
if (lsr & 0x01) {
// Success - can read data
*c++ = inb(addr+SEROFF_DATA);
count++;
}
if (timer_check(end))
break;
}
return count;
}
static void parisc_serial_out(char c)
{
portaddr_t addr = PAGE0->mem_cons.hpa + 0x800; /* PARISC_SERIAL_CONSOLE */
for (;;) {
if (c == '\n')
parisc_serial_out('\r');
u8 lsr = inb(addr+SEROFF_LSR);
if ((lsr & 0x60) == 0x60) {
// Success - can write data
outb(c, addr+SEROFF_DATA);
break;
}
}
}
static void parisc_putchar_internal(char c)
{
if (HPA_is_graphics_device(PAGE0->mem_cons.hpa))
sti_putc(c);
else
parisc_serial_out(c);
}
/* print char to default PDC output device */
void parisc_putchar(char c)
{
if (c == '\n')
parisc_putchar_internal('\r');
parisc_putchar_internal(c);
}
/* read char from default PDC input device (serial port / ps2-kbd) */
static char parisc_getchar(void)
{
int count;
char c;
if (HPA_is_serial_device(PAGE0->mem_kbd.hpa))
count = parisc_serial_in(&c, sizeof(c));
else
count = lasips2_kbd_in(&c, sizeof(c));
if (count == 0)
c = 0;
return c;
}
void iodc_log_call(unsigned int *arg, const char *func)
{
if (pdc_debug & DEBUG_IODC) {
printf("\nIODC %s called: hpa=0x%x (%s) option=0x%x arg2=0x%x arg3=0x%x ", func, ARG0, hpa_name(ARG0), ARG1, ARG2, ARG3);
printf("result=0x%x arg5=0x%x arg6=0x%x arg7=0x%x\n", ARG4, ARG5, ARG6, ARG7);
}
}
#define FUNC_MANY_ARGS , \
int a0, int a1, int a2, int a3, int a4, int a5, int a6, \
int a7, int a8, int a9, int a10, int a11, int a12
int __VISIBLE parisc_iodc_ENTRY_IO(unsigned int *arg FUNC_MANY_ARGS)
{
unsigned long hpa = ARG0;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG4;
int ret, len;
char *c;
struct disk_op_s disk_op;
if (1 &&
(((HPA_is_serial_device(hpa) || HPA_is_graphics_device(hpa)) && option == ENTRY_IO_COUT) ||
((HPA_is_serial_device(hpa) || HPA_is_graphics_device(hpa)) && option == ENTRY_IO_CIN) ||
((HPA_is_storage_device(hpa) && option == ENTRY_IO_BOOTIN && !(pdc_debug & DEBUG_BOOT_IO)))) ) {
/* avoid debug messages */
} else {
iodc_log_call(arg, __FUNCTION__);
}
/* console I/O */
switch (option) {
case ENTRY_IO_COUT: /* console output */
c = (char*)ARG6;
result[0] = len = ARG7;
if (HPA_is_serial_device(hpa) || HPA_is_graphics_device(hpa)) {
while (len--)
printf("%c", *c++);
}
return PDC_OK;
case ENTRY_IO_CIN: /* console input, with 5 seconds timeout */
c = (char*)ARG6;
/* FIXME: Add loop to wait for up to 5 seconds for input */
if (HPA_is_serial_device(hpa))
result[0] = parisc_serial_in(c, ARG7);
else if (HPA_is_keyboard_device(hpa))
result[0] = lasips2_kbd_in(c, ARG7);
return PDC_OK;
}
/* boot medium I/O */
if (HPA_is_storage_device(hpa))
switch (option) {
case ENTRY_IO_BOOTIN: /* boot medium IN */
case ENTRY_IO_BBLOCK_IN: /* boot block medium IN */
disk_op.drive_fl = boot_drive;
disk_op.buf_fl = (void*)ARG6;
disk_op.command = CMD_READ;
if (option == ENTRY_IO_BBLOCK_IN) { /* in 2k blocks */
/* reqsize must not be bigger than maxsize */
// if (ARG7 > ARG8) return PDC_INVALID_ARG;
disk_op.count = (ARG7 * ((u64)FW_BLOCKSIZE / disk_op.drive_fl->blksize));
disk_op.lba = (ARG5 * ((u64)FW_BLOCKSIZE / disk_op.drive_fl->blksize));
} else {
// read one block at least.
if (ARG7 && (ARG7 < disk_op.drive_fl->blksize))
ARG7 = disk_op.drive_fl->blksize;
/* reqsize must be multiple of 2K */
if (ARG7 & (FW_BLOCKSIZE-1))
return PDC_INVALID_ARG;
/* reqsize must not be bigger than maxsize */
// if (ARG7 > ARG8) return PDC_INVALID_ARG;
/* medium start must be 2K aligned */
if (ARG5 & (FW_BLOCKSIZE-1))
return PDC_INVALID_ARG;
disk_op.count = (ARG7 / disk_op.drive_fl->blksize);
disk_op.lba = (ARG5 / disk_op.drive_fl->blksize);
}
// NOTE: LSI SCSI can not read more than 8191 blocks, so limit blocks to read
if (disk_op.count >= 8192)
disk_op.count = 8192-16;
// dprintf(0, "LBA %d COUNT %d\n", (u32) disk_op.lba, (u32)disk_op.count);
ret = process_op(&disk_op);
if (option == ENTRY_IO_BOOTIN)
result[0] = disk_op.count * disk_op.drive_fl->blksize; /* return bytes */
else
result[0] = (disk_op.count * (u64)disk_op.drive_fl->blksize) / FW_BLOCKSIZE; /* return blocks */
// printf("\nBOOT IO result %d, requested %d, read %ld\n", ret, ARG7, result[0]);
if (ret)
return PDC_ERROR;
return PDC_OK;
}
if (option == ENTRY_IO_CLOSE)
return PDC_OK;
// BUG_ON(1);
iodc_log_call(arg, __FUNCTION__);
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_INIT(unsigned int *arg FUNC_MANY_ARGS)
{
unsigned long hpa = ARG0;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG4;
int hpa_index;
iodc_log_call(arg, __FUNCTION__);
hpa_index = find_hpa_index(hpa);
if (hpa_index < 0 && hpa != IDE_HPA)
return PDC_INVALID_ARG;
switch (option) {
case ENTRY_INIT_SRCH_FRST: /* 2: Search first */
memcpy((void *)ARG3, &mod_path_emulated_drives.layers,
sizeof(mod_path_emulated_drives.layers)); /* fill ID_addr */
result[0] = 0;
result[1] = HPA_is_serial_device(hpa) ? CL_DUPLEX:
HPA_is_storage_device(hpa) ? CL_RANDOM : 0;
result[2] = result[3] = 0; /* No network card, so no MAC. */
return PDC_OK;
case ENTRY_INIT_SRCH_NEXT: /* 3: Search next */
return PDC_NE_BOOTDEV; /* No further boot devices */
case ENTRY_INIT_MOD_DEV: /* 4: Init & test mod & dev */
case ENTRY_INIT_DEV: /* 5: Init & test dev */
result[0] = 0; /* module IO_STATUS */
result[1] = HPA_is_serial_device(hpa) ? CL_DUPLEX:
HPA_is_storage_device(hpa) ? CL_RANDOM : 0;
result[2] = result[3] = 0; /* TODO?: MAC of network card. */
return PDC_OK;
case ENTRY_INIT_MOD: /* 6: INIT */
result[0] = 0; /* module IO_STATUS */
return PDC_OK;
}
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_SPA(unsigned int *arg FUNC_MANY_ARGS)
{
iodc_log_call(arg, __FUNCTION__);
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_CONFIG(unsigned int *arg FUNC_MANY_ARGS)
{
iodc_log_call(arg, __FUNCTION__);
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_TEST(unsigned int *arg FUNC_MANY_ARGS)
{
unsigned long hpa = ARG0;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG4;
int hpa_index;
iodc_log_call(arg, __FUNCTION__);
hpa_index = find_hpa_index(hpa);
if (hpa_index < 0 && hpa != IDE_HPA)
return PDC_INVALID_ARG;
/* The options ARG1=0 and ARG1=1 are required. Others are optional. */
if (option == 0) { // Return info
unsigned long *list_addr = (unsigned long *)ARG5;
list_addr[0] = 0; // no test lists available.
result[0] = 0; // data buffer size, no bytes required.
result[1] = 0; // message buffer size, no bytes required.
return PDC_OK;
}
if (option == 1) { // Execute step
result[0] = 0; // fixed address of remote por
return PDC_OK;
}
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_TLB(unsigned int *arg FUNC_MANY_ARGS)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG4;
iodc_log_call(arg, __FUNCTION__);
if (option == 0) {
result[0] = 0; /* no TLB */
result[1] = 0;
return PDC_OK;
}
return PDC_BAD_OPTION;
}
/********************************************************
* FIRMWARE PDC HANDLER
********************************************************/
#define STABLE_STORAGE_SIZE 512
static unsigned char stable_storage[STABLE_STORAGE_SIZE];
#define NVOLATILE_STORAGE_SIZE 512
static unsigned char nvolatile_storage[NVOLATILE_STORAGE_SIZE];
static void init_stable_storage(void)
{
/* see ENGINEERING NOTE on page 4-92 in PDC2.0 doc */
memset(&stable_storage, 0, STABLE_STORAGE_SIZE);
// no intial paths
stable_storage[0x07] = 0xff;
stable_storage[0x67] = 0xff;
stable_storage[0x87] = 0xff;
stable_storage[0xa7] = 0xff;
// 0x0e/0x0f => fastsize = all, needed for HPUX
stable_storage[0x5f] = 0x0f;
}
static unsigned long lasi_rtc_read(void)
{
return *(u32 *)LASI_RTC_HPA;
}
static void lasi_rtc_write(u32 val)
{
*(u32 *)LASI_RTC_HPA = val;
}
/* values in PDC_CHASSIS */
const char * const systat[] = {
"Off", "Fault", "Test", "Initialize",
"Shutdown", "Warning", "Run", "All On"
};
static const char *pdc_name(unsigned long num)
{
#define DO(x) if (num == x) return #x;
DO(PDC_POW_FAIL)
DO(PDC_CHASSIS)
DO(PDC_PIM)
DO(PDC_MODEL)
DO(PDC_CACHE)
DO(PDC_HPA)
DO(PDC_COPROC)
DO(PDC_IODC)
DO(PDC_TOD)
DO(PDC_STABLE)
DO(PDC_NVOLATILE)
DO(PDC_ADD_VALID)
DO(PDC_INSTR)
DO(PDC_PROC)
DO(PDC_BLOCK_TLB)
DO(PDC_TLB)
DO(PDC_MEM)
DO(PDC_PSW)
DO(PDC_SYSTEM_MAP)
DO(PDC_SOFT_POWER)
DO(PDC_CRASH_PREP)
DO(PDC_MEM_MAP)
DO(PDC_EEPROM)
DO(PDC_NVM)
DO(PDC_SEED_ERROR)
DO(PDC_IO)
DO(PDC_BROADCAST_RESET)
DO(PDC_LAN_STATION_ID)
DO(PDC_CHECK_RANGES)
DO(PDC_NV_SECTIONS)
DO(PDC_PERFORMANCE)
DO(PDC_SYSTEM_INFO)
DO(PDC_RDR)
DO(PDC_INTRIGUE)
DO(PDC_STI)
DO(PDC_PCI_INDEX)
DO(PDC_RELOCATE)
DO(PDC_INITIATOR)
DO(PDC_LINK)
#undef DO
return "UNKNOWN!";
}
static int pdc_chassis(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
short *display_model = (short *)ARG3;
switch (option) {
case PDC_CHASSIS_DISP:
ARG3 = ARG2;
result = (unsigned long *)&ARG4; // do not write to ARG2, use &ARG4 instead
// fall through
case PDC_CHASSIS_DISPWARN:
ARG4 = (ARG3 >> 17) & 7;
chassis_code = ARG3 & 0xffff;
if (pdc_debug & DEBUG_CHASSIS)
printf("\nPDC_CHASSIS: %s (%d), %sCHASSIS 0x%0x\n",
systat[ARG4], ARG4, (ARG3>>16)&1 ? "blank display, ":"", chassis_code);
// fall through
case PDC_CHASSIS_WARN:
// return warnings regarding fans, batteries and temperature: None!
result[0] = 0;
return PDC_OK;
case PDC_RETURN_CHASSIS_INFO: /* return chassis LED/LCD info */
// XXX: Later we could emulate an LCD display here.
result[0] = result[1] = 4; // actcnt & maxcnt
memset((char *)ARG3, 0, ARG4);
display_model[0] = 1; // 1=DISPLAY_MODEL_NONE
display_model[1] = 0; // 0=LCD WIDTH is 0
return PDC_OK;
}
return PDC_BAD_PROC;
}
static int pdc_pim(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
unsigned long hpa;
int i;
unsigned int count, default_size;
if (is_64bit())
default_size = sizeof(struct pdc_toc_pim_20);
else
default_size = sizeof(struct pdc_toc_pim_11);
switch (option) {
case PDC_PIM_HPMC:
break;
case PDC_PIM_RETURN_SIZE:
*result = default_size;
// B160 returns only "2". Why?
return PDC_OK;
case PDC_PIM_LPMC:
case PDC_PIM_SOFT_BOOT:
break;
case PDC_PIM_TOC:
hpa = mfctl(CPU_HPA_CR_REG); /* get CPU HPA from cr7 */
i = index_of_CPU_HPA(hpa);
if (i < 0 || i >= HPPA_MAX_CPUS) {
*result = PDC_INVALID_ARG;
return PDC_OK;
}
if (( is_64bit() && pim_toc_data[i].pim20.cpu_state.val == 0) ||
(!is_64bit() && pim_toc_data[i].pim11.cpu_state.val == 0)) {
/* PIM contents invalid */
*result = PDC_NE_MOD;
return PDC_OK;
}
count = (default_size < ARG4) ? default_size : ARG4;
memcpy((void *)ARG3, &pim_toc_data[i], count);
*result = count;
/* clear PIM contents */
if (is_64bit())
pim_toc_data[i].pim20.cpu_state.val = 0;
else
pim_toc_data[i].pim11.cpu_state.val = 0;
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static struct pdc_model model = { PARISC_PDC_MODEL };
static int pdc_model(unsigned int *arg)
{
static const char model_str[] = PARISC_MODEL;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_MODEL_INFO:
memcpy(result, &model, sizeof(model));
return PDC_OK;
case PDC_MODEL_VERSIONS:
switch (ARG3) {
case 0: /* return CPU0 version */
result[0] = 35; // TODO! ???
return PDC_OK;
case 1: /* return PDC version */
result[0] = PARISC_PDC_VERSION;
return PDC_OK;
}
return -4; // invalid c_index
case PDC_MODEL_SYSMODEL:
result[0] = sizeof(model_str) - 1;
strtcpy((char *)ARG4, model_str, sizeof(model_str));
return PDC_OK;
case PDC_MODEL_ENSPEC:
case PDC_MODEL_DISPEC:
if (ARG3 != model.pot_key)
return -20;
return PDC_OK;
case PDC_MODEL_CPU_ID:
result[0] = PARISC_PDC_CPUID;
return PDC_OK;
case PDC_MODEL_CAPABILITIES:
result[0] = PARISC_PDC_CAPABILITIES;
result[0] |= PDC_MODEL_OS32; /* we do support 32-bit */
result[0] &= ~PDC_MODEL_OS64; /* but not 64-bit (yet) */
return PDC_OK;
case PDC_MODEL_GET_INSTALL_KERNEL:
// No need to provide a special install kernel during installation of HP-UX
return PDC_BAD_OPTION;
}
dprintf(0, "\n\nSeaBIOS: Unimplemented PDC_MODEL function %d %x %x %x %x\n", ARG1, ARG2, ARG3, ARG4, ARG5);
return PDC_BAD_OPTION;
}
static int pdc_cache(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
static unsigned long cache_info[] = { PARISC_PDC_CACHE_INFO };
static struct pdc_cache_info *machine_cache_info
= (struct pdc_cache_info *) &cache_info;
switch (option) {
case PDC_CACHE_INFO:
BUG_ON(sizeof(cache_info) != sizeof(*machine_cache_info));
machine_cache_info->it_size = tlb_entries;
machine_cache_info->dt_size = tlb_entries;
machine_cache_info->it_loop = 1;
machine_cache_info->dt_loop = 1;
#if 0
dprintf(0, "\n\nCACHE IC: %ld %ld %ld DC: %ld %ld %ld\n",
machine_cache_info->ic_count, machine_cache_info->ic_loop, machine_cache_info->ic_stride,
machine_cache_info->dc_count, machine_cache_info->dc_loop, machine_cache_info->dc_stride);
#endif
#if 1
/* Increase cc_block from 1 to 11. This increases icache_stride
* and dcache_stride to 32768 bytes. Revisit for HP-UX. */
machine_cache_info->dc_conf.cc_block = 11;
machine_cache_info->ic_conf.cc_block = 11;
machine_cache_info->ic_size = 0; /* no instruction cache */
machine_cache_info->ic_count = 0;
machine_cache_info->ic_loop = 0;
machine_cache_info->dc_size = 0; /* no data cache */
machine_cache_info->dc_count = 0;
machine_cache_info->dc_loop = 0;
#endif
memcpy(result, cache_info, sizeof(cache_info));
return PDC_OK;
}
dprintf(0, "\n\nSeaBIOS: Unimplemented PDC_CACHE function %d %x %x %x %x\n", ARG1, ARG2, ARG3, ARG4, ARG5);
return PDC_BAD_OPTION;
}
static int pdc_hpa(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
unsigned long hpa;
int i;
switch (option) {
case PDC_HPA_PROCESSOR:
hpa = mfctl(CPU_HPA_CR_REG); /* get CPU HPA from cr7 */
i = index_of_CPU_HPA(hpa);
BUG_ON(i < 0); /* ARGH, someone modified cr7! */
result[0] = hpa; /* CPU_HPA */
result[1] = i; /* for SMP: 0,1,2,3,4...(num of this cpu) */
return PDC_OK;
case PDC_HPA_MODULES:
return PDC_BAD_OPTION; // all modules on same board as the processor.
}
return PDC_BAD_OPTION;
}
static int pdc_coproc(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
unsigned long mask;
switch (option) {
case PDC_COPROC_CFG:
memset(result, 0, 32 * sizeof(unsigned long));
/* Set one bit per cpu in ccr_functional and ccr_present.
Ignore that specification only mentions 8 bits for cr10
and set all FPUs functional */
mask = -1UL;
mtctl(mask, 10); /* initialize cr10 */
result[0] = mask;
result[1] = mask;
result[17] = 1; // Revision
result[18] = 19; // Model
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_iodc(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
unsigned long hpa;
struct pdc_iodc *iodc_p;
int hpa_index;
unsigned char *c;
// dprintf(0, "\n\nSeaBIOS: Info PDC_IODC function %ld ARG3=%x ARG4=%x ARG5=%x ARG6=%x\n", option, ARG3, ARG4, ARG5, ARG6);
switch (option) {
case PDC_IODC_READ:
hpa = ARG3;
if (hpa == IDE_HPA) { // do NOT check for DINO_SCSI_HPA, breaks Linux which scans IO areas for unlisted io modules
iodc_p = &iodc_data_hpa_fff8c000; // workaround for PCI ATA
} else {
hpa_index = find_hpa_index(hpa);
if (hpa_index < 0)
return -4; // not found
iodc_p = parisc_devices[hpa_index].iodc;
}
if (ARG4 == PDC_IODC_INDEX_DATA) {
// if (hpa == MEMORY_HPA)
// ARG6 = 2; // Memory modules return 2 bytes of IODC memory (result2 ret[0] = 0x6701f41 HI !!)
memcpy((void*) ARG5, iodc_p, ARG6);
c = (unsigned char *) ARG5;
// printf("SeaBIOS: PDC_IODC get: hpa = 0x%lx, HV: 0x%x 0x%x IODC_SPA=0x%x type 0x%x, \n", hpa, c[0], c[1], c[2], c[3]);
// c[0] = iodc_p->hversion_model; // FIXME. BROKEN HERE !!!
// c[1] = iodc_p->hversion_rev || (iodc_p->hversion << 4);
*result = ARG6;
return PDC_OK;
}
// ARG4 is IODC function to copy.
if (ARG4 < PDC_IODC_RI_INIT || ARG4 > PDC_IODC_RI_TLB)
return PDC_IODC_INVALID_INDEX;
*result = 512; /* max size of function iodc_entry */
if (ARG6 < *result)
return PDC_IODC_COUNT;
memcpy((void*) ARG5, &iodc_entry, *result);
c = (unsigned char *) &iodc_entry_table;
/* calculate offset into jump table. */
c += (ARG4 - PDC_IODC_RI_INIT) * 2 * sizeof(unsigned int);
memcpy((void*) ARG5, c, 2 * sizeof(unsigned int));
// dprintf(0, "\n\nSeaBIOS: Info PDC_IODC function OK\n");
flush_data_cache((char*)ARG5, *result);
return PDC_OK;
break;
case PDC_IODC_NINIT: /* non-destructive init */
case PDC_IODC_DINIT: /* destructive init */
break;
case PDC_IODC_MEMERR:
result[0] = 0; /* IO_STATUS */
result[1] = 0;
result[2] = 0;
result[3] = 0;
return PDC_OK;
}
dprintf(0, "\n\nSeaBIOS: Unimplemented PDC_IODC function %ld ARG3=%x ARG4=%x ARG5=%x ARG6=%x\n", option, ARG3, ARG4, ARG5, ARG6);
return PDC_BAD_OPTION;
}
static int pdc_tod(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_TOD_READ:
result[0] = lasi_rtc_read();
result[1] = result[2] = result[3] = 0;
return PDC_OK;
case PDC_TOD_WRITE:
lasi_rtc_write(ARG2);
return PDC_OK;
case 2: /* PDC_TOD_CALIBRATE_TIMERS */
/* double-precision floating-point with frequency of Interval Timer in megahertz: */
*(double*)&result[0] = (double)CPU_CLOCK_MHZ;
/* unsigned 64-bit integers representing clock accuracy in parts per billion: */
result[2] = 1000000000; /* TOD_acc */
result[3] = 0x5a6c; /* CR_acc (interval timer) */
return PDC_OK;
}
dprintf(0, "\n\nSeaBIOS: Unimplemented PDC_TOD function %ld ARG2=%x ARG3=%x ARG4=%x\n", option, ARG2, ARG3, ARG4);
return PDC_BAD_OPTION;
}
static int pdc_stable(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
// dprintf(0, "\n\nSeaBIOS: PDC_STABLE function %ld ARG2=%x ARG3=%x ARG4=%x\n", option, ARG2, ARG3, ARG4);
switch (option) {
case PDC_STABLE_READ:
if ((ARG2 + ARG4) > STABLE_STORAGE_SIZE)
return PDC_INVALID_ARG;
memcpy((unsigned char *) ARG3, &stable_storage[ARG2], ARG4);
return PDC_OK;
case PDC_STABLE_WRITE:
if ((ARG2 + ARG4) > STABLE_STORAGE_SIZE)
return PDC_INVALID_ARG;
memcpy(&stable_storage[ARG2], (unsigned char *) ARG3, ARG4);
return PDC_OK;
case PDC_STABLE_RETURN_SIZE:
result[0] = STABLE_STORAGE_SIZE;
return PDC_OK;
case PDC_STABLE_VERIFY_CONTENTS:
return PDC_OK;
case PDC_STABLE_INITIALIZE:
init_stable_storage();
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_nvolatile(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_NVOLATILE_READ:
if ((ARG2 + ARG4) > NVOLATILE_STORAGE_SIZE)
return PDC_INVALID_ARG;
memcpy((unsigned char *) ARG3, &nvolatile_storage[ARG2], ARG4);
return PDC_OK;
case PDC_NVOLATILE_WRITE:
if ((ARG2 + ARG4) > NVOLATILE_STORAGE_SIZE)
return PDC_INVALID_ARG;
memcpy(&nvolatile_storage[ARG2], (unsigned char *) ARG3, ARG4);
return PDC_OK;
case PDC_NVOLATILE_RETURN_SIZE:
result[0] = NVOLATILE_STORAGE_SIZE;
return PDC_OK;
case PDC_NVOLATILE_VERIFY_CONTENTS:
return PDC_OK;
case PDC_NVOLATILE_INITIALIZE:
memset(nvolatile_storage, 0, sizeof(nvolatile_storage));
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_add_valid(unsigned int *arg)
{
unsigned long option = ARG1;
// dprintf(0, "\n\nSeaBIOS: PDC_ADD_VALID function %ld ARG2=%x called.\n", option, ARG2);
if (option != 0)
return PDC_BAD_OPTION;
if (0 && ARG2 == 0) // should PAGE0 be valid? HP-UX asks for it, but maybe due a bug in our code...
return 1;
// if (ARG2 < PAGE_SIZE) return PDC_ERROR;
if (ARG2 < ram_size)
return PDC_OK;
if (ARG2 >= (unsigned long)_sti_rom_start &&
ARG2 <= (unsigned long)_sti_rom_end)
return PDC_OK;
if (ARG2 < FIRMWARE_END)
return 1;
if (ARG2 <= 0xffffffff)
return PDC_OK;
dprintf(0, "\n\nSeaBIOS: FAILED!!!! PDC_ADD_VALID function %ld ARG2=%x called.\n", option, ARG2);
return PDC_REQ_ERR_0; /* Operation completed with a requestor bus error. */
}
static int pdc_proc(unsigned int *arg)
{
extern void enter_smp_idle_loop(void);
unsigned long option = ARG1;
switch (option) {
case 1:
if (ARG2 != 0)
return PDC_BAD_PROC;
if (pdc_debug & DEBUG_PDC)
printf("\nSeaBIOS: CPU%d enters rendenzvous loop.\n",
index_of_CPU_HPA(mfctl(CPU_HPA_CR_REG)));
/* wait until all outstanding timer irqs arrived. */
msleep(500);
/* let the current CPU sleep until rendenzvous. */
enter_smp_idle_loop();
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_block_tlb(unsigned int *arg)
{
unsigned long option = ARG1;
struct pdc_btlb_info *info = (struct pdc_btlb_info *) ARG2;
switch (option) {
case PDC_BTLB_INFO:
memset(info, 0, sizeof(*info));
if (btlb_entries) {
/* TODO: fill in BTLB info */
}
return PDC_OK;
case PDC_BTLB_INSERT:
case PDC_BTLB_PURGE:
case PDC_BTLB_PURGE_ALL:
/* TODO: implement above functions */
return PDC_BAD_OPTION;
}
return PDC_BAD_OPTION;
}
static int pdc_tlb(unsigned int *arg)
{
#if 0
/* still buggy, let's avoid it to keep things simple. */
switch (option) {
case PDC_TLB_INFO:
result[0] = PAGE_SIZE;
result[0] = PAGE_SIZE << 2;
return PDC_OK;
case PDC_TLB_SETUP:
result[0] = ARG5 & 1;
result[1] = 0;
return PDC_OK;
}
#endif
return PDC_BAD_PROC;
}
static int pdc_mem(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
// only implemented on 64bit PDC!
if (sizeof(unsigned long) == sizeof(unsigned int))
return PDC_BAD_PROC;
switch (option) {
case PDC_MEM_MEMINFO:
result[0] = 0; // no PDT entries
result[1] = 0; // page entries
result[2] = 0; // PDT status
result[3] = (unsigned long)-1ULL; // dbe_loc
result[4] = GoldenMemory; // good_mem
return PDC_OK;
case PDC_MEM_READ_PDT:
result[0] = 0; // no PDT entries
return PDC_OK;
case PDC_MEM_GOODMEM:
GoldenMemory = ARG3;
return PDC_OK;
}
dprintf(0, "\n\nSeaBIOS: Check PDC_MEM option %ld ARG3=%x ARG4=%x ARG5=%x\n", option, ARG3, ARG4, ARG5);
return PDC_BAD_PROC;
}
static int pdc_psw(unsigned int *arg)
{
static unsigned long psw_defaults = PDC_PSW_ENDIAN_BIT;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
if (option > PDC_PSW_SET_DEFAULTS)
return PDC_BAD_OPTION;
/* FIXME: For 64bit support enable PDC_PSW_WIDE_BIT too! */
if (option == PDC_PSW_MASK)
*result = PDC_PSW_ENDIAN_BIT;
if (option == PDC_PSW_GET_DEFAULTS)
*result = psw_defaults;
if (option == PDC_PSW_SET_DEFAULTS) {
psw_defaults = ARG2;
}
return PDC_OK;
}
static int pdc_system_map(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
struct pdc_module_path *mod_path;
unsigned long hpa;
int hpa_index;
// dprintf(0, "\n\nSeaBIOS: Info: PDC_SYSTEM_MAP function %ld ARG3=%x ARG4=%x ARG5=%x\n", option, ARG3, ARG4, ARG5);
switch (option) {
case PDC_FIND_MODULE:
hpa_index = ARG4;
if (hpa_index >= ARRAY_SIZE(parisc_devices))
return PDC_NE_MOD; // Module not found
hpa = parisc_devices[hpa_index].hpa;
if (!hpa)
return PDC_NE_MOD; // Module not found
mod_path = (struct pdc_module_path *)ARG3;
if (mod_path)
*mod_path = *parisc_devices[hpa_index].mod_path;
// *pdc_mod_info = *parisc_devices[hpa_index].mod_info; -> can be dropped.
memset(result, 0, 32*sizeof(long));
result[0] = hpa; // .mod_addr for PDC_IODC
result[1] = HPA_is_graphics_device(hpa) ? GFX_NUM_PAGES : 1;
result[2] = parisc_devices[hpa_index].num_addr; // additional addresses
return PDC_OK;
case PDC_FIND_ADDRESS:
hpa_index = ARG3;
if (hpa_index >= ARRAY_SIZE(parisc_devices))
return PDC_NE_MOD; // Module not found
hpa = parisc_devices[hpa_index].hpa;
if (!hpa)
return PDC_NE_MOD; // Module not found
memset(result, 0, 32*sizeof(long));
ARG4 -= 1;
if (ARG4 >= parisc_devices[hpa_index].num_addr)
return PDC_INVALID_ARG;
result[0] = parisc_devices[hpa_index].add_addr[ARG4];
result[1] = HPA_is_graphics_device(hpa) ? GFX_NUM_PAGES : 1;
return PDC_OK;
case PDC_TRANSLATE_PATH:
mod_path = (struct pdc_module_path *)ARG3;
hppa_device_t *dev = find_hppa_device_by_path(mod_path, result+3, 1);
if (!dev)
return PDC_NE_MOD;
result[0] = dev->hpa;
result[1] = 1;
result[2] = 0;
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_soft_power(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_SOFT_POWER_INFO:
result[0] = (unsigned long) powersw_ptr;
return PDC_OK;
case PDC_SOFT_POWER_ENABLE:
/* put soft power button under hardware (ARG3=0) or
* software (ARG3=1) control. */
*powersw_ptr = (ARG3 & 1) << 8 | (*powersw_ptr & 1);
check_powersw_button();
return PDC_OK;
}
// dprintf(0, "\n\nSeaBIOS: PDC_SOFT_POWER called with ARG2=%x ARG3=%x ARG4=%x\n", ARG2, ARG3, ARG4);
return PDC_BAD_OPTION;
}
static int pdc_mem_map(unsigned int *arg)
{
unsigned long option = ARG1;
struct pdc_memory_map *memmap = (struct pdc_memory_map *) ARG2;
struct device_path *dp = (struct device_path *) ARG3;
hppa_device_t *dev;
unsigned long index;
switch (option) {
case PDC_MEM_MAP_HPA:
// dprintf(0, "\nSeaBIOS: PDC_MEM_MAP_HPA bus = %d, mod = %d\n", dp->bc[4], dp->mod);
dev = find_hppa_device_by_path((struct pdc_module_path *) dp, &index, 0);
if (!dev)
return PDC_NE_MOD;
memcpy(memmap, dev->mod_info, sizeof(*memmap));
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_io(unsigned int *arg)
{
unsigned long option = ARG1;
switch (option) {
case PDC_IO_READ_AND_CLEAR_ERRORS:
dprintf(0, "\n\nSeaBIOS: PDC_IO called with ARG2=%x ARG3=%x ARG4=%x\n", ARG2, ARG3, ARG4);
// return PDC_BAD_OPTION;
case PDC_IO_RESET:
case PDC_IO_RESET_DEVICES:
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_lan_station_id(unsigned int *arg)
{
unsigned long option = ARG1;
switch (option) {
case PDC_LAN_STATION_ID_READ:
if (ARG3 != LASI_LAN_HPA)
return PDC_INVALID_ARG;
if (!keep_this_hpa(LASI_LAN_HPA))
return PDC_INVALID_ARG;
/* Let qemu store the MAC of NIC to address @ARG2 */
*(unsigned long *)(LASI_LAN_HPA+12) = ARG2;
return PDC_OK;
}
return PDC_BAD_OPTION;
}
static int pdc_pci_index(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
// dprintf(0, "\n\nSeaBIOS: PDC_PCI_INDEX(%lu) called with ARG2=%x ARG3=%x ARG4=%x\n", option, ARG2, ARG3, ARG4);
switch (option) {
case PDC_PCI_INTERFACE_INFO:
memset(result, 0, 32 * sizeof(unsigned long));
result[0] = 2; /* XXX physical hardware returns those ?!? */
result[16] = 0x60;
result[17] = 0x90;
return PDC_OK;
case PDC_PCI_GET_INT_TBL_SIZE:
case PDC_PCI_GET_INT_TBL:
memset(result, 0, 32 * sizeof(unsigned long));
result[0] = 2; /* Hardware fills in, even though we return PDC_BAD_OPTION below. */
result[16] = 0x60;
result[17] = 0x90;
return PDC_BAD_OPTION;
case PDC_PCI_PCI_PATH_TO_PCI_HPA:
result[0] = PCI_HPA;
return PDC_OK;
case PDC_PCI_PCI_HPA_TO_PCI_PATH:
BUG_ON(1);
}
return PDC_BAD_OPTION;
}
static int pdc_initiator(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_GET_INITIATOR:
// ARG3 points to the hwpath of device for which initiator is asked for.
result[0] = 7; // initiator_id/host_id: 7 to 15.
result[1] = 10; // scsi_rate: 1, 2, 5 or 10 for 5, 10, 20 or 40 MT/s
result[2] = 7; // firmware suggested value for initiator_id
result[3] = 10; // firmware suggested value for scsi_rate
result[4] = 0; // width: 0:"Narrow, 1:"Wide"
result[5] = 0; // mode: 0:SMODE_SE, 1:SMODE_HVD, 2:SMODE_LVD
return PDC_OK;
case PDC_SET_INITIATOR:
case PDC_DELETE_INITIATOR:
case PDC_RETURN_TABLE_SIZE:
case PDC_RETURN_TABLE:
break;
}
dprintf(0, "\n\nSeaBIOS: Unimplemented PDC_INITIATOR function %ld ARG3=%x ARG4=%x ARG5=%x\n", option, ARG3, ARG4, ARG5);
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_pdc_entry(unsigned int *arg FUNC_MANY_ARGS)
{
unsigned long proc = ARG0;
unsigned long option = ARG1;
if (pdc_debug & DEBUG_PDC) {
printf("\nSeaBIOS: Start PDC proc %s(%d) option %d result=0x%x ARG3=0x%x %s ",
pdc_name(ARG0), ARG0, ARG1, ARG2, ARG3, (proc == PDC_IODC)?hpa_name(ARG3):"");
printf("ARG4=0x%x ARG5=0x%x ARG6=0x%x ARG7=0x%x\n", ARG4, ARG5, ARG6, ARG7);
}
switch (proc) {
case PDC_POW_FAIL:
break;
case PDC_CHASSIS: /* chassis functions */
return pdc_chassis(arg);
case PDC_PIM:
return pdc_pim(arg);
case PDC_MODEL: /* model information */
return pdc_model(arg);
case PDC_CACHE:
return pdc_cache(arg);
case PDC_HPA:
return pdc_hpa(arg);
case PDC_COPROC:
return pdc_coproc(arg);
case PDC_IODC: /* Call IODC functions */
return pdc_iodc(arg);
case PDC_TOD: /* Time of day */
return pdc_tod(arg);
case PDC_STABLE:
return pdc_stable(arg);
case PDC_NVOLATILE:
return pdc_nvolatile(arg);
case PDC_ADD_VALID:
return pdc_add_valid(arg);
case PDC_INSTR:
return PDC_BAD_PROC;
case PDC_PROC:
return pdc_proc(arg);
case PDC_CONFIG: /* Obsolete */
return PDC_BAD_PROC;
case PDC_BLOCK_TLB:
return pdc_block_tlb(arg);
case PDC_TLB: /* hardware TLB not used on Linux, but on HP-UX (if available) */
return pdc_tlb(arg);
case PDC_MEM:
return pdc_mem(arg);
case PDC_PSW: /* Get/Set default System Mask */
return pdc_psw(arg);
case PDC_SYSTEM_MAP:
return pdc_system_map(arg);
case PDC_SOFT_POWER: // don't have a soft-power switch
return pdc_soft_power(arg);
case PDC_CRASH_PREP:
/* This should actually quiesce all I/O and prepare the System for crash dumping.
Ignoring it for now, otherwise the BUG_ON below would quit qemu before we have
a chance to see the kernel panic */
return PDC_OK;
case 26: // PDC_SCSI_PARMS is the architected firmware interface to replace the Hversion PDC_INITIATOR procedure.
return PDC_BAD_PROC;
case 64: // Called by HP-UX 11 bootcd during boot. Probably checks PDC_PAT_CELL (even if we are not PAT firmware)
case 65: // Called by HP-UX 11 bootcd during boot. Probably checks PDC_PAT_CHASSIS_LOG (even if we are not PAT firmware)
dprintf(0, "\n\nSeaBIOS: UNKNOWN PDC proc %lu OPTION %lu called with ARG2=%x ARG3=%x ARG4=%x\n", proc, option, ARG2, ARG3, ARG4);
return PDC_BAD_PROC;
case PDC_MEM_MAP:
return pdc_mem_map(arg);
case 134:
if (ARG1 == 1 || ARG1 == 513) /* HP-UX 11.11 ask for it. */
return PDC_BAD_PROC;
break;
case PDC_IO:
return pdc_io(arg);
case PDC_BROADCAST_RESET:
dprintf(0, "\n\nSeaBIOS: PDC_BROADCAST_RESET (reset system) called with ARG3=%x ARG4=%x\n", ARG3, ARG4);
reset();
return PDC_OK;
case PDC_LAN_STATION_ID:
return pdc_lan_station_id(arg);
case PDC_SYSTEM_INFO:
if (ARG1 == PDC_SYSINFO_RETURN_INFO_SIZE)
return PDC_BAD_PROC;
break;
case PDC_PCI_INDEX:
return pdc_pci_index(arg);
case PDC_RELOCATE:
/* We don't want to relocate any firmware. */
return PDC_BAD_PROC;
case PDC_INITIATOR:
return pdc_initiator(arg);
}
printf("\n** WARNING **: SeaBIOS: Unimplemented PDC proc %s(%d) option %d result=%x ARG3=%x ",
pdc_name(ARG0), ARG0, ARG1, ARG2, ARG3);
printf("ARG4=%x ARG5=%x ARG6=%x ARG7=%x\n", ARG4, ARG5, ARG6, ARG7);
BUG_ON(pdc_debug);
return PDC_BAD_PROC;
}
/********************************************************
* TOC HANDLER
********************************************************/
unsigned long __VISIBLE toc_handler(struct pdc_toc_pim_11 *pim)
{
unsigned long hpa, os_toc_handler;
int cpu, y;
unsigned long *p;
struct pdc_toc_pim_11 *pim11;
struct pdc_toc_pim_20 *pim20;
struct pim_cpu_state_cf state = { .iqv=1, .iqf=1, .ipv=1, .grv=1, .crv=1, .srv=1, .trv=1, .td=1 };
hpa = mfctl(CPU_HPA_CR_REG); /* get CPU HPA from cr7 */
cpu = index_of_CPU_HPA(hpa);
pim11 = &pim_toc_data[cpu].pim11;
pim20 = &pim_toc_data[cpu].pim20;
if (is_64bit())
pim20->cpu_state = state;
else
pim11->cpu_state = state;
/* check that we use the same PIM entries as assembly code */
BUG_ON(pim11 != pim);
printf("\n");
printf("##### CPU %d HPA %lx: SeaBIOS TOC register dump #####\n", cpu, hpa);
for (y = 0; y < 32; y += 8) {
if (is_64bit())
p = (unsigned long *)&pim20->gr[y];
else
p = (unsigned long *)&pim11->gr[y];
printf("GR%02d: %lx %lx %lx %lx", y, p[0], p[1], p[2], p[3]);
printf( " %lx %lx %lx %lx\n", p[4], p[5], p[6], p[7]);
}
printf("\n");
for (y = 0; y < 32; y += 8) {
if (is_64bit())
p = (unsigned long *)&pim20->cr[y];
else
p = (unsigned long *)&pim11->cr[y];
printf("CR%02d: %lx %lx %lx %lx", y, p[0], p[1], p[2], p[3]);
printf( " %lx %lx %lx %lx\n", p[4], p[5], p[6], p[7]);
}
printf("\n");
if (is_64bit())
p = (unsigned long *)&pim20->sr[0];
else
p = (unsigned long *)&pim11->sr[0];
printf("SR0: %lx %lx %lx %lx %lx %lx %lx %lx\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
if (is_64bit()) {
printf("IAQ: %lx.%lx %lx.%lx PSW: %lx\n",
(unsigned long)pim20->cr[17], (unsigned long)pim20->cr[18],
(unsigned long)pim20->iasq_back, (unsigned long)pim20->iaoq_back,
(unsigned long)pim20->cr[22]);
printf("RP(r2): %lx\n", (unsigned long)pim20->gr[2]);
} else {
printf("IAQ: %x.%x %x.%x PSW: %x\n", pim11->cr[17], pim11->cr[18],
pim11->iasq_back, pim11->iaoq_back, pim11->cr[22]);
printf("RP(r2): %x\n", pim11->gr[2]);
}
os_toc_handler = PAGE0->vec_toc;
if (is_64bit())
os_toc_handler |= ((unsigned long long) PAGE0->vec_toc_hi << 32);
/* release lock - let other CPUs join now. */
toc_lock = 1;
num_online_cpus--;
if (os_toc_handler) {
/* will call OS handler, after all CPUs are here */
while (num_online_cpus)
; /* wait */
return os_toc_handler; /* let asm code call os handler */
}
/* No OS handler installed. Wait for all CPUs, then last CPU will reset. */
if (num_online_cpus)
while (1) /* this CPU will wait endless. */;
printf("SeaBIOS: Resetting machine after TOC.\n");
reset();
}
/********************************************************
* BOOT MENU
********************************************************/
extern void find_initial_parisc_boot_drives(
struct drive_s **harddisc,
struct drive_s **cdrom);
extern struct drive_s *select_parisc_boot_drive(char bootdrive);
extern int parisc_get_scsi_target(struct drive_s **boot_drive, int target);
static void print_menu(void)
{
printf("\n------- Main Menu -------------------------------------------------------------\n\n"
" Command Description\n"
" ------- -----------\n"
" BOot [PRI|ALT|<path>] Boot from specified path\n"
#if 0
" PAth [PRI|ALT|CON|KEY] [<path>] Display or modify a path\n"
" SEArch [DIsplay|IPL] [<path>] Search for boot devices\n\n"
" COnfiguration [<command>] Access Configuration menu/commands\n"
" INformation [<command>] Access Information menu/commands\n"
" SERvice [<command>] Access Service menu/commands\n\n"
" DIsplay Redisplay the current menu\n"
#endif
" HElp [<menu>|<command>] Display help for menu or command\n"
" RESET Restart the system\n"
"-------\n");
}
/* Copyright (C) 1999 Jason L. Eckhardt (jle@cygnus.com) - taken from palo */
static char *enter_text(char *txt, int maxchars)
{
char c;
int pos;
for (pos = 0; txt[pos]; pos++); /* calculate no. of chars */
if (pos > maxchars) /* if input too long, shorten it */
{
pos = maxchars;
txt[pos] = '\0';
}
printf(txt); /* print initial text */
do
{
c = parisc_getchar();
if (c == 13)
{ /* CR -> finish! */
if (pos <= maxchars)
txt[pos] = 0;
else
txt[maxchars] = '\0';
return txt;
};
if (c == '\b' || c == 127 )
{ /* BS -> delete prev. char */
if (pos)
{
pos--;
c='\b';
parisc_putchar(c);
parisc_putchar(' ');
parisc_putchar(c);
}
} else if (c == 21)
{ /* CTRL-U */
while (pos)
{
pos--;
c='\b';
parisc_putchar(c);
parisc_putchar(' ');
parisc_putchar(c);
}
txt[0] = 0;
} else if ((pos < maxchars) && c >= ' ')
{
txt[pos] = c;
pos++;
parisc_putchar(c);
}
}
while (c != 13);
return txt;
}
static void menu_loop(void)
{
int scsi_boot_target;
char input[24];
char *c, reply;
// snprintf(input, sizeof(input), "BOOT FWSCSI.%d.0", boot_drive->target);
again:
print_menu();
again2:
input[0] = '\0';
printf("Main Menu: Enter command > ");
/* ask user for boot menu command */
enter_text(input, sizeof(input)-1);
parisc_putchar('\n');
/* convert to uppercase */
c = input;
while (*c) {
if ((*c >= 'a') && (*c <= 'z'))
*c += 'A'-'a';
c++;
}
if (input[0] == 'R' && input[1] == 'E') // RESET?
reset();
if (input[0] == 'H' && input[1] == 'E') // HELP?
goto again;
if (input[0] != 'B' || input[1] != 'O') { // BOOT?
printf("Unknown command, please try again.\n\n");
goto again2;
}
// from here on we handle "BOOT PRI/ALT/FWSCSI.x"
c = input;
while (*c && (*c != ' ')) c++; // search space
// preset with default boot target (this is same as "BOOT PRI"
scsi_boot_target = boot_drive->target;
if (c[0] == 'A' && c[1] == 'L' && c[2] == 'T')
scsi_boot_target = parisc_boot_cdrom->target;
while (*c) {
if (*c >= '0' && *c <= '9') {
scsi_boot_target = *c - '0';
break;
}
c++;
}
if (!parisc_get_scsi_target(&boot_drive, scsi_boot_target)) {
printf("No FWSCSI.%d.0 device available for boot. Please try again.\n\n",
scsi_boot_target);
goto again2;
}
printf("Interact with IPL (Y, N, or Cancel)?> ");
input[0] = '\0';
enter_text(input, 1);
parisc_putchar('\n');
reply = input[0];
if (reply == 'C' || reply == 'c')
goto again2;
// allow Z as Y. It's the key used on german keyboards.
if (reply == 'Y' || reply == 'y' || reply == 'Z' || reply == 'z')
interact_ipl = 1;
}
static int parisc_boot_menu(unsigned long *iplstart, unsigned long *iplend,
char bootdrive)
{
int ret;
unsigned int *target = (void *)(PAGE0->mem_free + 32*1024);
struct disk_op_s disk_op = {
.buf_fl = target,
.command = CMD_SEEK,
.count = 0,
.lba = 0,
};
boot_drive = select_parisc_boot_drive(bootdrive);
/* enter main menu if booted with "boot menu=on" */
if (show_boot_menu)
menu_loop();
else
interact_ipl = 0;
disk_op.drive_fl = boot_drive;
if (boot_drive == NULL) {
printf("SeaBIOS: No boot device.\n");
return 0;
}
printf("\nBooting from FWSCSI.%d.0 ...\n", boot_drive->target);
/* seek to beginning of disc/CD */
disk_op.drive_fl = boot_drive;
ret = process_op(&disk_op);
// printf("DISK_SEEK returned %d\n", ret);
if (ret)
return 0;
// printf("Boot disc type is 0x%x\n", boot_drive->type);
disk_op.drive_fl = boot_drive;
if (boot_drive->type == DTYPE_ATA_ATAPI ||
boot_drive->type == DTYPE_ATA) {
disk_op.command = CMD_ISREADY;
ret = process_op(&disk_op);
} else {
ret = scsi_is_ready(&disk_op);
}
// printf("DISK_READY returned %d\n", ret);
/* read boot sector of disc/CD */
disk_op.drive_fl = boot_drive;
disk_op.buf_fl = target;
disk_op.command = CMD_READ;
disk_op.count = (FW_BLOCKSIZE / disk_op.drive_fl->blksize);
disk_op.lba = 0;
// printf("blocksize is %d, count is %d\n", disk_op.drive_fl->blksize, disk_op.count);
ret = process_op(&disk_op);
// printf("DISK_READ(count=%d) = %d\n", disk_op.count, ret);
if (ret)
return 0;
unsigned int ipl_addr = be32_to_cpu(target[0xf0/sizeof(int)]); /* offset 0xf0 in bootblock */
unsigned int ipl_size = be32_to_cpu(target[0xf4/sizeof(int)]);
unsigned int ipl_entry= be32_to_cpu(target[0xf8/sizeof(int)]);
/* check LIF header of bootblock */
if ((target[0]>>16) != 0x8000) {
printf("Not a PA-RISC boot image. LIF magic is 0x%x, should be 0x8000.\n", target[0]>>16);
return 0;
}
// printf("ipl start at 0x%x, size %d, entry 0x%x\n", ipl_addr, ipl_size, ipl_entry);
// TODO: check ipl values for out of range. Rules are:
// IPL_ADDR - 2 Kbyte aligned, nonzero.
// IPL_SIZE - Multiple of 2 Kbytes, nonzero, less than or equal to 256 Kbytes.
// IPL_ENTRY- Word aligned, less than IPL_SIZE
/* seek to beginning of IPL */
disk_op.drive_fl = boot_drive;
disk_op.command = CMD_SEEK;
disk_op.count = 0; // (ipl_size / disk_op.drive_fl->blksize);
disk_op.lba = (ipl_addr / disk_op.drive_fl->blksize);
ret = process_op(&disk_op);
// printf("DISK_SEEK to IPL returned %d\n", ret);
/* read IPL */
disk_op.drive_fl = boot_drive;
disk_op.buf_fl = target;
disk_op.command = CMD_READ;
disk_op.count = (ipl_size / disk_op.drive_fl->blksize);
disk_op.lba = (ipl_addr / disk_op.drive_fl->blksize);
ret = process_op(&disk_op);
// printf("DISK_READ IPL returned %d\n", ret);
// printf("First word at %p is 0x%x\n", target, target[0]);
/* execute IPL */
// TODO: flush D- and I-cache, not needed in emulation ?
*iplstart = *iplend = (unsigned long) target;
*iplstart += ipl_entry;
*iplend += ALIGN(ipl_size, sizeof(unsigned long));
return 1;
}
/********************************************************
* FIRMWARE MAIN ENTRY POINT
********************************************************/
static const struct pz_device mem_cons_sti_boot = {
.hpa = LASI_GFX_HPA,
.iodc_io = (unsigned long)&iodc_entry,
.cl_class = CL_DISPL,
};
static const struct pz_device mem_kbd_sti_boot = {
.hpa = LASI_PS2KBD_HPA,
.iodc_io = (unsigned long)&iodc_entry,
.cl_class = CL_KEYBD,
};
static struct pz_device mem_cons_boot = {
.hpa = PARISC_SERIAL_CONSOLE - 0x800,
.iodc_io = (unsigned long)&iodc_entry,
.cl_class = CL_DUPLEX,
};
static struct pz_device mem_kbd_boot = {
.hpa = PARISC_SERIAL_CONSOLE - 0x800,
.iodc_io = (unsigned long)&iodc_entry,
.cl_class = CL_KEYBD,
};
static const struct pz_device mem_boot_boot = {
.dp.flags = PF_AUTOBOOT,
.hpa = IDE_HPA, // DINO_SCSI_HPA, // IDE_HPA
.iodc_io = (unsigned long) &iodc_entry,
.cl_class = CL_RANDOM,
};
static void find_pci_slot_for_dev(unsigned int pciid, char *pci_slot)
{
struct pci_device *pci;
foreachpci(pci)
if (pci->vendor == pciid) {
*pci_slot = (pci->bdf >> 3) & 0x0f;
return;
}
}
/* Prepare boot paths in PAGE0 and stable memory */
static void prepare_boot_path(volatile struct pz_device *dest,
const struct pz_device *source,
unsigned int stable_offset)
{
int hpa_index;
unsigned long hpa;
struct pdc_module_path *mod_path;
hpa = source->hpa;
hpa_index = find_hpa_index(hpa);
if (HPA_is_storage_device(hpa))
mod_path = &mod_path_emulated_drives;
else if (hpa == LASI_UART_HPA) // HPA_is_serial_device(hpa))
mod_path = &mod_path_hpa_ffd05000;
else if (hpa == DINO_UART_HPA) // HPA_is_serial_device(hpa))
mod_path = &mod_path_hpa_fff83000;
else {
BUG_ON(hpa_index < 0);
mod_path = parisc_devices[hpa_index].mod_path;
}
/* copy device path to entry in PAGE0 */
memcpy((void*)dest, source, sizeof(*source));
memcpy((void*)&dest->dp, mod_path, sizeof(struct device_path));
/* copy device path to stable storage */
memcpy(&stable_storage[stable_offset], mod_path, sizeof(*mod_path));
BUG_ON(sizeof(*mod_path) != 0x20);
BUG_ON(sizeof(struct device_path) != 0x20);
}
static int artist_present(void)
{
return !!(*(u32 *)0xf8380004 == 0x6dc20006);
}
unsigned long _atoul(char *str)
{
unsigned long val = 0;
while (*str) {
val *= 10;
val += *str - '0';
str++;
}
return val;
}
unsigned long romfile_loadstring_to_int(const char *name, unsigned long defval)
{
char *str = romfile_loadfile(name, NULL);
if (str)
return _atoul(str);
return defval;
}
void __VISIBLE start_parisc_firmware(void)
{
unsigned int i, cpu_hz;
unsigned long iplstart, iplend;
char *str;
char bootdrive = (char)cmdline; // c = hdd, d = CD/DVD
show_boot_menu = (linux_kernel_entry == 1);
if (smp_cpus > HPPA_MAX_CPUS)
smp_cpus = HPPA_MAX_CPUS;
num_online_cpus = smp_cpus;
if (ram_size >= FIRMWARE_START)
ram_size = FIRMWARE_START;
/* Initialize malloc stack */
malloc_preinit();
/* Initialize qemu fw_cfg interface */
PORT_QEMU_CFG_CTL = fw_cfg_port;
qemu_cfg_init();
/* Initialize boot structures. Needs working fw_cfg for bootprio option. */
boot_init();
i = romfile_loadint("/etc/firmware-min-version", 0);
if (i && i > SEABIOS_HPPA_VERSION) {
printf("\nSeaBIOS firmware is version %d, but version %d is required. "
"Please update.\n", (int)SEABIOS_HPPA_VERSION, i);
hlt();
}
/* Qemu versions which request a SEABIOS_HPPA_VERSION < 6 have the bug that
* they use the DINO UART instead of the LASI UART as serial port #0.
* Fix it up here and switch the serial console code to use PORT_SERIAL2
* for such Qemu versions, so that we can still use this higher SeaBIOS
* version with older Qemus. */
if (i < 6) {
mem_cons_boot.hpa = PORT_SERIAL2 - 0x800;
mem_kbd_boot.hpa = PORT_SERIAL2 - 0x800;
}
tlb_entries = romfile_loadint("/etc/cpu/tlb_entries", 256);
dprintf(0, "fw_cfg: TLB entries %d\n", tlb_entries);
btlb_entries = romfile_loadint("/etc/cpu/btlb_entries", 8);
dprintf(0, "fw_cfg: BTLB entries %d\n", btlb_entries);
powersw_ptr = (int *) (unsigned long)
romfile_loadint("/etc/power-button-addr", (unsigned long)&powersw_nop);
/* use -fw_cfg opt/pdc_debug,string=255 to enable all firmware debug infos */
pdc_debug = romfile_loadstring_to_int("opt/pdc_debug", 0);
pdc_console = CONSOLE_DEFAULT;
str = romfile_loadfile("opt/console", NULL);
if (str) {
if (strcmp(str, "serial") == 0)
pdc_console = CONSOLE_SERIAL;
if (strcmp(str, "graphics") == 0)
pdc_console = CONSOLE_GRAPHICS;
}
/* 0,1 = default 8x16 font, 2 = 16x32 font */
sti_font = romfile_loadstring_to_int("opt/font", 0);
model.sw_id = romfile_loadstring_to_int("opt/hostid", model.sw_id);
dprintf(0, "fw_cfg: machine hostid %lu\n", model.sw_id);
/* Do not initialize PAGE0. We have the boot args stored there. */
/* memset((void*)PAGE0, 0, sizeof(*PAGE0)); */
/* copy pdc_entry entry into low memory. */
memcpy((void*)MEM_PDC_ENTRY, &pdc_entry_table, 3*4);
flush_data_cache((char*)MEM_PDC_ENTRY, 3*4);
PAGE0->memc_cont = ram_size;
PAGE0->memc_phsize = ram_size;
PAGE0->memc_adsize = ram_size;
PAGE0->mem_pdc_hi = (MEM_PDC_ENTRY + 0ULL) >> 32;
PAGE0->mem_free = 0x6000; // min PAGE_SIZE
PAGE0->mem_hpa = CPU_HPA; // HPA of boot-CPU
PAGE0->mem_pdc = MEM_PDC_ENTRY;
PAGE0->mem_10msec = CPU_CLOCK_MHZ*(1000000ULL/100);
BUG_ON(PAGE0->mem_free <= MEM_PDC_ENTRY);
BUG_ON(smp_cpus < 1 || smp_cpus > HPPA_MAX_CPUS);
BUG_ON(sizeof(pim_toc_data[0]) != PIM_STORAGE_SIZE);
/* Put QEMU/SeaBIOS marker in PAGE0.
* The Linux kernel will search for it. */
memcpy((char*)&PAGE0->pad0, "SeaBIOS", 8);
PAGE0->pad0[2] = ((unsigned long long)PORT_QEMU_CFG_CTL) >> 32; /* store as 64bit value */
PAGE0->pad0[3] = PORT_QEMU_CFG_CTL;
*powersw_ptr = 0x01; /* button not pressed, hw controlled. */
PAGE0->imm_hpa = MEMORY_HPA;
PAGE0->imm_spa_size = ram_size;
PAGE0->imm_max_mem = ram_size;
/* initialize graphics (if available) */
if (artist_present()) {
sti_rom_init();
sti_console_init(&sti_proc_rom);
PAGE0->proc_sti = (u32)&sti_proc_rom;
ps2port_setup();
} else {
remove_from_keep_list(LASI_GFX_HPA);
remove_from_keep_list(LASI_PS2KBD_HPA);
remove_from_keep_list(LASI_PS2MOU_HPA);
}
// Initialize boot paths (graphics & keyboard)
if (pdc_console == CONSOLE_DEFAULT) {
if (artist_present())
pdc_console = CONSOLE_GRAPHICS;
else
pdc_console = CONSOLE_SERIAL;
}
if (pdc_console == CONSOLE_GRAPHICS) {
prepare_boot_path(&(PAGE0->mem_cons), &mem_cons_sti_boot, 0x60);
prepare_boot_path(&(PAGE0->mem_kbd), &mem_kbd_sti_boot, 0xa0);
} else {
prepare_boot_path(&(PAGE0->mem_cons), &mem_cons_boot, 0x60);
prepare_boot_path(&(PAGE0->mem_kbd), &mem_kbd_boot, 0xa0);
}
/* Initialize device list */
remove_parisc_devices(smp_cpus);
/* Show list of HPA devices which are still returned by firmware. */
if (0) { for (i=0; parisc_devices[i].hpa; i++)
printf("Kept #%d at 0x%lx\n", i, parisc_devices[i].hpa);
}
// Initialize stable storage
init_stable_storage();
chassis_code = 0;
// set Qemu serial debug port
DebugOutputPort = PARISC_SERIAL_CONSOLE;
// PlatformRunningOn = PF_QEMU; // emulate runningOnQEMU()
cpu_hz = 100 * PAGE0->mem_10msec; /* Hz of this PARISC */
dprintf(1, "\nPARISC SeaBIOS Firmware, %d x PA7300LC (PCX-L2) at %d.%06d MHz, %d MB RAM.\n",
smp_cpus, cpu_hz / 1000000, cpu_hz % 1000000,
ram_size/1024/1024);
if (ram_size < MIN_RAM_SIZE) {
printf("\nSeaBIOS: Machine configured with too little "
"memory (%d MB), minimum is %d MB.\n\n",
ram_size/1024/1024, MIN_RAM_SIZE/1024/1024);
hlt();
}
// handle_post();
serial_debug_preinit();
debug_banner();
// maininit();
qemu_preinit();
RamSize = ram_size;
// coreboot_preinit();
pci_setup();
serial_setup();
block_setup();
PAGE0->vec_rendz = 0; /* No rendezvous yet. Add MEM_RENDEZ_HI later */
/* if this is a reboot, force secondary CPUs to enter smp_idle loop */
if (PAGE0->imm_soft_boot) {
for (i = 1; i < smp_cpus; i++) {
unsigned long hpa;
hpa = CPU_HPA_IDX(i);
*(int*)hpa = -1; /* send Wakeup IRQ to trigger QEMU to load boot address */
}
}
printf("\n");
printf("SeaBIOS PA-RISC Firmware Version %d\n"
"\n"
"Duplex Console IO Dependent Code (IODC) revision 1\n"
"\n", SEABIOS_HPPA_VERSION);
printf("------------------------------------------------------------------------------\n"
" (c) Copyright 2017-2023 Helge Deller <deller@gmx.de> and SeaBIOS developers.\n"
"------------------------------------------------------------------------------\n\n");
printf( " Processor Speed State Coprocessor State Cache Size\n"
" --------- -------- --------------------- ----------------- ----------\n");
for (i = 0; i < smp_cpus; i++)
printf(" %s%d " __stringify(CPU_CLOCK_MHZ)
" MHz %s Functional 0 KB\n",
i < 10 ? " ":"", i, i?"Idle ":"Active");
printf("\n\n");
printf(" Available memory: %u MB\n"
" Good memory required: %d MB\n\n",
ram_size/1024/1024, MIN_RAM_SIZE/1024/1024);
// search boot devices
find_initial_parisc_boot_drives(&parisc_boot_harddisc, &parisc_boot_cdrom);
printf(" Primary boot path: FWSCSI.%d.%d\n"
" Alternate boot path: FWSCSI.%d.%d\n"
" Console path: %s\n"
" Keyboard path: %s\n\n",
parisc_boot_harddisc->target, parisc_boot_harddisc->lun,
parisc_boot_cdrom->target, parisc_boot_cdrom->lun,
hpa_device_name(PAGE0->mem_cons.hpa, 1),
hpa_device_name(PAGE0->mem_kbd.hpa, 0));
if (bootdrive == 'c')
boot_drive = parisc_boot_harddisc;
else
boot_drive = parisc_boot_cdrom;
// Find PCI bus id of LSI SCSI card
find_pci_slot_for_dev(PCI_VENDOR_ID_LSI_LOGIC,
&mod_path_emulated_drives.path.bc[5]);
// Store initial emulated drives path master data
if (parisc_boot_harddisc) {
mod_path_emulated_drives.layers[0] = parisc_boot_harddisc->target;
mod_path_emulated_drives.layers[1] = parisc_boot_harddisc->lun;
}
prepare_boot_path(&(PAGE0->mem_boot), &mem_boot_boot, 0x0);
// copy primary boot path to alt boot path
memcpy(&stable_storage[0x80], &stable_storage[0], 0x20);
if (parisc_boot_cdrom) {
stable_storage[0x80 + 11] = parisc_boot_cdrom->target;
stable_storage[0x80 + 12] = parisc_boot_cdrom->lun;
}
// currently booted path == CD in PAGE0->mem_boot
if (boot_drive) {
PAGE0->mem_boot.dp.layers[0] = boot_drive->target;
PAGE0->mem_boot.dp.layers[1] = boot_drive->lun;
}
/* Qemu-specific: Drop *all* TLB entries for all CPUs before start. */
/* Necessary if machine was rebooted. */
asm("pdtlbe %%r0(%%sr1,%%r0)" : : : "memory");
/* directly start Linux kernel if it was given on qemu command line. */
if (linux_kernel_entry > 1) {
void (*start_kernel)(unsigned long mem_free, unsigned long cline,
unsigned long rdstart, unsigned long rdend);
printf("Autobooting Linux kernel which was loaded by qemu...\n\n");
start_kernel = (void *) linux_kernel_entry;
/* zero out kernel entry point in case we reset the machine: */
linux_kernel_entry = 0;
start_kernel(PAGE0->mem_free, cmdline, initrd_start, initrd_end);
hlt(); /* this ends the emulator */
}
/* check for bootable drives, and load and start IPL bootloader if possible */
if (parisc_boot_menu(&iplstart, &iplend, bootdrive)) {
void (*start_ipl)(long interactive, long iplend);
PAGE0->mem_boot.dp.layers[0] = boot_drive->target;
PAGE0->mem_boot.dp.layers[1] = boot_drive->lun;
printf("\nBooting...\n"
"Boot IO Dependent Code (IODC) revision 153\n\n"
"%s Booted.\n", PAGE0->imm_soft_boot ? "SOFT":"HARD");
start_ipl = (void *) iplstart;
start_ipl(interact_ipl, iplend);
}
hlt(); /* this ends the emulator */
}