| // 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: |
| // ./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 |
| |
| #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); |
| |
| 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. |
| ********************************************************/ |
| |
| /* 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; |
| #define PCI_HPA DINO_HPA /* initial temp. PCI bus */ |
| unsigned long pci_hpa = PCI_HPA; /* HPA of Dino or Elroy0 */ |
| unsigned long hppa_port_pci_cmd = (PCI_HPA + DINO_PCI_ADDR); |
| unsigned long hppa_port_pci_data = (PCI_HPA + DINO_CONFIG_DATA); |
| |
| /* 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(SCSI_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; |
| dprintf(1, "PCI device #%d %pP bdf 0x%x at pfa 0x%lx\n", curr_pci_devices, pci, pci->bdf, pfa); |
| |
| 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); |
| |
| 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; |
| int ret, len; |
| char *c; |
| struct disk_op_s disk_op; |
| |
| dev = find_hpa_device(hpa); |
| 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__); |
| 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 |
| */ |
| memcpy(result, (cpu_bit_width == 64) ? |
| ¤t_machine->pdc_model : &machine_B160L.pdc_model, |
| sizeof(current_machine->pdc_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] = 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 |
| Interrupt Routing Table (cell 0) |
| 8b10000f30000002 fffffffffed30800 - 8b 10 00 0f 30 00 00 02 fffffffffed30800 |
| 8b10000f34000003 fffffffffed30800 - 8b 10 00 0f 34 00 00 03 fffffffffed30800 |
| 8b10000d3b000000 fffffffffed30800 - 8b 10 00 0d 3b 00 00 00 fffffffffed30800 |
| 8b10000f3c000001 fffffffffed30800 - 8b 10 00 0f 3c 00 00 01 fffffffffed30800 |
| 8b10000f3c000001 fffffffffed30800 - 8b 10 00 0f 3c 00 00 01 fffffffffed30800 |
| #endif |
| #define MAX_IRT_TABLE_ENTRIES 24 |
| #define IOSAPIC_HPA 0xfffffffffed30800ULL |
| #define ELROY_IRQS 8 /* IOSAPIC IRQs */ |
| static int irt_table_entries; |
| static u32 irt_table[MAX_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 >= MAX_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; |
| mem_kbd_boot.hpa = pdev->hpa; |
| } |
| |
| /* 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; |
| 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(volatile struct pz_device *dest, |
| const struct pz_device *source, |
| unsigned int stable_offset) |
| { |
| hppa_device_t *dev; |
| unsigned long hpa; |
| struct pdc_module_path *mod_path; |
| |
| hpa = source->hpa; |
| dev = find_hpa_device(hpa); |
| BUG_ON(!dev); |
| |
| if (DEV_is_storage_device(dev)) |
| mod_path = &mod_path_emulated_drives; |
| else if (dev) |
| mod_path = dev->mod_path; |
| else { |
| BUG_ON(1); |
| } |
| |
| /* 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; |
| } |
| 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); |
| } |
| |
| 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); |
| |
| // 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 */ |
| } |