blob: c3c4253e035166b7893cd16738bc92a94c6ac168 [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.
//
// Example command line:
//
//Funktioniert:
//./qemu-system-hppa -drive file=../qemu-images/hdd.img -kernel vmlinux -append "root=/dev/sda5 cryptomgr.notests" -smp cpus=2 -snapshot -machine C3700 -vnc :1 -fw_cfg opt/console,string=serial -serial mon:stdio -device secondary-vga #-d in_asm # -fw_cfg opt/pdc_debug,string=255 2>&1| grep ohci # -d trace:serial\* # ,mmu,trace:serial\*,trace:iosapic\*,trace:elroy\*,trace:astro\*,trace:pci\*
//
// ./qemu-system-hppa -drive file=../qemu-images/hdd.img -kernel vmlinux -append root=/dev/sda5 cryptomgr.notests console=ttyS0 earlycon=pdc -accel tcg,thread=multi,one-insn-per-tb=off -serial mon:stdio -smp cpus=1 -snapshot -nographic -machine B160L
// /qemu-system-hppa -drive file=../qemu-images/hdd.img -kernel vmlinux -append "root=/dev/sda5 cryptomgr.notests console=ttyS0 earlycon=pdc" -accel tcg,thread=multi,one-insn-per-tb=off -serial mon:stdio -smp cpus=1 -nographic -machine C3700 -device pci-serial-4x -device pci-ohci -device usb-tablet -device ES1370 -snapshot # -fw_cfg opt/pdc_debug,string=255 # -d trace:serial\* # ,mmu,trace:serial\*,trace:iosapic\*,trace:elroy\*,trace:astro\*,trace:pci\*
#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/usb-ohci.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/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);
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.
********************************************************/
/* boot_args[] variables provided by qemu */
#define boot_args PAGE0->pad608
#define ram_size boot_args[0]
#define linux_kernel_entry boot_args[1]
#define cmdline boot_args[2]
#define initrd_start boot_args[3]
#define initrd_end boot_args[4]
#define smp_cpus boot_args[5]
#define pdc_debug boot_args[6]
#define fw_cfg_port boot_args[7]
/* 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;
char qemu_version[16] = "unknown version";
char qemu_machine[16] = "B160L";
char cpu_bit_width;
char has_astro;
unsigned long pci_hpa; /* HPA of Dino or Elroy0 */
#if 0
unsigned long hppa_port_pci_cmd = (PCI_HPA + DINO_PCI_ADDR);
unsigned long hppa_port_pci_data = (PCI_HPA + DINO_CONFIG_DATA);
#else
unsigned long hppa_port_pci_cmd;
unsigned long hppa_port_pci_data;
#endif
/* 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;
#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;
/*
* Real time clock emulation
* Either LASI or Astro will provide access to an emulated RTC clock.
*/
int *rtc_ptr = (int *) (unsigned long) LASI_RTC_HPA;
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 SeaBIOS-hppa-v%d:\n%s:%d\n", SEABIOS_HPPA_VERSION, \
__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
********************************************************/
#define MAX_DEVICES HPPA_MAX_CPUS+16
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];
struct pci_device *pci;
unsigned long pci_addr;
} hppa_device_t;
static hppa_device_t *find_hpa_device(unsigned long hpa);
#define CONCAT2(a, b) a ## b
#define CONCAT(a, b) CONCAT2(a, b)
struct machine_info {
char pdc_modelstr[16];
struct pdc_model pdc_model;
int pdc_version;
int pdc_cpuid;
int pdc_caps;
int pdc_entry;
unsigned long pdc_cache_info[30];
hppa_device_t device_list[MAX_DEVICES];
};
/* create machine definitions */
#define MACHINE B160L
#include "parisc/b160l.h"
#include "parisc/machine-create.h"
#define MACHINE C3700
#include "parisc/c3700.h"
#include "parisc/machine-create.h"
struct machine_info *current_machine = &machine_B160L;
static hppa_device_t *parisc_devices = machine_B160L.device_list;
#define PARISC_KEEP_LIST \
DINO_UART_HPA,\
/* DINO_SCSI_HPA, */ \
LASI_UART_HPA, \
LASI_LAN_HPA, \
LASI_LPT_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(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_astro_ioport(unsigned long hpa)
{
if (!has_astro)
return 0;
return ((hpa - IOS_DIST_BASE_ADDR) < IOS_DIST_BASE_SIZE);
}
int HPA_is_astro_mmio(unsigned long hpa)
{
if (!has_astro)
return 0;
return ((hpa - LMMIO_DIST_BASE_ADDR) < LMMIO_DIST_BASE_SIZE);
}
struct pci_device *find_pci_from_HPA(unsigned long hpa)
{
struct pci_device *pci = NULL;
int ioport;
if (HPA_is_astro_ioport(hpa)) {
ioport = 1;
hpa -= IOS_DIST_BASE_ADDR;
} else if (HPA_is_astro_mmio(hpa))
ioport = 0;
else
return NULL;
foreachpci(pci) {
int i;
for (i = 0; i < 6; i++) {
unsigned long addr = PCI_BASE_ADDRESS_0 + 4*i;
unsigned long mem;
mem = pci_config_readl(pci->bdf, addr);
if ((mem & PCI_BASE_ADDRESS_SPACE_IO) &&
((mem & PCI_BASE_ADDRESS_IO_MASK) == hpa) &&
(ioport == 1))
return pci; /* found ioport */
if (((mem & PCI_BASE_ADDRESS_SPACE_IO) == 0) &&
((mem & PCI_BASE_ADDRESS_MEM_MASK) == hpa) &&
(ioport == 0))
return pci; /* found memaddr */
}
}
dprintf(1, "No PCI device found for HPA %lx\n", hpa);
return NULL;
}
int DEV_is_storage_device(hppa_device_t *dev)
{
BUG_ON(!dev);
if (dev->pci)
return (dev->pci->class == PCI_CLASS_STORAGE_SCSI);
return ((dev->iodc->type & 0xf) == 0x04); /* HPHW_A_DMA */
}
int DEV_is_serial_device(hppa_device_t *dev)
{
BUG_ON(!dev);
if (dev->pci)
return (dev->pci->class == PCI_CLASS_COMMUNICATION_SERIAL);
return ((dev->iodc->type & 0xf) == 0x0a); /* HPHW_FIO */
}
int HPA_is_serial_device(unsigned long hpa)
{
hppa_device_t *dev;
dev = find_hpa_device(hpa);
if (!dev)
return 0;
return DEV_is_serial_device(dev);
}
int DEV_is_network_device(hppa_device_t *dev)
{
BUG_ON(!dev);
if (dev->pci)
return (dev->pci->class == PCI_CLASS_NETWORK_ETHERNET);
return ((dev->iodc->type & 0xf) == 0x0a); /* HPHW_FIO */
}
static int HPA_is_LASI_keyboard(unsigned long hpa)
{
return !has_astro && (hpa == LASI_PS2KBD_HPA);
}
#if 0
static int HPA_is_keyboard_device(unsigned long hpa)
{
struct pci_device *pci;
int ret;
if (HPA_is_LASI_keyboard(hpa))
return 1;
pci = find_pci_from_HPA(hpa);
if (!pci)
return 0;
ret = pci->class == PCI_CLASS_COMMUNICATION_SERIAL ||
pci->class == PCI_CLASS_INPUT_KEYBOARD;
dprintf(1, "PCI: is_keyboard? %pP -> %s\n", pci, ret?"Yes":"no");
return ret;
}
#endif
int HPA_is_LASI_graphics(unsigned long hpa)
{
hppa_device_t *dev;
dev = find_hpa_device(hpa);
if (!dev)
return 0;
return (dev->iodc->sversion_model == 0x003b); /* XXX */
}
#define GFX_NUM_PAGES 0x2000
int HPA_is_graphics_device(unsigned long hpa)
{
hppa_device_t *dev;
dev = find_hpa_device(hpa);
if (!dev)
return 0;
if (dev->pci)
return (dev->pci->class >> 8) == PCI_BASE_CLASS_DISPLAY;
return (dev->iodc->sversion_model == 0x3b); /* XXX */
}
static const char *hpa_device_name(unsigned long hpa, int output)
{
return HPA_is_LASI_graphics(hpa) ? "GRAPHICS(1)" :
HPA_is_graphics_device(hpa) ? "VGA" :
HPA_is_LASI_keyboard(hpa) ? "PS2" :
((hpa + 0x800) == PORT_SERIAL1) ?
"SERIAL_1.9600.8.none" : "SERIAL_2.9600.8.none";
}
static void print_mod_path(struct pdc_module_path *p)
{
dprintf(1, "PATH %d/%d/%d/%d/%d/%d/%d:%d.%d.%d ", p->path.bc[0], p->path.bc[1],
p->path.bc[2],p->path.bc[3],p->path.bc[4],p->path.bc[5],
p->path.mod, p->layers[0], p->layers[1], p->layers[2]);
}
void make_module_path_from_pcidev(struct pci_device *pci,
struct pdc_module_path *p)
{
memset(p, 0, sizeof(*p));
memset(&p->path.bc, 0xff, sizeof(p->path.bc));
switch (pci->class) {
case PCI_CLASS_COMMUNICATION_SERIAL:
case PCI_CLASS_NETWORK_ETHERNET:
case PCI_CLASS_STORAGE_SCSI:
case PCI_CLASS_SERIAL_USB:
default:
#if 0
static struct pdc_module_path mod_path_hpa_fed3c000 = { // SCSI
.path = { .flags = 0x0, .bc = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xa }, .mod = 0x6 },
.layers = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
#endif
p->path.bc[3] = has_astro ? ASTRO_BUS_MODULE : 0x08; /* astro or dino */
p->path.bc[4] = 0; // elroy index (0,1,4,6) == PCI Bus number (0,1,2,3)
p->path.bc[5] = pci->bdf >> 3; /* slot */
p->path.mod = pci->bdf & 0x07; /* function */
break;
}
}
void make_iodc_from_pcidev(struct pci_device *pci,
struct pdc_iodc *p)
{
memset(p, 0, sizeof(*p));
p->hversion_model = 0x0058;
p->hversion = 0x0020;
p->sversion_model = 0;
p->sversion_opt = 0;
p->rev = 0x0099;
p->length = 1;
switch (pci->class) {
case PCI_CLASS_STORAGE_SCSI:
p->features = 0x0001;
p->type = 0x0084;
break;
case PCI_CLASS_COMMUNICATION_SERIAL:
case PCI_CLASS_NETWORK_ETHERNET:
case PCI_CLASS_SERIAL_USB:
default:
p->type = 0x008a;
break;
}
};
void make_modinfo_from_pcidev(struct pci_device *pci,
struct pdc_system_map_mod_info *p, unsigned long pfa)
{
memset(p, 0, sizeof(*p));
p->mod_addr = pfa;
p->mod_pgs = 0;
p->add_addrs = HPA_is_graphics_device(pfa) ? GFX_NUM_PAGES : 0;
};
#define MAX_PCI_DEVICES 10
static int curr_pci_devices; /* current number of PCI devices in list */
static hppa_device_t hppa_pci_devices[MAX_PCI_DEVICES];
static struct pdc_iodc hppa_pci_iodc[MAX_PCI_DEVICES];
static struct pdc_system_map_mod_info hppa_pci_mod_info[MAX_PCI_DEVICES];
static struct pdc_module_path hppa_pci_mod_path[MAX_PCI_DEVICES];
/* create table of PFA (PCI Function address), similiar to HPA */
static void hppa_pci_build_devices_list(void)
{
struct pci_device *pci;
memset(&hppa_pci_devices, 0, sizeof(hppa_pci_devices));
curr_pci_devices = 0;
dprintf(1, "\n PCI DEVICE LIST PFA TABLE\n");
foreachpci(pci) {
unsigned long pfa, offs;
hppa_device_t *pdev = &hppa_pci_devices[curr_pci_devices];
offs = elroy_offset(pci->bdf);
BUG_ON(offs == -1UL);
pfa = (unsigned long) elroy_port(0, offs);
// pfa += pci->bdf << 8;
// pfa |= SCSI_HPA;
pfa = pci_hpa; /* Elroy0 or Dino */
pdev->hpa = pfa;
pdev->iodc = &hppa_pci_iodc[curr_pci_devices];
pdev->mod_info = &hppa_pci_mod_info[curr_pci_devices];
pdev->mod_path = &hppa_pci_mod_path[curr_pci_devices];
pdev->num_addr = 0;
pdev->pci = pci;
make_iodc_from_pcidev(pci, pdev->iodc);
make_modinfo_from_pcidev(pci, pdev->mod_info, pfa);
make_module_path_from_pcidev(pci, pdev->mod_path);
dprintf(1, "PCI device #%d %pP bdf 0x%x at pfa 0x%lx ", curr_pci_devices, pci, pci->bdf, pfa);
print_mod_path(pdev->mod_path);
dprintf(1, "\n");
curr_pci_devices++;
BUG_ON(curr_pci_devices >= MAX_PCI_DEVICES);
}
}
static hppa_device_t *find_hpa_device(unsigned long hpa)
{
int i;
/* search classical HPPA devices */
if (hpa)
for (i = 0; i < (MAX_DEVICES-1); i++) {
if (hpa == parisc_devices[i].hpa)
return &parisc_devices[i];
if (!parisc_devices[i].hpa)
break;
}
/* search PCI devices */
for (i = 0; i < curr_pci_devices; i++) {
if (hpa == hppa_pci_devices[i].hpa)
return &hppa_pci_devices[i];
}
return NULL;
}
static unsigned long keep_list[20] = { 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;
}
/* walk all machine devices and add generic ones to the keep_list[] */
static int keep_add_generic_devices(void)
{
hppa_device_t *dev = current_machine->device_list;
int i = 0;
/* search end of list */
while (keep_list[i]) i++;
while (dev->hpa) {
switch (dev->iodc->type) {
case 0x0041: /* Memory. Save HPA in PAGE0 entry. */
PAGE0->imm_hpa = dev->hpa;
/* fallthrough */
case 0x0007: /* GSC+ Port bridge */
case 0x004d: /* Dino PCI bridge */
case 0x004b: /* Core Bus adapter (LASI) */
case 0x0040: /* CPU */
case 0x000d: /* Elroy PCI bridge */
case 0x000c: /* Runway port */
keep_list[i++] = dev->hpa;
}
dev++;
}
BUG_ON(i >= ARRAY_SIZE(keep_list));
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 > MAX_DEVICES);
while (t < MAX_DEVICES) {
memset(&parisc_devices[t], 0, sizeof(*parisc_devices));
t++;
}
}
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 0;
for(i = 0; i < ARRAY_SIZE(path->path.bc); i++) {
if (path->path.bc[i] != search->path.bc[i])
return 0;
}
if (check_layers) {
for(i = 0; i < ARRAY_SIZE(path->layers); i++) {
if (path->layers[i] != search->layers[i])
return 0;
}
}
return 1;
}
static hppa_device_t *find_hppa_device_by_path(unsigned long hpa,
struct pdc_module_path *search,
unsigned long *index, int check_layers)
{
hppa_device_t *dev;
int i, nr = 0;
for (i = 0; i < (MAX_DEVICES-1); i++) {
dev = parisc_devices + i;
if (dev->hpa != hpa)
continue;
if (compare_module_path(dev->mod_path, search, check_layers)) {
if (index)
*index = nr;
return dev;
}
nr++;
}
/* search PCI devices */
for (i = 0; i < curr_pci_devices; i++) {
dev = hppa_pci_devices + i;
if (compare_module_path(dev->mod_path, search, check_layers)) {
if (index)
*index = nr;
return dev;
}
nr++;
}
return NULL;
}
static hppa_device_t *find_hppa_device_by_index(unsigned int index, int search_pci)
{
hppa_device_t *dev;
int i;
for (i = 0; i < (MAX_DEVICES-1); i++) {
dev = parisc_devices + i;
if (!dev->hpa)
continue;
if (index-- == 0)
return dev;
}
/* search PCI devices */
if (search_pci) {
for (i = 0; i < curr_pci_devices; i++) {
dev = hppa_pci_devices + i;
if (index-- == 0)
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;
unsigned long end = timer_calc(SERIAL_TIMEOUT);
unsigned long count = 0;
if (has_astro) {
hppa_device_t *dev = find_hpa_device(addr);
BUG_ON(!dev);
addr = dev->pci_addr;
} else
addr += 0x800;
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;
/* might not be initialized if problems happen during early bootup */
if (!addr) {
/* use debugoutput instead */
dprintf(0, "%c", c);
return;
}
if (0) {
dprintf(1,"parisc_serial_out search hpa %x ", PAGE0->mem_cons.hpa);
print_mod_path(&PAGE0->mem_cons.dp);
dprintf(1," \n");
}
hppa_device_t *dev;
dev = find_hppa_device_by_path(addr, &PAGE0->mem_cons.dp, NULL, 0);
if (0) {
dprintf(1,"parisc_serial_out hpa %x\n", PAGE0->mem_cons.hpa);
print_mod_path(dev->mod_path);
}
if (!dev) hlt();
BUG_ON(!dev);
if (dev->pci_addr)
addr = dev->pci_addr;
else
addr += 0x800; /* add offset for serial port on GSC */
// dprintf(1," addr %x\n", addr);
if (c == '\n')
parisc_serial_out('\r');
for (;;) {
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_LASI_graphics(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_LASI_keyboard(PAGE0->mem_kbd.hpa))
count = lasips2_kbd_in(&c, sizeof(c));
else if (HPA_is_serial_device(PAGE0->mem_kbd.hpa))
count = parisc_serial_in(&c, sizeof(c));
else
BUG_ON(1);
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;
hppa_device_t *dev = NULL;
int ret, len;
char *c;
struct disk_op_s disk_op;
if (option == ENTRY_IO_COUT || option == ENTRY_IO_CIN)
dev = find_hppa_device_by_path(hpa, &PAGE0->mem_cons.dp, NULL, 0);
else if (option == ENTRY_IO_BOOTIN || option == ENTRY_IO_BBLOCK_IN)
dev = find_hppa_device_by_path(hpa, &PAGE0->mem_cons.dp, NULL, 0);
if (!dev) {
// BUG_ON(1);
return PDC_INVALID_ARG;
}
if (1 &&
(((DEV_is_serial_device(dev) || HPA_is_graphics_device(hpa)) && option == ENTRY_IO_COUT) ||
((DEV_is_serial_device(dev) || HPA_is_graphics_device(hpa)) && option == ENTRY_IO_CIN) ||
((DEV_is_storage_device(dev) && 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 (DEV_is_serial_device(dev) || HPA_is_LASI_graphics(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_LASI_keyboard(hpa))
result[0] = lasips2_kbd_in(c, ARG7);
else if (DEV_is_serial_device(dev))
result[0] = parisc_serial_in(c, ARG7);
return PDC_OK;
}
/* boot medium I/O */
if (DEV_is_storage_device(dev))
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;
hppa_device_t *dev;
iodc_log_call(arg, __FUNCTION__);
dev = find_hpa_device(hpa);
// dprintf(1, "HPA1 %lx DEV %p pci %p\n", hpa, dev, dev->pci);
if (!dev || !(DEV_is_storage_device(dev) || DEV_is_serial_device(dev) || DEV_is_network_device(dev)))
return PDC_INVALID_ARG;
// dprintf(1, "HPA2 %lx DEV %p\n", hpa, dev);
switch (option) {
case ENTRY_INIT_SRCH_FRST: /* 2: Search first */
if (DEV_is_network_device(dev))
return PDC_NE_BOOTDEV; /* No further boot devices */
memcpy((void *)ARG3, &mod_path_emulated_drives.layers,
sizeof(mod_path_emulated_drives.layers)); /* fill ID_addr */
result[0] = 0;
result[1] = DEV_is_serial_device(dev) ? CL_DUPLEX : CL_RANDOM;
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] = DEV_is_serial_device(dev) ? CL_DUPLEX: CL_RANDOM;
if (DEV_is_network_device(dev))
result[2] = result[3] = 0x11221133; /* TODO?: MAC of network card. */
else
result[2] = result[3] = 0;
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__);
BUG_ON(1);
return PDC_BAD_OPTION;
}
int __VISIBLE parisc_iodc_ENTRY_CONFIG(unsigned int *arg FUNC_MANY_ARGS)
{
iodc_log_call(arg, __FUNCTION__);
BUG_ON(1);
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;
hppa_device_t *dev;
iodc_log_call(arg, __FUNCTION__);
dev = find_hpa_device(hpa);
if (!dev || !(DEV_is_storage_device(dev) || DEV_is_serial_device(dev)))
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;
}
/* 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 >= smp_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 int pdc_model(unsigned int *arg)
{
const char *model_str = current_machine->pdc_modelstr;
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
switch (option) {
case PDC_MODEL_INFO:
/*
* In case we run on a 32-bit only emulation, avoid a kernel crash
* with old qemu versions which will try to run 64-bit instructions
* kernel sr_disable_hash() function
*/
#if 1
memcpy(result, (cpu_bit_width == 64) ?
&current_machine->pdc_model : &machine_B160L.pdc_model,
sizeof(current_machine->pdc_model));
#else
memcpy(result, &current_machine->pdc_model, sizeof(current_machine->pdc_model));
struct pdc_model *p = (struct pdc_model *) result;
/* HACK */
p->arch_rev = 0x04; /* 32-bit CPU, 64-bit has value 0x08 */
#endif
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] = current_machine->pdc_version;
return PDC_OK;
}
return -4; // invalid c_index
case PDC_MODEL_SYSMODEL:
result[0] = strlen(model_str);
strtcpy((char *)ARG4, model_str, result[0] + 1);
return PDC_OK;
case PDC_MODEL_ENSPEC:
case PDC_MODEL_DISPEC:
if (ARG3 != current_machine->pdc_model.pot_key)
return -20;
return PDC_OK;
case PDC_MODEL_CPU_ID:
result[0] = current_machine->pdc_cpuid;
/* if CPU does not support 64bits, use the B160L CPUID */
if (cpu_bit_width != 64)
result[0] = machine_B160L.pdc_cpuid;
return PDC_OK;
case PDC_MODEL_CAPABILITIES:
result[0] = current_machine->pdc_caps;
result[0] |= PDC_MODEL_OS32; /* we do support 32-bit */
if (cpu_bit_width == 64) /* and maybe 64-bit */
result[0] |= PDC_MODEL_OS64;
else
result[0] &= ~PDC_MODEL_OS64;
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;
case PDC_MODEL_GET_PLATFORM_INFO:
model_str = has_astro ? "A6057A" : "9000/778";
strtcpy((char *)ARG2, model_str, 16);
strtcpy((char *)ARG3, model_str, 16);
/* use: current_machine->pdc_model.sw_id ? */
strtcpy((char *)ARG4, "001122334455", 16);
return PDC_OK;
}
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;
struct pdc_cache_info *machine_cache_info
= (struct pdc_cache_info *) current_machine->pdc_cache_info;
switch (option) {
case PDC_CACHE_INFO:
machine_cache_info->it_size = tlb_entries;
machine_cache_info->dt_size = tlb_entries;
machine_cache_info->it_conf = (struct pdc_tlb_cf) { .tc_sh = 3, .tc_page = 1, .tc_cst = 1, .tc_sr = 0, };
machine_cache_info->dt_conf = (struct pdc_tlb_cf) { .tc_sh = 3, .tc_page = 1, .tc_cst = 1, .tc_sr = 0, };
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
machine_cache_info->ic_size = 1024; /* no instruction cache */
machine_cache_info->dc_size = 1024; /* no data cache */
#elif 0
machine_cache_info->dc_conf = (struct pdc_cache_cf) { 0 }; // .alias=1, .sh=3, };
machine_cache_info->ic_conf = (struct pdc_cache_cf) { 0 }; // .alias=1, .sh=3, };
machine_cache_info->ic_size = 0; /* no instruction cache */
machine_cache_info->ic_count = 0;
machine_cache_info->ic_loop = 0;
machine_cache_info->ic_base = 0;
machine_cache_info->ic_stride = 0;
machine_cache_info->dc_size = 0; /* no data cache */
machine_cache_info->dc_count = 0;
machine_cache_info->dc_loop = 0;
machine_cache_info->dc_base = 0;
machine_cache_info->dc_stride = 0;
#endif
memcpy(result, machine_cache_info, sizeof(*machine_cache_info));
return PDC_OK;
case PDC_CACHE_RET_SPID: /* returns space-ID bits */
memset(result, 0, 32 * sizeof(unsigned long));
/* for 32- and 64-bit CPUs, we allow only 32 useable bits for SIDs */
result[0] = 32;
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 || i >= smp_cpus); /* 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 << 7;
mtctl(mask, 10); /* initialize cr10 */
result[0] = mask;
result[1] = mask;
result[17] = 1; // Revision
result[18] = has_astro ? 0x0f : 0x13; // 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;
hppa_device_t *dev;
struct pdc_iodc *iodc_p;
unsigned char *c;
// dprintf(1, "\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;
dev = find_hpa_device(hpa);
// dprintf(1, "pdc_iodc found dev %p\n", dev);
if (!dev)
return -4; // not found
iodc_p = dev->iodc;
if (ARG4 == PDC_IODC_INDEX_DATA) {
if (iodc_p->type == 0x0041) // Memory ?
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[0] = 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 - for memory */
case PDC_IODC_DINIT: /* destructive init */
result[0] = 0; /* IO_STATUS */
result[1] = 0; /* max_spa */
result[2] = ram_size; /* max memory */
result[3] = 0;
return PDC_OK;
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] = *rtc_ptr;
result[1] = result[2] = result[3] = 0;
return PDC_OK;
case PDC_TOD_WRITE:
*rtc_ptr = 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)
{
int ret;
/* Block TLB is only supported on 32-bit CPUs */
if (cpu_bit_width != 32)
return PDC_BAD_PROC;
asm(
"ldw (7-0)*%2(%1),%%r26 ! ldw (7-1)*%2(%1),%%r25 ! ldw (7-2)*%2(%1),%%r24 ! ldw (7-3)*%2(%1),%%r23\n"
"ldw (7-4)*%2(%1),%%r22 ! ldw (7-5)*%2(%1),%%r21 ! ldw (7-6)*%2(%1),%%r20 ! ldw (7-7)*%2(%1),%%r19\n"
"ldi %3,%%ret0\n"
"diag 0x100\n"
"copy %%ret0,%0\n"
: "=r" (ret)
: "r" (arg), "i" (sizeof(long)), "i" (PDC_BAD_OPTION)
: "r28", "r26", "r25", "r24", "r23", "r22", "r21", "r20", "r19" );
return ret;
}
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;
hppa_device_t *dev;
unsigned long hpa;
unsigned long 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;
dev = find_hppa_device_by_index(hpa_index, 0);
if (!dev)
return PDC_NE_MOD; // Module not found
hpa = dev->hpa;
if (0) {
dprintf(1, "PDC_FIND_MODULE dev=%p hpa=%lx ", dev, dev ? dev->hpa:0UL);
print_mod_path(dev->mod_path);
if (dev->pci)
dprintf(1, "PCI %pP ", dev->pci);
dprintf(1, "\n");
}
memset(result, 0, 32*sizeof(long));
mod_path = (struct pdc_module_path *)ARG3;
if (mod_path)
*mod_path = *dev->mod_path;
// *pdc_mod_info = *parisc_devices[hpa_index].mod_info; -> can be dropped.
result[0] = dev->mod_info->mod_addr; // for PDC_IODC
result[1] = dev->mod_info->mod_pgs;
result[2] = dev->num_addr; // dev->mod_info->add_addr;
return PDC_OK;
case PDC_FIND_ADDRESS:
hpa_index = ARG3;
dev = find_hppa_device_by_index(hpa_index, 1);
if (!dev)
return PDC_NE_MOD; // Module not found
hpa = dev->hpa;
memset(result, 0, 32*sizeof(long));
ARG4 -= 1;
if (ARG4 >= dev->num_addr)
return PDC_INVALID_ARG;
result[0] = dev->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(0, mod_path, &hpa_index, 1); // XXX
if (0) {
dprintf(1, "PDC_TRANSLATE_PATH dev=%p hpa=%lx ", dev, dev ? dev->hpa:0UL);
print_mod_path(mod_path);
if (dev && dev->pci)
dprintf(1, "PCI %pP ", dev->pci);
dprintf(1, "\n");
}
if (!dev)
return PDC_NE_MOD;
result[0] = dev->mod_info->mod_addr; // for PDC_IODC
result[1] = dev->mod_info->mod_pgs;
result[2] = dev->num_addr; // dev->mod_info->add_addr;
result[3] = hpa_index;
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 pdc_module_path *dp = (struct pdc_module_path *) ARG3;
hppa_device_t *dev;
unsigned long index;
switch (option) {
case PDC_MEM_MAP_HPA:
// NEEDS FIXING !!
dprintf(0, "\nSeaBIOS: PDC_MEM_MAP_HPA bus = %d, mod = %d\n", dp->path.bc[4], dp->path.mod);
dev = find_hppa_device_by_path(memmap->hpa, (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;
}
#if 0
[ 3.212566] iosapic Interrupt Routing Table (cell 0)
[ 3.282568] iosapic start = 0x1302f980 num_entries 5 entry_size 16
[ 3.362568] iosapic 8b10000f30000002 fffffffffed30800 - 8b 10 00 0f 30 00 00 02 fffffffffed30800
[ 3.482568] iosapic 8b10000f34000003 fffffffffed30800 - 8b 10 00 0f 34 00 00 03 fffffffffed30800
[ 3.592568] iosapic 8b10000d3b000000 fffffffffed30800 - 8b 10 00 0d 3b 00 00 00 fffffffffed30800
[ 3.712568] iosapic 8b10000f3c000001 fffffffffed30800 - 8b 10 00 0f 3c 00 00 01 fffffffffed30800
[ 3.832568] iosapic 8b10000f3c000001 fffffffffed30800 - 8b 10 00 0f 3c 00 00 01 fffffffffed30800
printk(MODULE_NAME " %016llx %016llx - %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
p1[0], p1[1],
p->entry_type, p->entry_length, p->interrupt_type, p->polarity_trigger,
p->src_bus_irq_devno, p->src_bus_id, p->src_seg_id, p->dest_iosapic_intin,
((u32 *) p)[2],
((u32 *) p)[3]
#endif
#define IRT_TABLE_ENTRIES 24
#define IOSAPIC_HPA 0xfffffffffed30800ULL
#define ELROY_IRQS 8 /* IOSAPIC IRQs */
static int irt_table_entries;
static u32 irt_table[IRT_TABLE_ENTRIES * 16/sizeof(u32)];
static void iosapic_table_setup(void)
{
struct pci_device *pci;
u32 *p;
u8 slot = 0, iosapic_intin = 0, irq_devno, bus_id;
irt_table_entries = 0;
memset(irt_table, 0, sizeof(irt_table));
p = irt_table;
foreachpci(pci) {
// if (!pci->irq) continue;
BUG_ON(irt_table_entries >= IRT_TABLE_ENTRIES);
irt_table_entries++;
dprintf(5, "IRT ENTRY #%d: bdf %02x\n", irt_table_entries, pci->bdf);
/* write the 16 bytes */
/* 1: entry_type, entry_length, interrupt_type, polarity_trigger */
*p++ = 0x8b10000f; // oder 0x8b10000d
/* 2: src_bus_irq_devno, src_bus_id, src_seg_id, dest_iosapic_intin */
/* irq_devno = (slot << 2) | (intr_pin-1); */
irq_devno = (slot++ << 2) | (pci->irq - 1);
bus_id = 0;
*p++ = (irq_devno << 24) | (bus_id << 16) | (0 << 8) | (iosapic_intin << 0);
*p++ = IOSAPIC_HPA >> 32;
*p++ = (u32) IOSAPIC_HPA;
iosapic_intin++;
iosapic_intin &= (ELROY_IRQS - 1 );
}
}
static int pdc_pci_index(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
/* machines with Dino don't provide this info */
// 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));
// BUG_ON(1);
result[0] = 2; /* XXX physical hardware returns those ?!? */
return PDC_OK;
case PDC_PCI_GET_INT_TBL_SIZE:
if (!has_astro)
return PDC_BAD_OPTION;
result[0] = irt_table_entries;
return PDC_OK;
case PDC_PCI_GET_INT_TBL:
if (!has_astro)
return PDC_BAD_OPTION;
result[0] = irt_table_entries;
/* ARG4 is ptr to irt table */
memcpy((void *)ARG4, irt_table, irt_table_entries * 16);
return PDC_OK;
case PDC_PCI_PCI_PATH_TO_PCI_HPA:
BUG_ON(1);
result[0] = pci_hpa;
return PDC_OK;
case PDC_PCI_PCI_HPA_TO_PCI_PATH:
BUG_ON(1);
break;
}
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:
/* SCSI controller is on normal PCI bus on machines with Astro */
if (has_astro) return PDC_BAD_OPTION;
// 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"
" EXIT Exit QEMU emulation\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] == 'L' && input[1] == 'S') // HELP? (ls)
goto again;
if (input[0] == 'E' && input[1] == 'X') // EXIT
hlt();
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 struct pz_device mem_boot_boot = {
.dp.path.flags = PF_AUTOBOOT,
.hpa = DINO_SCSI_HPA, // will be overwritten
.iodc_io = (unsigned long) &iodc_entry,
.cl_class = CL_RANDOM,
};
#if 0
static void find_pci_slot_for_dev(unsigned int vendor, char *pci_slot)
{
struct pci_device *pci;
foreachpci(pci)
if (pci->vendor == vendor) {
*pci_slot = (pci->bdf >> 3) & 0x0f;
return;
}
}
#endif
/* find serial PCI card (to be used as console) */
static void find_serial_pci_card(void)
{
struct pci_device *pci;
hppa_device_t *pdev;
u32 pmem;
if (!has_astro) /* use built-in LASI serial port for console */
return;
pci = pci_find_class(PCI_CLASS_COMMUNICATION_SERIAL);
if (!pci)
return;
dprintf(1, "PCI: Enabling %pP for primary SERIAL PORT\n", pci);
pci_config_maskw(pci->bdf, PCI_COMMAND, 0,
PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
pmem = pci_enable_iobar(pci, PCI_BASE_ADDRESS_0);
dprintf(1, "PCI: Enabling %pP for primary SERIAL PORT mem %x\n", pci, pmem);
pmem += IOS_DIST_BASE_ADDR;
/* set serial port for console output and keyboard input */
pdev = &hppa_pci_devices[0];
while (pdev->pci != pci)
pdev++;
pdev->pci_addr = pmem;
mem_cons_boot.hpa = pdev->hpa; // HELGE
mem_cons_boot.dp = *pdev->mod_path;
dprintf(1,"SET hpa %lx ", pdev->hpa);
print_mod_path(&mem_cons_boot.dp);
dprintf(1," \n");
mem_kbd_boot.hpa = pdev->hpa;
mem_kbd_boot.dp = *pdev->mod_path;
}
/* find SCSI PCI card (to be used as boot device) */
static void find_scsi_pci_card(void)
{
struct pci_device *pci;
hppa_device_t *pdev;
u32 pmem;
// if (!has_astro) return;
pci = pci_find_class(PCI_CLASS_STORAGE_SCSI);
if (!pci)
return;
dprintf(1, "PCI: Enabling %pP for primary SCSI PORT\n", pci);
pmem = pci_enable_iobar(pci, PCI_BASE_ADDRESS_0);
dprintf(1, "PCI: Enabling %pP for primary SCSI PORT mem %x\n", pci, pmem);
pmem += IOS_DIST_BASE_ADDR;
/* set SCSI HPA */
pdev = &hppa_pci_devices[0];
while (pdev->pci != pci)
pdev++;
pdev->pci_addr = pmem;
mem_boot_boot.hpa = pdev->hpa;
mem_boot_boot.dp = *pdev->mod_path;
dprintf(1, "PCI: Enabling BOOT DEVICE HPA %x\n", mem_boot_boot.hpa);
}
/* Prepare boot paths in PAGE0 and stable memory */
static void prepare_boot_path(struct pz_device *dest,
const struct pz_device *source,
unsigned int stable_offset)
{
const struct pdc_module_path *mod_path;
// if (DEV_is_storage_device(dev)) mod_path = &mod_path_emulated_drives; else if (dev)
mod_path = &source->dp;
/* copy device path to entry in PAGE0 */
memcpy((void*)dest, source, sizeof(*source));
// memcpy((void*)&dest->dp, mod_path, sizeof(struct pdc_module_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 pdc_module_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);
// detect if we emulate a 32- or 64-bit CPU.
// set all bits in cr11, read back, and if the return
// value is 63 this is a 64-bit capable CPU.
// A 32-bit only CPU returns 31.
mtctl(-1UL, 11);
cpu_bit_width = (mfctl(11) == 63) ? 64 : 32;
// cpu_bit_width = 64; /* XXX HACK */
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();
// PlatformRunningOn = PF_QEMU; // emulate runningOnQEMU()
/* 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();
DebugOutputPort = romfile_loadint("/etc/hppa/DebugOutputPort", CPU_HPA + 24);
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();
}
/* which machine shall we emulate? */
str = romfile_loadfile("/etc/hppa/machine", NULL);
if (!str) {
str = "B160L";
current_machine = &machine_B160L;
pci_hpa = DINO_HPA;
hppa_port_pci_cmd = pci_hpa + DINO_PCI_ADDR;
hppa_port_pci_data = pci_hpa + DINO_CONFIG_DATA;
}
if (strcmp(str, "C3700") == 0) {
has_astro = 1;
current_machine = &machine_C3700;
pci_hpa = (unsigned long) ELROY0_BASE_HPA;
hppa_port_pci_cmd = pci_hpa + 0x040;
hppa_port_pci_data = pci_hpa + 0x048;
/* no serial port for now, will find later */
mem_cons_boot.hpa = 0;
mem_kbd_boot.hpa = 0;
}
parisc_devices = current_machine->device_list;
strtcpy(qemu_machine, str, sizeof(qemu_machine));
tlb_entries = romfile_loadint("/etc/cpu/tlb_entries", 256);
dprintf(0, "fw_cfg: TLB entries %d\n", tlb_entries);
powersw_ptr = (int *) (unsigned long)
romfile_loadint("/etc/hppa/power-button-addr", (unsigned long)&powersw_nop);
/* real-time-clock addr */
rtc_ptr = (int *) (unsigned long)
romfile_loadint("/etc/hppa/rtc-addr", (unsigned long) LASI_RTC_HPA);
// dprintf(0, "RTC PTR 0x%x\n", (int)rtc_ptr);
/* 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);
current_machine->pdc_model.sw_id = romfile_loadstring_to_int("opt/hostid",
current_machine->pdc_model.sw_id);
dprintf(0, "fw_cfg: machine hostid %lu\n", current_machine->pdc_model.sw_id);
str = romfile_loadfile("/etc/qemu-version", NULL);
if (str)
strtcpy(qemu_version, str, sizeof(qemu_version));
/* 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 - is set later (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;
if (has_astro)
kbd_init();
else
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 device list */
keep_add_generic_devices();
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;
cpu_hz = 100 * PAGE0->mem_10msec; /* Hz of this PARISC */
dprintf(1, "\nPARISC SeaBIOS Firmware, %d x %d-bit PA-RISC CPU at %d.%06d MHz, %d MB RAM.\n",
smp_cpus, cpu_bit_width, 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();
if (has_astro) {
iosapic_table_setup();
}
hppa_pci_build_devices_list();
/* find serial PCI card when running on Astro */
find_serial_pci_card();
serial_setup();
// ohci_setup();
block_setup();
/* find SCSI PCI card when running on Astro or Dino */
find_scsi_pci_card();
// Initialize boot paths (graphics & keyboard)
if (pdc_console != CONSOLE_SERIAL) {
if (artist_present())
pdc_console = CONSOLE_GRAPHICS;
else
pdc_console = CONSOLE_SERIAL;
}
dprintf(1, "WECHSEL 111\n");
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);
}
dprintf(1, "WECHSEL 222\n");
PAGE0->vec_rendz = 0; /* No rendezvous yet. Add MEM_RENDEZ_HI later */
printf("\n");
printf("SeaBIOS PA-RISC Firmware Version " SEABIOS_HPPA_VERSION_STR
" (QEMU %s)\n\n"
"Duplex Console IO Dependent Code (IODC) revision 1\n"
"\n", qemu_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(" Emulated machine: HP %s (%d-bit %s)\n"
" Available memory: %u MB\n"
" Good memory required: %d MB\n\n",
qemu_machine, cpu_bit_width, (cpu_bit_width == 64) ? "PA2.0" : "PA1.1",
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);
#if 0
HELGE HPA f4008000
48 86 a9 54 2f 94 fe 03 c0 a8 14 33 c0 a8 14 42 f4 00 80 00 00 00 00 00 00 01 90 00 00 00 10 02
struct pz_device {
// struct pdc_module_path path; /* see above */
unsigned char flags; /* flags see above! */ 00
unsigned char bc[6]; /* bus converter routing info */ ff ff ff 0a 00 0c
unsigned char mod; 00
unsigned int layers[6];/* device-specific layer-info */ 00 01 00 00 d0 29 10 bf
/* struct iomod *hpa; */
unsigned int hpa; /* HPA base address */
/* char *spa; */
unsigned int spa; /* SPA base address */
/* int (*iodc_io)(struct iomod*, ...); */
unsigned int iodc_io; /* device entry point */
short pad; /* reserved */
unsigned short cl_class;/* see below */
} __attribute__((aligned(8))) ;
#define CL_NULL 0 /* invalid */
#define CL_RANDOM 1 /* random access (as disk) */
#define CL_SEQU 2 /* sequential access (as tape) */
#define CL_DUPLEX 7 /* full-duplex point-to-point (RS-232, Net) */
#define CL_KEYBD 8 /* half-duplex console (HIL Keyboard) */
#define CL_DISPL 9 /* half-duplex console (display) */
#define CL_FC 10 /* FiberChannel access media */
#endif
// 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 */
}