diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/seabios/src | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/seabios/src')
152 files changed, 39575 insertions, 0 deletions
diff --git a/qemu/roms/seabios/src/Kconfig b/qemu/roms/seabios/src/Kconfig new file mode 100644 index 000000000..45ca59cf3 --- /dev/null +++ b/qemu/roms/seabios/src/Kconfig @@ -0,0 +1,516 @@ +# Kconfig SeaBIOS configuration + +mainmenu "SeaBIOS Configuration" + +menu "General Features" + +choice + prompt "Build Target" + default QEMU + + config COREBOOT + bool "Build for coreboot" + help + Configure as a coreboot payload. + + config QEMU + bool "Build for QEMU/Xen/KVM/Bochs" + select QEMU_HARDWARE + help + Configure for an emulated machine (QEMU, Xen, KVM, or Bochs). + + config CSM + bool "Build as Compatibilty Support Module for EFI BIOS" + help + Configure to be used by EFI firmware as Compatibility Support + module (CSM) to provide legacy BIOS services. + +endchoice + + config QEMU_HARDWARE + bool "Support hardware found on emulators (QEMU/Xen/KVM/Bochs)" if !QEMU + default n + help + Support virtual hardware when the code detects it is + running on an emulator. + + config XEN + depends on QEMU + bool "Support Xen HVM" + default y + help + Configure to be used by xen hvmloader, for a HVM guest. + + config THREADS + bool "Parallelize hardware init" + default y + help + Support running hardware initialization in parallel. + + config RELOCATE_INIT + bool "Copy init code to high memory" + default y + help + Support relocating the one time initialization code to high memory. + + config BOOTMENU + depends on BOOT + bool "Bootmenu" + default y + help + Support an interactive boot menu at end of post. + config BOOTSPLASH + depends on BOOTMENU + bool "Graphical boot splash screen" + default y + help + Support showing a graphical boot splash screen. + config BOOTORDER + depends on BOOT + bool "Boot ordering" + default y + help + Support controlling of the boot order via the fw_cfg/CBFS + "bootorder" file. + + config COREBOOT_FLASH + depends on COREBOOT + bool "coreboot CBFS support" + default y + help + Support searching coreboot flash format. + config LZMA + depends on COREBOOT_FLASH + bool "CBFS lzma support" + default y + help + Support CBFS files compressed using the lzma decompression + algorithm. + config CBFS_LOCATION + depends on COREBOOT_FLASH + hex "CBFS memory end location" + default 0 + help + Memory address of where the CBFS data ends. This should + be zero for normal builds. It may be a non-zero value if + the CBFS filesystem is at a non-standard location (eg, + 0xffe00000 if CBFS ends 2Meg below the end of flash). + + config FLASH_FLOPPY + depends on COREBOOT_FLASH + bool "Floppy images in CBFS" + default y + help + Support floppy images in coreboot flash. + config ENTRY_EXTRASTACK + bool "Use internal stack for 16bit interrupt entry points" + default y + help + Utilize an internal stack for all the legacy 16bit + interrupt entry points. This reduces the amount of space + on the caller's stack that SeaBIOS uses. This may + adversely impact any legacy operating systems that call + the BIOS in 16bit protected mode. + + config MALLOC_UPPERMEMORY + bool "Allocate memory that needs to be in first Meg above 0xc0000" + default y + help + Use the "Upper Memory Block" area (0xc0000-0xf0000) for + internal "low memory" allocations. If this is not + selected, the memory is instead allocated from the + "9-segment" (0x90000-0xa0000). + + config ROM_SIZE + int "ROM size (in KB)" + default 0 + help + Set the ROM size. Say '0' here to make seabios figure the + needed size automatically. + + Currently SeaBIOS will easily fit into 256 KB. To make it fit + it into 128 KB (which was big enouth for a long time) you'll + probably have to disable some featues such as xhci support. + +endmenu + +menu "Hardware support" + config ATA + depends on DRIVES + bool "ATA controllers" + default y + help + Support for IDE disk code. + config ATA_DMA + depends on ATA + bool "ATA DMA" + default n + help + Detect and try to use ATA bus mastering DMA controllers. + config ATA_PIO32 + depends on ATA + bool "ATA 32bit PIO" + default n + help + Use 32bit PIO accesses on ATA (minor optimization on PCI transfers). + config AHCI + depends on DRIVES + bool "AHCI controllers" + default y + help + Support for AHCI disk code. + config SDCARD + depends on DRIVES && QEMU_HARDWARE + bool "SD controllers" + default y + help + Support for SD cards on PCI host controllers. + config VIRTIO_BLK + depends on DRIVES && QEMU_HARDWARE + bool "virtio-blk controllers" + default y + help + Support boot from virtio-blk storage. + config VIRTIO_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "virtio-scsi controllers" + default y + help + Support boot from virtio-scsi storage. + config PVSCSI + depends on DRIVES && QEMU_HARDWARE + bool "PVSCSI controllers" + default y + help + Support boot from Paravirtualized SCSI storage. This kind of storage + is mainly supported by VMware ESX hypervisor. It is commonly used + to allow fast storage access by communicating directly with the + underlying hypervisor. Enabling this type of boot will allow + booting directly from images imported from an ESX platform, + without the need to use slower emulation of storage controllers + such as IDE. + config ESP_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "AMD PCscsi controllers" + default y + help + Support boot from AMD PCscsi storage. + config LSI_SCSI + depends on DRIVES && QEMU_HARDWARE + bool "lsi53c895a scsi controllers" + default y + help + Support boot from qemu-emulated lsi53c895a scsi storage. + config MEGASAS + depends on DRIVES + bool "LSI MegaRAID SAS controllers" + default y + help + Support boot from LSI MegaRAID SAS scsi storage. + config FLOPPY + depends on DRIVES + bool "Floppy controller" + default y + help + Support floppy drive access. + + config PS2PORT + depends on KEYBOARD || MOUSE + bool "PS/2 port" + default y + help + Support PS2 ports (keyboard and mouse). + + config USB + bool "USB" + default y + help + Support USB devices. + config USB_UHCI + depends on USB + bool "USB UHCI controllers" + default y + help + Support USB UHCI controllers. + config USB_OHCI + depends on USB + bool "USB OHCI controllers" + default y + help + Support USB OHCI controllers. + config USB_EHCI + depends on USB + bool "USB EHCI controllers" + default y + help + Support USB EHCI controllers. + config USB_XHCI + depends on USB + bool "USB XHCI controllers" + default y + help + Support USB XHCI controllers. + config USB_MSC + depends on USB && DRIVES + bool "USB drives" + default y + help + Support USB BOT (bulk-only transport) disks. + config USB_UAS + depends on USB && DRIVES + bool "UAS drives" + default y + help + Support USB UAS (usb attached scsi) disks. + config USB_HUB + depends on USB + bool "USB hubs" + default y + help + Support USB hubs. + config USB_KEYBOARD + depends on USB && KEYBOARD + bool "USB keyboards" + default y + help + Support USB keyboards. + config USB_MOUSE + depends on USB && MOUSE + bool "USB mice" + default y + help + Support USB mice. + + config SERIAL + bool "Serial port" + default y + help + Support serial ports. This also enables int 14 serial port calls. + config LPT + bool "Parallel port" + default y + help + Support parallel ports. This also enables int 17 parallel port calls. + + config USE_SMM + depends on QEMU + bool "System Management Mode (SMM)" + default y + help + Support System Management Mode (on emulators). + config CALL32_SMM + bool + depends on USE_SMM + default y + config MTRR_INIT + depends on QEMU + bool "Initialize MTRRs" + default y + help + Initialize the Memory Type Range Registers (on emulators). + config PMTIMER + bool "Use ACPI timer" + default y + help + Use the ACPI timer instead of the TSC for timekeeping (on qemu). +endmenu + +menu "BIOS interfaces" + config DRIVES + bool "Drive interface" + default y + help + Support int13 disk/floppy drive functions. + + config CDROM_BOOT + depends on DRIVES + bool "DVD/CDROM booting" + default y + help + Support for booting from a CD. (El Torito spec support.) + config CDROM_EMU + depends on CDROM_BOOT + bool "DVD/CDROM boot drive emulation" + default y + help + Support bootable CDROMs that emulate a floppy/harddrive. + + config PCIBIOS + bool "PCIBIOS interface" + default y + help + Support int 1a/b1 PCI BIOS calls. + config APMBIOS + bool "APM interface" + default y + help + Support int 15/53 APM BIOS calls. + config PNPBIOS + bool "PnP BIOS interface" + default y + help + Support PnP BIOS entry point. + config OPTIONROMS + bool "Option ROMS" + default y + help + Support finding and running option roms during POST. + config OPTIONROMS_DEPLOYED + depends on OPTIONROMS && QEMU + bool "Option roms are already at 0xc0000-0xf0000" + default n + help + Select this if option ROMs are already copied to + 0xc0000-0xf0000. This must only be selected when using + Bochs or QEMU versions older than 0.12. + config PMM + depends on OPTIONROMS + bool "PMM interface" + default y + help + Support Post Memory Manager (PMM) entry point. + config BOOT + bool "Boot interface" + default y + help + Support int 19/18 system bootup support. + config KEYBOARD + bool "Keyboard interface" + default y + help + Support int 16 keyboard calls. + config KBD_CALL_INT15_4F + depends on KEYBOARD + bool "Keyboard hook interface" + default y + help + Support calling int155f on each keyboard event. + config MOUSE + bool "Mouse interface" + default y + help + Support for int15c2 mouse calls. + + config S3_RESUME + bool "S3 resume" + default y + help + Support S3 resume handler. + + config VGAHOOKS + bool "Hardware specific VGA helpers" + default y + help + Support int 155f BIOS callbacks specific to some Intel and + VIA on-board vga devices. + + config DISABLE_A20 + bool "Disable A20" + default n + help + Disable A20 on 16bit boot. + + config WRITABLE_UPPERMEMORY + depends on QEMU + bool "Make unused UMB memory read/writeable." + default n + help + When selected, the "Upper Memory Block" area + (0x90000-0xa0000) that is not used for option roms will be + made writable. This allows the ram to be directly + modified by programs. However, some old DOS high memory + managers may require the UMB region to be read-only. + +endmenu + +menu "BIOS Tables" + depends on QEMU + config PIRTABLE + bool "PIR table" + default y + help + Support generation of a PIR table in 0xf000 segment. + config MPTABLE + bool "MPTable" + default y + help + Support generation of MPTable. + config SMBIOS + bool "SMBIOS" + default y + help + Support generation of SM BIOS tables. This is also + sometimes called DMI. + config ACPI + bool "ACPI" + default y + help + Support generation of ACPI tables. + config ACPI_DSDT + bool "Include default ACPI DSDT" + default y + depends on ACPI + help + Include default DSDT ACPI table in BIOS. + Required for QEMU 1.3 and older. + This option can be disabled for QEMU 1.4 and newer + to save some space in the ROM file. + If unsure, say Y. + config FW_ROMFILE_LOAD + bool "Load BIOS tables from ROM files" + depends on QEMU_HARDWARE + default y + help + Support loading BIOS firmware tables from ROM files. + At the moment, only ACPI tables can be loaded in this way. + Required for QEMU 1.7 and newer. + This option can be disabled for QEMU 1.6 and older + to save some space in the ROM file. + If unsure, say Y. +endmenu + +source vgasrc/Kconfig + +menu "Debugging" + config DEBUG_LEVEL + int "Debug level" + default 1 + help + Control how verbose debug output is. The higher the + number, the more verbose SeaBIOS will be. + + Set to zero to disable debugging. + + config DEBUG_SERIAL + depends on DEBUG_LEVEL != 0 + bool "Serial port debugging" + default n + help + Send debugging information to serial port. + config DEBUG_SERIAL_PORT + depends on DEBUG_SERIAL + hex "Serial port base address" + default 0x3f8 + help + Base port for serial - generally 0x3f8, 0x2f8, 0x3e8, or 0x2e8. + + config DEBUG_IO + depends on QEMU_HARDWARE && DEBUG_LEVEL != 0 + bool "Special IO port debugging" + default y + help + Some emulators or hypervisors provide with a way to output debug + information by outputing strings in a special port present in the + IO space. + + config DEBUG_COREBOOT + depends on COREBOOT && DEBUG_LEVEL != 0 + bool "coreboot cbmem debug logging" + default y + help + Send debugging information to the coreboot cbmem console buffer. + Needs CONFIG_CONSOLE_CBMEM in coreboot. You can read the log + after boot using 'cbmem -c'. Only 32bit code (basically every- + thing before booting the OS) writes to the log buffer. + +endmenu diff --git a/qemu/roms/seabios/src/apm.c b/qemu/roms/seabios/src/apm.c new file mode 100644 index 000000000..f7c2306c3 --- /dev/null +++ b/qemu/roms/seabios/src/apm.c @@ -0,0 +1,215 @@ +// Basic support for apmbios callbacks. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2005 Struan Bartlett +// Copyright (C) 2004 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "stacks.h" // yield_toirq +#include "util.h" // apm_shutdown +#include "x86.h" // outb + +// APM installation check +static void +handle_155300(struct bregs *regs) +{ + regs->ah = 1; // APM major version + regs->al = 2; // APM minor version + regs->bh = 'P'; + regs->bl = 'M'; + // bit 0 : 16 bit interface supported + // bit 1 : 32 bit interface supported + regs->cx = 0x03; + set_success(regs); +} + +// APM real mode interface connect +static void +handle_155301(struct bregs *regs) +{ + set_success(regs); +} + +// APM 16 bit protected mode interface connect +static void +handle_155302(struct bregs *regs) +{ + extern void entry_apm16(void); + regs->bx = (u32)entry_apm16; + regs->ax = SEG_BIOS; // 16 bit code segment base + regs->si = 0xfff0; // 16 bit code segment size + regs->cx = SEG_BIOS; // data segment address + regs->di = 0xfff0; // data segment length + set_success(regs); +} + +// APM 32 bit protected mode interface connect +static void +handle_155303(struct bregs *regs) +{ + extern void entry_apm32(void); + regs->ax = SEG_BIOS; // 32 bit code segment base + regs->ebx = (u32)entry_apm32; + regs->cx = SEG_BIOS; // 16 bit code segment base + // 32 bit code segment size (low 16 bits) + // 16 bit code segment size (high 16 bits) + regs->esi = 0xfff0fff0; + regs->dx = SEG_BIOS; // data segment address + regs->di = 0xfff0; // data segment length + set_success(regs); +} + +// APM interface disconnect +static void +handle_155304(struct bregs *regs) +{ + set_success(regs); +} + +// APM cpu idle +static void +handle_155305(struct bregs *regs) +{ + yield_toirq(); + set_success(regs); +} + +// APM cpu busy +static void +handle_155306(struct bregs *regs) +{ + set_success(regs); +} + +void +apm_shutdown(void) +{ + u16 pm1a_cnt = GET_GLOBAL(acpi_pm1a_cnt); + if (pm1a_cnt) + outw(0x2000, pm1a_cnt); + + irq_disable(); + for (;;) + hlt(); +} + +// APM Set Power State +static void +handle_155307(struct bregs *regs) +{ + if (regs->bx != 1) { + set_success(regs); + return; + } + switch (regs->cx) { + case 1: + dprintf(1, "APM standby request\n"); + break; + case 2: + dprintf(1, "APM suspend request\n"); + break; + case 3: + apm_shutdown(); + break; + } + set_success(regs); +} + +static void +handle_155308(struct bregs *regs) +{ + set_success(regs); +} + +// Get Power Status +static void +handle_15530a(struct bregs *regs) +{ + regs->bh = 0x01; // on line + regs->bl = 0xff; // unknown battery status + regs->ch = 0x80; // no system battery + regs->cl = 0xff; // unknown remaining time + regs->dx = 0xffff; // unknown remaining time + regs->si = 0x00; // zero battery + set_success(regs); +} + +#define RET_ENOEVENT 0x80 + +// Get PM Event +static void +handle_15530b(struct bregs *regs) +{ + set_code_invalid_silent(regs, RET_ENOEVENT); +} + +// APM Driver Version +static void +handle_15530e(struct bregs *regs) +{ + regs->ah = 1; + regs->al = 2; + set_success(regs); +} + +// APM Engage / Disengage +static void +handle_15530f(struct bregs *regs) +{ + set_success(regs); +} + +// APM Get Capabilities +static void +handle_155310(struct bregs *regs) +{ + regs->bl = 0; + regs->cx = 0; + set_success(regs); +} + +static void +handle_1553XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +void +handle_1553(struct bregs *regs) +{ + if (! CONFIG_APMBIOS) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + //debug_stub(regs); + switch (regs->al) { + case 0x00: handle_155300(regs); break; + case 0x01: handle_155301(regs); break; + case 0x02: handle_155302(regs); break; + case 0x03: handle_155303(regs); break; + case 0x04: handle_155304(regs); break; + case 0x05: handle_155305(regs); break; + case 0x06: handle_155306(regs); break; + case 0x07: handle_155307(regs); break; + case 0x08: handle_155308(regs); break; + case 0x0a: handle_15530a(regs); break; + case 0x0b: handle_15530b(regs); break; + case 0x0e: handle_15530e(regs); break; + case 0x0f: handle_15530f(regs); break; + case 0x10: handle_155310(regs); break; + default: handle_1553XX(regs); break; + } +} + +void VISIBLE16 VISIBLE32SEG +handle_apm(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_apm); + handle_1553(regs); +} diff --git a/qemu/roms/seabios/src/asm-offsets.c b/qemu/roms/seabios/src/asm-offsets.c new file mode 100644 index 000000000..b98f3b5a8 --- /dev/null +++ b/qemu/roms/seabios/src/asm-offsets.c @@ -0,0 +1,23 @@ +// Generate assembler offsets. + +#include "gen-defs.h" // OFFSET +#include "bregs.h" // struct bregs + +/* workaround for a warning with -Wmissing-prototypes */ +void foo(void) VISIBLE16; + +void foo(void) +{ + COMMENT("BREGS"); + OFFSET(BREGS_es, bregs, es); + OFFSET(BREGS_ds, bregs, ds); + OFFSET(BREGS_eax, bregs, eax); + OFFSET(BREGS_ebx, bregs, ebx); + OFFSET(BREGS_ecx, bregs, ecx); + OFFSET(BREGS_edx, bregs, edx); + OFFSET(BREGS_ebp, bregs, ebp); + OFFSET(BREGS_esi, bregs, esi); + OFFSET(BREGS_edi, bregs, edi); + OFFSET(BREGS_flags, bregs, flags); + OFFSET(BREGS_code, bregs, code); +} diff --git a/qemu/roms/seabios/src/biosvar.h b/qemu/roms/seabios/src/biosvar.h new file mode 100644 index 000000000..58bcbcedb --- /dev/null +++ b/qemu/roms/seabios/src/biosvar.h @@ -0,0 +1,130 @@ +// Memory access to BIOS variables. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __BIOSVAR_H +#define __BIOSVAR_H + +#include "config.h" // SEG_BDA +#include "farptr.h" // GET_FARVAR +#include "std/bda.h" // struct bios_data_area_s + + +/**************************************************************** + * Interupt vector table + ****************************************************************/ + +#define GET_IVT(vector) \ + GET_FARVAR(SEG_IVT, ((struct rmode_IVT *)0)->ivec[vector]) +#define SET_IVT(vector, segoff) \ + SET_FARVAR(SEG_IVT, ((struct rmode_IVT *)0)->ivec[vector], segoff) + +#define FUNC16(func) ({ \ + ASSERT32FLAT(); \ + extern void func (void); \ + SEGOFF(SEG_BIOS, (u32)func - BUILD_BIOS_ADDR); \ + }) + + +/**************************************************************** + * Bios Data Area (BDA) + ****************************************************************/ + +// Accessor functions +#define GET_BDA(var) \ + GET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var) +#define SET_BDA(var, val) \ + SET_FARVAR(SEG_BDA, ((struct bios_data_area_s *)0)->var, (val)) + +// Helper function to set the bits of the equipment_list_flags variable. +static inline void set_equipment_flags(u16 clear, u16 set) { + u16 eqf = GET_BDA(equipment_list_flags); + SET_BDA(equipment_list_flags, (eqf & ~clear) | set); +} + + +/**************************************************************** + * Extended Bios Data Area (EBDA) + ****************************************************************/ + +// The initial size and location of EBDA +#define EBDA_SIZE_START \ + DIV_ROUND_UP(sizeof(struct extended_bios_data_area_s), 1024) +#define EBDA_SEGMENT_START \ + FLATPTR_TO_SEG(BUILD_LOWRAM_END - EBDA_SIZE_START*1024) + +// Accessor functions +static inline u16 get_ebda_seg(void) { + return GET_BDA(ebda_seg); +} +static inline struct extended_bios_data_area_s * +get_ebda_ptr(void) +{ + ASSERT32FLAT(); + return MAKE_FLATPTR(get_ebda_seg(), 0); +} +#define GET_EBDA(eseg, var) \ + GET_FARVAR(eseg, ((struct extended_bios_data_area_s *)0)->var) +#define SET_EBDA(eseg, var, val) \ + SET_FARVAR(eseg, ((struct extended_bios_data_area_s *)0)->var, (val)) + + +/**************************************************************** + * Global variables + ****************************************************************/ + +#if MODE16 == 0 && MODESEGMENT == 1 +// In 32bit segmented mode %cs may not be readable and the code may be +// relocated. The entry code sets up %gs with a readable segment and +// the code offset can be determined by get_global_offset(). +#define GLOBAL_SEGREG GS +static inline u32 __attribute_const get_global_offset(void) { + u32 ret; + asm(" calll 1f\n" + "1:popl %0\n" + " subl $1b, %0" + : "=r"(ret)); + return ret; +} +#else +#define GLOBAL_SEGREG CS +static inline u32 __attribute_const get_global_offset(void) { + return 0; +} +#endif +static inline u16 get_global_seg(void) { + return GET_SEG(GLOBAL_SEGREG); +} +#define GET_GLOBAL(var) \ + GET_VAR(GLOBAL_SEGREG, *(typeof(&(var)))((void*)&(var) \ + + get_global_offset())) +#if MODESEGMENT +#define GLOBALFLAT2GLOBAL(var) ((typeof(var))((void*)(var) - BUILD_BIOS_ADDR)) +#else +#define GLOBALFLAT2GLOBAL(var) (var) +#endif +// Access a "flat" pointer known to point to the f-segment. +#define GET_GLOBALFLAT(var) GET_GLOBAL(*GLOBALFLAT2GLOBAL(&(var))) + + +/**************************************************************** + * "Low" memory variables + ****************************************************************/ + +extern u8 _zonelow_seg, zonelow_base[]; +#define SEG_LOW ((u32)&_zonelow_seg) + +#if MODESEGMENT +#define GET_LOW(var) GET_FARVAR(SEG_LOW, (var)) +#define SET_LOW(var, val) SET_FARVAR(SEG_LOW, (var), (val)) +#define LOWFLAT2LOW(var) ((typeof(var))((void*)(var) - (u32)zonelow_base)) +#else +#define GET_LOW(var) (var) +#define SET_LOW(var, val) do { (var) = (val); } while (0) +#define LOWFLAT2LOW(var) (var) +#endif +#define GET_LOWFLAT(var) GET_LOW(*LOWFLAT2LOW(&(var))) +#define SET_LOWFLAT(var, val) SET_LOW(*LOWFLAT2LOW(&(var)), (val)) + +#endif // __BIOSVAR_H diff --git a/qemu/roms/seabios/src/block.c b/qemu/roms/seabios/src/block.c new file mode 100644 index 000000000..3f7ecb1d7 --- /dev/null +++ b/qemu/roms/seabios/src/block.c @@ -0,0 +1,582 @@ +// Disk setup and access +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "block.h" // process_op +#include "hw/ata.h" // process_ata_op +#include "hw/ahci.h" // process_ahci_op +#include "hw/blockcmd.h" // cdb_* +#include "hw/pci.h" // pci_bdf_to_bus +#include "hw/rtc.h" // rtc_read +#include "hw/virtio-blk.h" // process_virtio_blk_op +#include "malloc.h" // malloc_low +#include "output.h" // dprintf +#include "stacks.h" // stack_hop +#include "std/disk.h" // struct dpte_s +#include "string.h" // checksum +#include "util.h" // process_floppy_op + +u8 FloppyCount VARFSEG; +u8 CDCount; +struct drive_s *IDMap[3][BUILD_MAX_EXTDRIVE] VARFSEG; +u8 *bounce_buf_fl VARFSEG; + +struct drive_s * +getDrive(u8 exttype, u8 extdriveoffset) +{ + if (extdriveoffset >= ARRAY_SIZE(IDMap[0])) + return NULL; + return GET_GLOBAL(IDMap[exttype][extdriveoffset]); +} + +int getDriveId(u8 exttype, struct drive_s *drive) +{ + ASSERT32FLAT(); + int i; + for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++) + if (getDrive(exttype, i) == drive) + return i; + return -1; +} + +int create_bounce_buf(void) +{ + if (bounce_buf_fl) + return 0; + + u8 *buf = malloc_low(CDROM_SECTOR_SIZE); + if (!buf) { + warn_noalloc(); + return -1; + } + bounce_buf_fl = buf; + return 0; +} + +/**************************************************************** + * Disk geometry translation + ****************************************************************/ + +static u8 +get_translation(struct drive_s *drive) +{ + u8 type = drive->type; + if (CONFIG_QEMU && type == DTYPE_ATA) { + // Emulators pass in the translation info via nvram. + u8 ataid = drive->cntl_id; + u8 channel = ataid / 2; + u8 translation = rtc_read(CMOS_BIOS_DISKTRANSFLAG + channel/2); + translation >>= 2 * (ataid % 4); + translation &= 0x03; + return translation; + } + + // Otherwise use a heuristic to determine translation type. + u16 heads = drive->pchs.head; + u16 cylinders = drive->pchs.cylinder; + u16 spt = drive->pchs.sector; + u64 sectors = drive->sectors; + u64 psectors = (u64)heads * cylinders * spt; + if (!heads || !cylinders || !spt || psectors > sectors) + // pchs doesn't look valid - use LBA. + return TRANSLATION_LBA; + + if (cylinders <= 1024 && heads <= 16 && spt <= 63) + return TRANSLATION_NONE; + if (cylinders * heads <= 131072) + return TRANSLATION_LARGE; + return TRANSLATION_LBA; +} + +static void +setup_translation(struct drive_s *drive) +{ + u8 translation = get_translation(drive); + drive->translation = translation; + + u16 heads = drive->pchs.head ; + u16 cylinders = drive->pchs.cylinder; + u16 spt = drive->pchs.sector; + u64 sectors = drive->sectors; + const char *desc = NULL; + + switch (translation) { + default: + case TRANSLATION_NONE: + desc = "none"; + break; + case TRANSLATION_LBA: + desc = "lba"; + spt = 63; + if (sectors > 63*255*1024) { + heads = 255; + cylinders = 1024; + break; + } + u32 sect = (u32)sectors / 63; + heads = sect / 1024; + if (heads>128) + heads = 255; + else if (heads>64) + heads = 128; + else if (heads>32) + heads = 64; + else if (heads>16) + heads = 32; + else + heads = 16; + cylinders = sect / heads; + break; + case TRANSLATION_RECHS: + desc = "r-echs"; + // Take care not to overflow + if (heads==16) { + if (cylinders>61439) + cylinders=61439; + heads=15; + cylinders = (u16)((u32)(cylinders)*16/15); + } + // then go through the large bitshift process + case TRANSLATION_LARGE: + if (translation == TRANSLATION_LARGE) + desc = "large"; + while (cylinders > 1024) { + cylinders >>= 1; + heads <<= 1; + + // If we max out the head count + if (heads > 127) + break; + } + break; + } + // clip to 1024 cylinders in lchs + if (cylinders > 1024) + cylinders = 1024; + dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n" + , drive + , drive->pchs.cylinder, drive->pchs.head, drive->pchs.sector + , desc + , cylinders, heads, spt + , (u32)sectors); + + drive->lchs.head = heads; + drive->lchs.cylinder = cylinders; + drive->lchs.sector = spt; +} + + +/**************************************************************** + * Drive mapping + ****************************************************************/ + +// Fill in Fixed Disk Parameter Table (located in ebda). +static void +fill_fdpt(struct drive_s *drive, int hdid) +{ + if (hdid > 1) + return; + + u16 nlc = drive->lchs.cylinder; + u16 nlh = drive->lchs.head; + u16 nls = drive->lchs.sector; + + u16 npc = drive->pchs.cylinder; + u16 nph = drive->pchs.head; + u16 nps = drive->pchs.sector; + + struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid]; + fdpt->precompensation = 0xffff; + fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3); + fdpt->landing_zone = npc; + fdpt->cylinders = nlc; + fdpt->heads = nlh; + fdpt->sectors = nls; + + if (nlc != npc || nlh != nph || nls != nps) { + // Logical mapping present - use extended structure. + + // complies with Phoenix style Translated Fixed Disk Parameter + // Table (FDPT) + fdpt->phys_cylinders = npc; + fdpt->phys_heads = nph; + fdpt->phys_sectors = nps; + fdpt->a0h_signature = 0xa0; + + // Checksum structure. + fdpt->checksum -= checksum(fdpt, sizeof(*fdpt)); + } + + if (hdid == 0) + SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof( + struct extended_bios_data_area_s, fdpt[0]))); + else + SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof( + struct extended_bios_data_area_s, fdpt[1]))); +} + +// Find spot to add a drive +static void +add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive) +{ + if (*count >= ARRAY_SIZE(IDMap[0])) { + warn_noalloc(); + return; + } + idmap[*count] = drive; + *count = *count + 1; +} + +// Map a hard drive +void +map_hd_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + int hdid = bda->hdcount; + dprintf(3, "Mapping hd drive %p to %d\n", drive, hdid); + add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive); + + // Setup disk geometry translation. + setup_translation(drive); + + // Fill "fdpt" structure. + fill_fdpt(drive, hdid); +} + +// Map a cd +void +map_cd_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + dprintf(3, "Mapping cd drive %p\n", drive); + add_drive(IDMap[EXTTYPE_CD], &CDCount, drive); +} + +// Map a floppy +void +map_floppy_drive(struct drive_s *drive) +{ + ASSERT32FLAT(); + dprintf(3, "Mapping floppy drive %p\n", drive); + add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive); + + // Update equipment word bits for floppy + if (FloppyCount == 1) { + // 1 drive, ready for boot + set_equipment_flags(0x41, 0x01); + SET_BDA(floppy_harddisk_info, 0x07); + } else if (FloppyCount >= 2) { + // 2 drives, ready for boot + set_equipment_flags(0x41, 0x41); + SET_BDA(floppy_harddisk_info, 0x77); + } +} + + +/**************************************************************** + * Extended Disk Drive (EDD) get drive parameters + ****************************************************************/ + +static int +fill_generic_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf + , u32 dpte_so, char *iface_type + , int bdf, u8 channel, u16 iobase, u64 device_path) +{ + u16 size = GET_FARVAR(seg, param_far->size); + u16 t13 = size == 74; + + // Buffer is too small + if (size < 26) + return DISK_RET_EPARAM; + + // EDD 1.x + + u8 type = GET_GLOBALFLAT(drive_gf->type); + u16 npc = GET_GLOBALFLAT(drive_gf->pchs.cylinder); + u16 nph = GET_GLOBALFLAT(drive_gf->pchs.head); + u16 nps = GET_GLOBALFLAT(drive_gf->pchs.sector); + u64 lba = GET_GLOBALFLAT(drive_gf->sectors); + u16 blksize = GET_GLOBALFLAT(drive_gf->blksize); + + dprintf(DEBUG_HDL_13, "disk_1348 size=%d t=%d chs=%d,%d,%d lba=%d bs=%d\n" + , size, type, npc, nph, nps, (u32)lba, blksize); + + SET_FARVAR(seg, param_far->size, 26); + if (lba == (u64)-1) { + // 0x74 = removable, media change, lockable, max values + SET_FARVAR(seg, param_far->infos, 0x74); + SET_FARVAR(seg, param_far->cylinders, 0xffffffff); + SET_FARVAR(seg, param_far->heads, 0xffffffff); + SET_FARVAR(seg, param_far->spt, 0xffffffff); + } else { + if (lba > (u64)nps*nph*0x3fff) { + SET_FARVAR(seg, param_far->infos, 0x00); // geometry is invalid + SET_FARVAR(seg, param_far->cylinders, 0x3fff); + } else { + SET_FARVAR(seg, param_far->infos, 0x02); // geometry is valid + SET_FARVAR(seg, param_far->cylinders, (u32)npc); + } + SET_FARVAR(seg, param_far->heads, (u32)nph); + SET_FARVAR(seg, param_far->spt, (u32)nps); + } + SET_FARVAR(seg, param_far->sector_count, lba); + SET_FARVAR(seg, param_far->blksize, blksize); + + if (size < 30 || !dpte_so) + return DISK_RET_SUCCESS; + + // EDD 2.x + + SET_FARVAR(seg, param_far->size, 30); + SET_FARVAR(seg, param_far->dpte.segoff, dpte_so); + + if (size < 66 || !iface_type) + return DISK_RET_SUCCESS; + + // EDD 3.x + SET_FARVAR(seg, param_far->key, 0xbedd); + SET_FARVAR(seg, param_far->dpi_length, t13 ? 44 : 36); + SET_FARVAR(seg, param_far->reserved1, 0); + SET_FARVAR(seg, param_far->reserved2, 0); + + int i; + for (i=0; i<sizeof(param_far->iface_type); i++) + SET_FARVAR(seg, param_far->iface_type[i], GET_GLOBAL(iface_type[i])); + + if (bdf != -1) { + SET_FARVAR(seg, param_far->host_bus[0], 'P'); + SET_FARVAR(seg, param_far->host_bus[1], 'C'); + SET_FARVAR(seg, param_far->host_bus[2], 'I'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + u32 path = (pci_bdf_to_bus(bdf) | (pci_bdf_to_dev(bdf) << 8) + | (pci_bdf_to_fn(bdf) << 16)); + if (t13) + path |= channel << 24; + + SET_FARVAR(seg, param_far->iface_path, path); + } else { + // ISA + SET_FARVAR(seg, param_far->host_bus[0], 'I'); + SET_FARVAR(seg, param_far->host_bus[1], 'S'); + SET_FARVAR(seg, param_far->host_bus[2], 'A'); + SET_FARVAR(seg, param_far->host_bus[3], ' '); + + SET_FARVAR(seg, param_far->iface_path, iobase); + } + + if (t13) { + SET_FARVAR(seg, param_far->t13.device_path[0], device_path); + SET_FARVAR(seg, param_far->t13.device_path[1], 0); + + SET_FARVAR(seg, param_far->t13.checksum + , -checksum_far(seg, (void*)param_far+30, 43)); + } else { + SET_FARVAR(seg, param_far->phoenix.device_path, device_path); + + SET_FARVAR(seg, param_far->phoenix.checksum + , -checksum_far(seg, (void*)param_far+30, 35)); + } + + return DISK_RET_SUCCESS; +} + +struct dpte_s DefaultDPTE VARLOW; + +static int +fill_ata_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +{ + if (!CONFIG_ATA) + return DISK_RET_EPARAM; + + // Fill in dpte + struct atadrive_s *adrive_gf = container_of( + drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + u8 irq = GET_GLOBALFLAT(chan_gf->irq); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + int bdf = GET_GLOBALFLAT(chan_gf->pci_bdf); + u8 channel = GET_GLOBALFLAT(chan_gf->chanid); + + u16 options = 0; + if (GET_GLOBALFLAT(drive_gf->type) == DTYPE_ATA) { + u8 translation = GET_GLOBALFLAT(drive_gf->translation); + if (translation != TRANSLATION_NONE) { + options |= 1<<3; // CHS translation + if (translation == TRANSLATION_LBA) + options |= 1<<9; + if (translation == TRANSLATION_RECHS) + options |= 3<<9; + } + } else { + // ATAPI + options |= 1<<5; // removable device + options |= 1<<6; // atapi device + } + options |= 1<<4; // lba translation + if (CONFIG_ATA_PIO32) + options |= 1<<7; + + SET_LOW(DefaultDPTE.iobase1, iobase1); + SET_LOW(DefaultDPTE.iobase2, iobase2 + ATA_CB_DC); + SET_LOW(DefaultDPTE.prefix, ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) + | ATA_CB_DH_LBA)); + SET_LOW(DefaultDPTE.unused, 0xcb); + SET_LOW(DefaultDPTE.irq, irq); + SET_LOW(DefaultDPTE.blkcount, 1); + SET_LOW(DefaultDPTE.dma, 0); + SET_LOW(DefaultDPTE.pio, 0); + SET_LOW(DefaultDPTE.options, options); + SET_LOW(DefaultDPTE.reserved, 0); + SET_LOW(DefaultDPTE.revision, 0x11); + + u8 sum = checksum_far(SEG_LOW, &DefaultDPTE, 15); + SET_LOW(DefaultDPTE.checksum, -sum); + + return fill_generic_edd( + seg, param_far, drive_gf, SEGOFF(SEG_LOW, (u32)&DefaultDPTE).segoff + , "ATA ", bdf, channel, iobase1, slave); +} + +int noinline +fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf) +{ + switch (GET_GLOBALFLAT(drive_gf->type)) { + case DTYPE_ATA: + case DTYPE_ATA_ATAPI: + return fill_ata_edd(seg, param_far, drive_gf); + case DTYPE_VIRTIO_BLK: + case DTYPE_VIRTIO_SCSI: + return fill_generic_edd( + seg, param_far, drive_gf, 0xffffffff + , "SCSI ", GET_GLOBALFLAT(drive_gf->cntl_id), 0, 0, 0); + default: + return fill_generic_edd(seg, param_far, drive_gf, 0, NULL, 0, 0, 0, 0); + } +} + + +/**************************************************************** + * 16bit calling interface + ****************************************************************/ + +int VISIBLE32FLAT +process_atapi_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_WRITE: + case CMD_FORMAT: + return DISK_RET_EWRITEPROTECT; + default: + return scsi_process_op(op); + } +} + +// Execute a disk_op request. +int +process_op(struct disk_op_s *op) +{ + ASSERT16(); + int ret, origcount = op->count; + if (origcount * GET_GLOBALFLAT(op->drive_gf->blksize) > 64*1024) { + op->count = 0; + return DISK_RET_EBOUNDARY; + } + u8 type = GET_GLOBALFLAT(op->drive_gf->type); + switch (type) { + case DTYPE_FLOPPY: + ret = process_floppy_op(op); + break; + case DTYPE_ATA: + ret = process_ata_op(op); + break; + case DTYPE_RAMDISK: + ret = process_ramdisk_op(op); + break; + case DTYPE_CDEMU: + ret = process_cdemu_op(op); + break; + case DTYPE_VIRTIO_BLK: + ret = process_virtio_blk_op(op); + break; + case DTYPE_AHCI: ; + extern void _cfunc32flat_process_ahci_op(void); + ret = call32(_cfunc32flat_process_ahci_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_ATA_ATAPI: + ret = process_atapi_op(op); + break; + case DTYPE_AHCI_ATAPI: ; + extern void _cfunc32flat_process_atapi_op(void); + ret = call32(_cfunc32flat_process_atapi_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_SDCARD: ; + extern void _cfunc32flat_process_sdcard_op(void); + ret = call32(_cfunc32flat_process_sdcard_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + case DTYPE_USB: + case DTYPE_UAS: + case DTYPE_VIRTIO_SCSI: + case DTYPE_LSI_SCSI: + case DTYPE_ESP_SCSI: + case DTYPE_MEGASAS: + ret = scsi_process_op(op); + break; + case DTYPE_USB_32: + case DTYPE_UAS_32: + case DTYPE_PVSCSI: ; + extern void _cfunc32flat_scsi_process_op(void); + ret = call32(_cfunc32flat_scsi_process_op + , (u32)MAKE_FLATPTR(GET_SEG(SS), op), DISK_RET_EPARAM); + break; + default: + ret = DISK_RET_EPARAM; + break; + } + if (ret && op->count == origcount) + // If the count hasn't changed on error, assume no data transferred. + op->count = 0; + return ret; +} + +// Execute a "disk_op_s" request - this runs on the extra stack. +static int +__send_disk_op(struct disk_op_s *op_far, u16 op_seg) +{ + struct disk_op_s dop; + memcpy_far(GET_SEG(SS), &dop + , op_seg, op_far + , sizeof(dop)); + + dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n" + , dop.drive_gf, (u32)dop.lba, dop.buf_fl + , dop.count, dop.command); + + int status = process_op(&dop); + + // Update count with total sectors transferred. + SET_FARVAR(op_seg, op_far->count, dop.count); + + return status; +} + +// Execute a "disk_op_s" request by jumping to the extra 16bit stack. +int +send_disk_op(struct disk_op_s *op) +{ + ASSERT16(); + if (! CONFIG_DRIVES) + return -1; + + return stack_hop((u32)op, GET_SEG(SS), __send_disk_op); +} diff --git a/qemu/roms/seabios/src/block.h b/qemu/roms/seabios/src/block.h new file mode 100644 index 000000000..8182288d4 --- /dev/null +++ b/qemu/roms/seabios/src/block.h @@ -0,0 +1,109 @@ +#ifndef __BLOCK_H +#define __BLOCK_H + +#include "types.h" // u32 + + +/**************************************************************** + * Disk command request + ****************************************************************/ + +struct disk_op_s { + u64 lba; + void *buf_fl; + struct drive_s *drive_gf; + u16 count; + u8 command; +}; + +#define CMD_RESET 0x00 +#define CMD_READ 0x02 +#define CMD_WRITE 0x03 +#define CMD_VERIFY 0x04 +#define CMD_FORMAT 0x05 +#define CMD_SEEK 0x07 +#define CMD_ISREADY 0x10 + + +/**************************************************************** + * Global storage + ****************************************************************/ + +struct chs_s { + u16 head; + u16 cylinder; + u16 sector; + u16 pad; +}; + +struct drive_s { + u8 type; // Driver type (DTYPE_*) + u8 floppy_type; // Type of floppy (only for floppy drives). + struct chs_s lchs; // Logical CHS + u64 sectors; // Total sectors count + u32 cntl_id; // Unique id for a given driver type. + u8 removable; // Is media removable (currently unused) + + // Info for EDD calls + u8 translation; // type of translation + u16 blksize; // block size + struct chs_s pchs; // Physical CHS +}; + +#define DISK_SECTOR_SIZE 512 +#define CDROM_SECTOR_SIZE 2048 + +#define DTYPE_NONE 0x00 +#define DTYPE_FLOPPY 0x10 +#define DTYPE_ATA 0x20 +#define DTYPE_ATA_ATAPI 0x21 +#define DTYPE_RAMDISK 0x30 +#define DTYPE_CDEMU 0x40 +#define DTYPE_AHCI 0x50 +#define DTYPE_AHCI_ATAPI 0x51 +#define DTYPE_VIRTIO_SCSI 0x60 +#define DTYPE_VIRTIO_BLK 0x61 +#define DTYPE_USB 0x70 +#define DTYPE_USB_32 0x71 +#define DTYPE_UAS 0x72 +#define DTYPE_UAS_32 0x73 +#define DTYPE_LSI_SCSI 0x80 +#define DTYPE_ESP_SCSI 0x81 +#define DTYPE_MEGASAS 0x82 +#define DTYPE_PVSCSI 0x83 +#define DTYPE_SDCARD 0x90 + +#define MAXDESCSIZE 80 + +#define TRANSLATION_NONE 0 +#define TRANSLATION_LBA 1 +#define TRANSLATION_LARGE 2 +#define TRANSLATION_RECHS 3 + +#define EXTTYPE_FLOPPY 0 +#define EXTTYPE_HD 1 +#define EXTTYPE_CD 2 + +#define EXTSTART_HD 0x80 +#define EXTSTART_CD 0xE0 + + +/**************************************************************** + * Function defs + ****************************************************************/ + +// block.c +extern u8 FloppyCount, CDCount; +extern u8 *bounce_buf_fl; +struct drive_s *getDrive(u8 exttype, u8 extdriveoffset); +int getDriveId(u8 exttype, struct drive_s *drive); +void map_floppy_drive(struct drive_s *drive); +void map_hd_drive(struct drive_s *drive); +void map_cd_drive(struct drive_s *drive); +struct int13dpt_s; +int fill_edd(u16 seg, struct int13dpt_s *param_far, struct drive_s *drive_gf); +int process_op(struct disk_op_s *op); +int send_disk_op(struct disk_op_s *op); +int create_bounce_buf(void); + +#endif // block.h diff --git a/qemu/roms/seabios/src/bmp.c b/qemu/roms/seabios/src/bmp.c new file mode 100644 index 000000000..d8e76b789 --- /dev/null +++ b/qemu/roms/seabios/src/bmp.c @@ -0,0 +1,117 @@ +/* +* Basic BMP data process and Raw picture data handle functions. +* Could be used to adjust pixel data format, get infomation, etc. +* +* Copyright (C) 2011 Wayne Xia <xiawenc@cn.ibm.com> +* +* This work is licensed under the terms of the GNU LGPLv3. +*/ +#include "malloc.h" // malloc_tmphigh +#include "string.h" // memcpy +#include "util.h" // struct bmp_decdata + +struct bmp_decdata { + struct tagRGBQUAD *quadp; + unsigned char *datap; + int width; + int height; + int bpp; +}; + +#define bmp_load4byte(addr) (*(u32 *)(addr)) +#define bmp_load2byte(addr) (*(u16 *)(addr)) + +typedef struct tagBITMAPFILEHEADER { + u8 bfType[2]; + u8 bfSize[4]; + u8 bfReserved1[2]; + u8 bfReserved2[2]; + u8 bfOffBits[4]; +} BITMAPFILEHEADER, tagBITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER { + u8 biSize[4]; + u8 biWidth[4]; + u8 biHeight[4]; + u8 biPlanes[2]; + u8 biBitCount[2]; + u8 biCompression[4]; + u8 biSizeImage[4]; + u8 biXPelsPerMeter[4]; + u8 biYPelsPerMeter[4]; + u8 biClrUsed[4]; + u8 biClrImportant[4]; +} BITMAPINFOHEADER, tagBITMAPINFOHEADER; + +typedef struct tagRGBQUAD { + u8 rgbBlue; + u8 rgbGreen; + u8 rgbRed; + u8 rgbReserved; +} RGBQUAD, tagRGBQUAD; + +/* flat picture data adjusting function +* description: +* switch the vertical line sequence +* arrange horizontal pixel data, add extra space in the dest buffer +* for every line +*/ +static void raw_data_format_adjust_24bpp(u8 *src, u8 *dest, int width, + int height, int bytes_per_line_dest) +{ + int bytes_per_line_src = 3 * width; + int i; + for (i = 0 ; i < height ; i++) { + memcpy(dest + i * bytes_per_line_dest, + src + (height - 1 - i) * bytes_per_line_src, bytes_per_line_src); + } +} + +/* allocate decdata struct */ +struct bmp_decdata *bmp_alloc(void) +{ + struct bmp_decdata *bmp = malloc_tmphigh(sizeof(*bmp)); + return bmp; +} + +/* extract information from bmp file data */ +int bmp_decode(struct bmp_decdata *bmp, unsigned char *data, int data_size) +{ + if (data_size < 54) + return 1; + + u16 bmp_filehead = bmp_load2byte(data + 0); + if (bmp_filehead != 0x4d42) + return 2; + u32 bmp_recordsize = bmp_load4byte(data + 2); + if (bmp_recordsize != data_size) + return 3; + u32 bmp_dataoffset = bmp_load4byte(data + 10); + bmp->datap = (unsigned char *)data + bmp_dataoffset; + bmp->width = bmp_load4byte(data + 18); + bmp->height = bmp_load4byte(data + 22); + bmp->bpp = bmp_load2byte(data + 28); + return 0; +} + +/* get bmp properties */ +void bmp_get_size(struct bmp_decdata *bmp, int *width, int *height) +{ + *width = bmp->width; + *height = bmp->height; +} + +/* flush flat picture data to *pc */ +int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest) +{ + if (bmp->datap == pic) + return 0; + /* now only support 24bpp bmp file */ + if ((depth == 24) && (bmp->bpp == 24)) { + raw_data_format_adjust_24bpp(bmp->datap, pic, width, height, + bytes_per_line_dest); + return 0; + } + return 1; +} diff --git a/qemu/roms/seabios/src/boot.c b/qemu/roms/seabios/src/boot.c new file mode 100644 index 000000000..f23e9e154 --- /dev/null +++ b/qemu/roms/seabios/src/boot.c @@ -0,0 +1,756 @@ +// Code to load disk image and start system boot. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // qemu_cfg_show_boot_menu +#include "hw/pci.h" // pci_bdf_to_* +#include "hw/rtc.h" // rtc_read +#include "hw/usb.h" // struct usbdevice_s +#include "list.h" // hlist_node +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "std/disk.h" // struct mbr_s +#include "string.h" // memset +#include "util.h" // irqtimer_calc + + +/**************************************************************** + * Boot priority ordering + ****************************************************************/ + +static char **Bootorder VARVERIFY32INIT; +static int BootorderCount; + +static void +loadBootOrder(void) +{ + if (!CONFIG_BOOTORDER) + return; + + char *f = romfile_loadfile("bootorder", NULL); + if (!f) + return; + + int i = 0; + BootorderCount = 1; + while (f[i]) { + if (f[i] == '\n') + BootorderCount++; + i++; + } + Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*)); + if (!Bootorder) { + warn_noalloc(); + free(f); + BootorderCount = 0; + return; + } + + dprintf(1, "boot order:\n"); + i = 0; + do { + Bootorder[i] = f; + f = strchr(f, '\n'); + if (f) + *(f++) = '\0'; + Bootorder[i] = nullTrailingSpace(Bootorder[i]); + dprintf(1, "%d: %s\n", i+1, Bootorder[i]); + i++; + } while (f); +} + +// See if 'str' starts with 'glob' - if glob contains an '*' character +// it will match any number of characters in str that aren't a '/' or +// the next glob character. +static char * +glob_prefix(const char *glob, const char *str) +{ + for (;;) { + if (!*glob && (!*str || *str == '/')) + return (char*)str; + if (*glob == '*') { + if (!*str || *str == '/' || *str == glob[1]) + glob++; + else + str++; + continue; + } + if (*glob != *str) + return NULL; + glob++; + str++; + } +} + +// Search the bootorder list for the given glob pattern. +static int +find_prio(const char *glob) +{ + dprintf(1, "Searching bootorder for: %s\n", glob); + int i; + for (i = 0; i < BootorderCount; i++) + if (glob_prefix(glob, Bootorder[i])) + return i+1; + return -1; +} + +#define FW_PCI_DOMAIN "/pci@i0cf8" + +static char * +build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci) +{ + // Build the string path of a bdf - for example: /pci@i0cf8/isa@1,2 + char *p = buf; + if (pci->parent) { + p = build_pci_path(p, max, "pci-bridge", pci->parent); + } else { + if (pci->rootbus) + p += snprintf(p, max, "/pci-root@%x", pci->rootbus); + p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN); + } + + int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf); + p += snprintf(p, buf+max-p, "/%s@%x", devname, dev); + if (fn) + p += snprintf(p, buf+max-p, ",%x", fn); + return p; +} + +int bootprio_find_pci_device(struct pci_device *pci) +{ + if (CONFIG_CSM) + return csm_bootprio_pci(pci); + if (!CONFIG_BOOTORDER) + return -1; + // Find pci device - for example: /pci@i0cf8/ethernet@5 + char desc[256]; + build_pci_path(desc, sizeof(desc), "*", pci); + return find_prio(desc); +} + +int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun) +{ + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%x,%x", target, lun); + return find_prio(desc); +} + +int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave) +{ + if (CONFIG_CSM) + return csm_bootprio_ata(pci, chanid, slave); + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave); + return find_prio(desc); +} + +int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid) +{ + if (CONFIG_CSM) + return csm_bootprio_fdc(pci, port, fdid); + if (!CONFIG_BOOTORDER) + return -1; + if (!pci) + // support only pci machine for now + return -1; + // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "isa", pci); + snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid); + return find_prio(desc); +} + +int bootprio_find_pci_rom(struct pci_device *pci, int instance) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "*", pci); + if (instance) + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); + return find_prio(desc); +} + +int bootprio_find_named_rom(const char *name, int instance) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find named rom - for example: /rom@genroms/linuxboot.bin + char desc[256], *p; + p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name); + if (instance) + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); + return find_prio(desc); +} + +static char * +build_usb_path(char *buf, int max, struct usbhub_s *hub) +{ + if (!hub->usbdev) + // Root hub - nothing to add. + return buf; + char *p = build_usb_path(buf, max, hub->usbdev->hub); + p += snprintf(p, buf+max-p, "/hub@%x", hub->usbdev->port+1); + return p; +} + +int bootprio_find_usb(struct usbdevice_s *usbdev, int lun) +{ + if (!CONFIG_BOOTORDER) + return -1; + // Find usb - for example: /pci@i0cf8/usb@1,2/storage@1/channel@0/disk@0,0 + char desc[256], *p; + p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci); + p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub); + snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x" + , usbdev->port+1, lun); + int ret = find_prio(desc); + if (ret >= 0) + return ret; + // Try usb-host/redir - for example: /pci@i0cf8/usb@1,2/usb-host@1 + snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usbdev->port+1); + return find_prio(desc); +} + + +/**************************************************************** + * Boot setup + ****************************************************************/ + +static int BootRetryTime; +static int CheckFloppySig = 1; + +#define DEFAULT_PRIO 9999 + +static int DefaultFloppyPrio = 101; +static int DefaultCDPrio = 102; +static int DefaultHDPrio = 103; +static int DefaultBEVPrio = 104; + +void +boot_init(void) +{ + if (! CONFIG_BOOT) + return; + + if (CONFIG_QEMU) { + // On emulators, get boot order from nvram. + if (rtc_read(CMOS_BIOS_BOOTFLAG1) & 1) + CheckFloppySig = 0; + u32 bootorder = (rtc_read(CMOS_BIOS_BOOTFLAG2) + | ((rtc_read(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4)); + DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio + = DefaultBEVPrio = DEFAULT_PRIO; + int i; + for (i=101; i<104; i++) { + u32 val = bootorder & 0x0f; + bootorder >>= 4; + switch (val) { + case 1: DefaultFloppyPrio = i; break; + case 2: DefaultHDPrio = i; break; + case 3: DefaultCDPrio = i; break; + case 4: DefaultBEVPrio = i; break; + } + } + } + + BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000); + + loadBootOrder(); +} + + +/**************************************************************** + * BootList handling + ****************************************************************/ + +struct bootentry_s { + int type; + union { + u32 data; + struct segoff_s vector; + struct drive_s *drive; + }; + int priority; + const char *description; + struct hlist_node node; +}; +static struct hlist_head BootList VARVERIFY32INIT; + +#define IPL_TYPE_FLOPPY 0x01 +#define IPL_TYPE_HARDDISK 0x02 +#define IPL_TYPE_CDROM 0x03 +#define IPL_TYPE_CBFS 0x20 +#define IPL_TYPE_BEV 0x80 +#define IPL_TYPE_BCV 0x81 +#define IPL_TYPE_HALT 0xf0 + +static void +bootentry_add(int type, int prio, u32 data, const char *desc) +{ + if (! CONFIG_BOOT) + return; + struct bootentry_s *be = malloc_tmp(sizeof(*be)); + if (!be) { + warn_noalloc(); + return; + } + be->type = type; + be->priority = prio; + be->data = data; + be->description = desc ?: "?"; + dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n" + , be->description, type, prio, data); + + // Add entry in sorted order. + struct hlist_node **pprev; + struct bootentry_s *pos; + hlist_for_each_entry_pprev(pos, pprev, &BootList, node) { + if (be->priority < pos->priority) + break; + if (be->priority > pos->priority) + continue; + if (be->type < pos->type) + break; + if (be->type > pos->type) + continue; + if (be->type <= IPL_TYPE_CDROM + && (be->drive->type < pos->drive->type + || (be->drive->type == pos->drive->type + && be->drive->cntl_id < pos->drive->cntl_id))) + break; + } + hlist_add(&be->node, pprev); +} + +// Return the given priority if it's set - defaultprio otherwise. +static inline int defPrio(int priority, int defaultprio) { + return (priority < 0) ? defaultprio : priority; +} + +// Add a BEV vector for a given pnp compatible option rom. +void +boot_add_bev(u16 seg, u16 bev, u16 desc, int prio) +{ + bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio) + , SEGOFF(seg, bev).segoff + , desc ? MAKE_FLATPTR(seg, desc) : "Unknown"); + DefaultBEVPrio = DEFAULT_PRIO; +} + +// Add a bcv entry for an expansion card harddrive or legacy option rom +void +boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio) +{ + bootentry_add(IPL_TYPE_BCV, defPrio(prio, DefaultHDPrio) + , SEGOFF(seg, ip).segoff + , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom"); +} + +void +boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio) + , (u32)drive_g, desc); +} + +void +boot_add_hd(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio) + , (u32)drive_g, desc); +} + +void +boot_add_cd(struct drive_s *drive_g, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio) + , (u32)drive_g, desc); +} + +// Add a CBFS payload entry +void +boot_add_cbfs(void *data, const char *desc, int prio) +{ + bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc); +} + + +/**************************************************************** + * Keyboard calls + ****************************************************************/ + +// See if a keystroke is pending in the keyboard buffer. +static int +check_for_keystroke(void) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF|F_ZF; + br.ah = 1; + call16_int(0x16, &br); + return !(br.flags & F_ZF); +} + +// Return a keystroke - waiting forever if necessary. +static int +get_raw_keystroke(void) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x16, &br); + return br.ah; +} + +// Read a keystroke - waiting up to 'msec' milliseconds. +static int +get_keystroke(int msec) +{ + u32 end = irqtimer_calc(msec); + for (;;) { + if (check_for_keystroke()) + return get_raw_keystroke(); + if (irqtimer_check(end)) + return -1; + yield_toirq(); + } +} + + +/**************************************************************** + * Boot menu and BCV execution + ****************************************************************/ + +#define DEFAULT_BOOTMENU_WAIT 2500 + +// Show IPL option menu. +void +interactive_bootmenu(void) +{ + // XXX - show available drives? + + if (! CONFIG_BOOTMENU || !romfile_loadint("etc/show-boot-menu", 1)) + return; + + while (get_keystroke(0) >= 0) + ; + + char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); + int menukey = romfile_loadint("etc/boot-menu-key", 0x86); + printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n\n"); + free(bootmsg); + + u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); + enable_bootsplash(); + int scan_code = get_keystroke(menutime); + disable_bootsplash(); + if (scan_code != menukey) + return; + + while (get_keystroke(0) >= 0) + ; + + printf("Select boot device:\n\n"); + wait_threads(); + + // Show menu items + int maxmenu = 0; + struct bootentry_s *pos; + hlist_for_each_entry(pos, &BootList, node) { + char desc[60]; + maxmenu++; + printf("%d. %s\n", maxmenu + , strtcpy(desc, pos->description, ARRAY_SIZE(desc))); + } + + // Get key press + for (;;) { + scan_code = get_keystroke(1000); + if (scan_code >= 1 && scan_code <= maxmenu+1) + break; + } + printf("\n"); + if (scan_code == 0x01) + // ESC + return; + + // Find entry and make top priority. + int choice = scan_code - 1; + hlist_for_each_entry(pos, &BootList, node) { + if (! --choice) + break; + } + hlist_del(&pos->node); + pos->priority = 0; + hlist_add_head(&pos->node, &BootList); +} + +// BEV (Boot Execution Vector) list +struct bev_s { + int type; + u32 vector; +}; +static struct bev_s BEV[20]; +static int BEVCount; +static int HaveHDBoot, HaveFDBoot; + +static void +add_bev(int type, u32 vector) +{ + if (type == IPL_TYPE_HARDDISK && HaveHDBoot++) + return; + if (type == IPL_TYPE_FLOPPY && HaveFDBoot++) + return; + if (BEVCount >= ARRAY_SIZE(BEV)) + return; + struct bev_s *bev = &BEV[BEVCount++]; + bev->type = type; + bev->vector = vector; +} + +// Prepare for boot - show menu and run bcvs. +void +bcv_prepboot(void) +{ + if (! CONFIG_BOOT) + return; + + int haltprio = find_prio("HALT"); + if (haltprio >= 0) + bootentry_add(IPL_TYPE_HALT, haltprio, 0, "HALT"); + + // Map drives and populate BEV list + struct bootentry_s *pos; + hlist_for_each_entry(pos, &BootList, node) { + switch (pos->type) { + case IPL_TYPE_BCV: + call_bcv(pos->vector.seg, pos->vector.offset); + add_bev(IPL_TYPE_HARDDISK, 0); + break; + case IPL_TYPE_FLOPPY: + map_floppy_drive(pos->drive); + add_bev(IPL_TYPE_FLOPPY, 0); + break; + case IPL_TYPE_HARDDISK: + map_hd_drive(pos->drive); + add_bev(IPL_TYPE_HARDDISK, 0); + break; + case IPL_TYPE_CDROM: + map_cd_drive(pos->drive); + // NO BREAK + default: + add_bev(pos->type, pos->data); + break; + } + } + + // If nothing added a floppy/hd boot - add it manually. + add_bev(IPL_TYPE_FLOPPY, 0); + add_bev(IPL_TYPE_HARDDISK, 0); +} + + +/**************************************************************** + * Boot code (int 18/19) + ****************************************************************/ + +// Jump to a bootup entry point. +static void +call_boot_entry(struct segoff_s bootsegip, u8 bootdrv) +{ + dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.code = bootsegip; + // Set the magic number in ax and the boot drive in dl. + br.dl = bootdrv; + br.ax = 0xaa55; + farcall16(&br); +} + +// Boot from a disk (either floppy or harddrive) +static void +boot_disk(u8 bootdrv, int checksig) +{ + u16 bootseg = 0x07c0; + + // Read sector + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.dl = bootdrv; + br.es = bootseg; + br.ah = 2; + br.al = 1; + br.cl = 1; + call16_int(0x13, &br); + + if (br.flags & F_CF) { + printf("Boot failed: could not read the boot disk\n\n"); + return; + } + + if (checksig) { + struct mbr_s *mbr = (void*)0; + if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) { + printf("Boot failed: not a bootable disk\n\n"); + return; + } + } + + /* Canonicalize bootseg:bootip */ + u16 bootip = (bootseg & 0x0fff) << 4; + bootseg &= 0xf000; + + call_boot_entry(SEGOFF(bootseg, bootip), bootdrv); +} + +// Boot from a CD-ROM +static void +boot_cdrom(struct drive_s *drive_g) +{ + if (! CONFIG_CDROM_BOOT) + return; + printf("Booting from DVD/CD...\n"); + + int status = cdrom_boot(drive_g); + if (status) { + printf("Boot failed: Could not read from CDROM (code %04x)\n", status); + return; + } + + u8 bootdrv = CDEmu.emulated_drive; + u16 bootseg = CDEmu.load_segment; + /* Canonicalize bootseg:bootip */ + u16 bootip = (bootseg & 0x0fff) << 4; + bootseg &= 0xf000; + + call_boot_entry(SEGOFF(bootseg, bootip), bootdrv); +} + +// Boot from a CBFS payload +static void +boot_cbfs(struct cbfs_file *file) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + printf("Booting from CBFS...\n"); + cbfs_run_payload(file); +} + +// Boot from a BEV entry on an optionrom. +static void +boot_rom(u32 vector) +{ + printf("Booting from ROM...\n"); + struct segoff_s so; + so.segoff = vector; + call_boot_entry(so, 0); +} + +// Unable to find bootable device - warn user and eventually retry. +static void +boot_fail(void) +{ + if (BootRetryTime == (u32)-1) + printf("No bootable device.\n"); + else + printf("No bootable device. Retrying in %d seconds.\n" + , BootRetryTime/1000); + // Wait for 'BootRetryTime' milliseconds and then reboot. + u32 end = irqtimer_calc(BootRetryTime); + for (;;) { + if (BootRetryTime != (u32)-1 && irqtimer_check(end)) + break; + yield_toirq(); + } + printf("Rebooting.\n"); + reset(); +} + +// Determine next boot method and attempt a boot using it. +static void +do_boot(int seq_nr) +{ + if (! CONFIG_BOOT) + panic("Boot support not compiled in.\n"); + + if (seq_nr >= BEVCount) + boot_fail(); + + // Boot the given BEV type. + struct bev_s *ie = &BEV[seq_nr]; + switch (ie->type) { + case IPL_TYPE_FLOPPY: + printf("Booting from Floppy...\n"); + boot_disk(0x00, CheckFloppySig); + break; + case IPL_TYPE_HARDDISK: + printf("Booting from Hard Disk...\n"); + boot_disk(0x80, 1); + break; + case IPL_TYPE_CDROM: + boot_cdrom((void*)ie->vector); + break; + case IPL_TYPE_CBFS: + boot_cbfs((void*)ie->vector); + break; + case IPL_TYPE_BEV: + boot_rom(ie->vector); + break; + case IPL_TYPE_HALT: + boot_fail(); + break; + } + + // Boot failed: invoke the boot recovery function + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x18, &br); +} + +int BootSequence VARLOW = -1; + +// Boot Failure recovery: try the next device. +void VISIBLE32FLAT +handle_18(void) +{ + debug_enter(NULL, DEBUG_HDL_18); + int seq = BootSequence + 1; + BootSequence = seq; + do_boot(seq); +} + +// INT 19h Boot Load Service Entry Point +void VISIBLE32FLAT +handle_19(void) +{ + debug_enter(NULL, DEBUG_HDL_19); + BootSequence = 0; + do_boot(0); +} diff --git a/qemu/roms/seabios/src/bootsplash.c b/qemu/roms/seabios/src/bootsplash.c new file mode 100644 index 000000000..c572685de --- /dev/null +++ b/qemu/roms/seabios/src/bootsplash.c @@ -0,0 +1,254 @@ +// Initialize the VGA console and possibly show a boot splash image. +// +// Copyright (C) 2009-2010 coresystems GmbH +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEG +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadfile +#include "stacks.h" // call16_int +#include "std/vbe.h" // struct vbe_info +#include "string.h" // memset +#include "util.h" // enable_bootsplash + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Call int10 vga handler. +static void +call16_int10(struct bregs *br) +{ + br->flags = F_IF; + start_preempt(); + call16_int(0x10, br); + finish_preempt(); +} + + +/**************************************************************** + * VGA text / graphics console + ****************************************************************/ + +void +enable_vga_console(void) +{ + dprintf(1, "Turning on vga text mode console\n"); + struct bregs br; + + /* Enable VGA text mode */ + memset(&br, 0, sizeof(br)); + br.ax = 0x0003; + call16_int10(&br); + + // Write to screen. + printf("SeaBIOS (version %s)\n", VERSION); + display_uuid(); +} + +static int +find_videomode(struct vbe_info *vesa_info, struct vbe_mode_info *mode_info + , int width, int height, int bpp_req) +{ + dprintf(3, "Finding vesa mode with dimensions %d/%d\n", width, height); + u16 *videomodes = SEGOFF_TO_FLATPTR(vesa_info->video_mode); + for (;; videomodes++) { + u16 videomode = *videomodes; + if (videomode == 0xffff) { + dprintf(1, "Unable to find vesa video mode dimensions %d/%d\n" + , width, height); + return -1; + } + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ax = 0x4f01; + br.cx = videomode; + br.di = FLATPTR_TO_OFFSET(mode_info); + br.es = FLATPTR_TO_SEG(mode_info); + call16_int10(&br); + if (br.ax != 0x4f) { + dprintf(1, "get_mode failed.\n"); + continue; + } + if (mode_info->xres != width + || mode_info->yres != height) + continue; + u8 depth = mode_info->bits_per_pixel; + if (bpp_req == 0) { + if (depth != 16 && depth != 24 && depth != 32) + continue; + } else { + if (depth != bpp_req) + continue; + } + return videomode; + } +} + +static int BootsplashActive; + +void +enable_bootsplash(void) +{ + if (!CONFIG_BOOTSPLASH) + return; + /* splash picture can be bmp or jpeg file */ + dprintf(3, "Checking for bootsplash\n"); + u8 type = 0; /* 0 means jpg, 1 means bmp, default is 0=jpg */ + int filesize; + u8 *filedata = romfile_loadfile("bootsplash.jpg", &filesize); + if (!filedata) { + filedata = romfile_loadfile("bootsplash.bmp", &filesize); + if (!filedata) + return; + type = 1; + } + dprintf(3, "start showing bootsplash\n"); + + u8 *picture = NULL; /* data buff used to be flushed to the video buf */ + struct jpeg_decdata *jpeg = NULL; + struct bmp_decdata *bmp = NULL; + struct vbe_info *vesa_info = malloc_tmplow(sizeof(*vesa_info)); + struct vbe_mode_info *mode_info = malloc_tmplow(sizeof(*mode_info)); + if (!vesa_info || !mode_info) { + warn_noalloc(); + goto done; + } + + /* Check whether we have a VESA 2.0 compliant BIOS */ + memset(vesa_info, 0, sizeof(struct vbe_info)); + vesa_info->signature = VBE2_SIGNATURE; + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ax = 0x4f00; + br.di = FLATPTR_TO_OFFSET(vesa_info); + br.es = FLATPTR_TO_SEG(vesa_info); + call16_int10(&br); + if (vesa_info->signature != VESA_SIGNATURE) { + dprintf(1,"No VBE2 found.\n"); + goto done; + } + + /* Print some debugging information about our card. */ + char *vendor = SEGOFF_TO_FLATPTR(vesa_info->oem_vendor_string); + char *product = SEGOFF_TO_FLATPTR(vesa_info->oem_product_string); + dprintf(3, "VESA %d.%d\nVENDOR: %s\nPRODUCT: %s\n", + vesa_info->version>>8, vesa_info->version&0xff, + vendor, product); + + int ret, width, height; + int bpp_require = 0; + if (type == 0) { + jpeg = jpeg_alloc(); + if (!jpeg) { + warn_noalloc(); + goto done; + } + /* Parse jpeg and get image size. */ + dprintf(5, "Decoding bootsplash.jpg\n"); + ret = jpeg_decode(jpeg, filedata); + if (ret) { + dprintf(1, "jpeg_decode failed with return code %d...\n", ret); + goto done; + } + jpeg_get_size(jpeg, &width, &height); + } else { + bmp = bmp_alloc(); + if (!bmp) { + warn_noalloc(); + goto done; + } + /* Parse bmp and get image size. */ + dprintf(5, "Decoding bootsplash.bmp\n"); + ret = bmp_decode(bmp, filedata, filesize); + if (ret) { + dprintf(1, "bmp_decode failed with return code %d...\n", ret); + goto done; + } + bmp_get_size(bmp, &width, &height); + bpp_require = 24; + } + /* jpeg would use 16 or 24 bpp video mode, BMP use 24bpp mode only */ + + // Try to find a graphics mode with the corresponding dimensions. + int videomode = find_videomode(vesa_info, mode_info, width, height, + bpp_require); + if (videomode < 0) { + dprintf(1, "failed to find a videomode with %dx%d %dbpp (0=any).\n", + width, height, bpp_require); + goto done; + } + void *framebuffer = (void *)mode_info->phys_base; + int depth = mode_info->bits_per_pixel; + dprintf(3, "mode: %04x\n", videomode); + dprintf(3, "framebuffer: %p\n", framebuffer); + dprintf(3, "bytes per scanline: %d\n", mode_info->bytes_per_scanline); + dprintf(3, "bits per pixel: %d\n", depth); + + // Allocate space for image and decompress it. + int imagesize = height * mode_info->bytes_per_scanline; + picture = malloc_tmphigh(imagesize); + if (!picture) { + warn_noalloc(); + goto done; + } + + if (type == 0) { + dprintf(5, "Decompressing bootsplash.jpg\n"); + ret = jpeg_show(jpeg, picture, width, height, depth, + mode_info->bytes_per_scanline); + if (ret) { + dprintf(1, "jpeg_show failed with return code %d...\n", ret); + goto done; + } + } else { + dprintf(5, "Decompressing bootsplash.bmp\n"); + ret = bmp_show(bmp, picture, width, height, depth, + mode_info->bytes_per_scanline); + if (ret) { + dprintf(1, "bmp_show failed with return code %d...\n", ret); + goto done; + } + } + + /* Switch to graphics mode */ + dprintf(5, "Switching to graphics mode\n"); + memset(&br, 0, sizeof(br)); + br.ax = 0x4f02; + br.bx = videomode | VBE_MODE_LINEAR_FRAME_BUFFER; + call16_int10(&br); + if (br.ax != 0x4f) { + dprintf(1, "set_mode failed.\n"); + goto done; + } + + /* Show the picture */ + dprintf(5, "Showing bootsplash picture\n"); + iomemcpy(framebuffer, picture, imagesize); + dprintf(5, "Bootsplash copy complete\n"); + BootsplashActive = 1; + +done: + free(filedata); + free(picture); + free(vesa_info); + free(mode_info); + free(jpeg); + free(bmp); + return; +} + +void +disable_bootsplash(void) +{ + if (!CONFIG_BOOTSPLASH || !BootsplashActive) + return; + BootsplashActive = 0; + enable_vga_console(); +} diff --git a/qemu/roms/seabios/src/bregs.h b/qemu/roms/seabios/src/bregs.h new file mode 100644 index 000000000..d40526345 --- /dev/null +++ b/qemu/roms/seabios/src/bregs.h @@ -0,0 +1,80 @@ +// Structure layout of cpu registers that the bios uses. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#ifndef __BREGS_H +#define __BREGS_H + +#include "types.h" // u16 +#include "x86.h" // F_CF + + +/**************************************************************** + * Registers saved/restored in romlayout.S + ****************************************************************/ + +#define UREG(ER, R, RH, RL) union { u32 ER; struct { u16 R; u16 R ## _hi; }; struct { u8 RL; u8 RH; u8 R ## _hilo; u8 R ## _hihi; }; } + +// Layout of registers passed in to irq handlers. Note that this +// layout corresponds to code in romlayout.S - don't change it here +// without also updating the assembler code. +struct bregs { + u16 ds; + u16 es; + UREG(edi, di, di8u, di8l); + UREG(esi, si, si8u, si8l); + UREG(ebp, bp, bp8u, bp8l); + UREG(ebx, bx, bh, bl); + UREG(edx, dx, dh, dl); + UREG(ecx, cx, ch, cl); + UREG(eax, ax, ah, al); + struct segoff_s code; + u16 flags; +} PACKED; + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +static inline void +set_cf(struct bregs *regs, int cond) +{ + if (cond) + regs->flags |= F_CF; + else + regs->flags &= ~F_CF; +} + +// Frequently used return codes +#define RET_EUNSUPPORTED 0x86 + +static inline void +set_success(struct bregs *regs) +{ + set_cf(regs, 0); +} + +static inline void +set_code_success(struct bregs *regs) +{ + regs->ah = 0; + set_cf(regs, 0); +} + +static inline void +set_invalid_silent(struct bregs *regs) +{ + set_cf(regs, 1); +} + +static inline void +set_code_invalid_silent(struct bregs *regs, u8 code) +{ + regs->ah = code; + set_cf(regs, 1); +} + +#endif // bregs.h diff --git a/qemu/roms/seabios/src/byteorder.h b/qemu/roms/seabios/src/byteorder.h new file mode 100644 index 000000000..928c1b807 --- /dev/null +++ b/qemu/roms/seabios/src/byteorder.h @@ -0,0 +1,71 @@ +#ifndef __BYTEORDER_H +#define __BYTEORDER_H + +#include "types.h" // u32 + +static inline u16 __swab16_constant(u16 val) { + return (val<<8) | (val>>8); +} +static inline u32 __swab32_constant(u32 val) { + return (val<<24) | ((val&0xff00)<<8) | ((val&0xff0000)>>8) | (val>>24); +} +static inline u64 __swab64_constant(u64 val) { + return ((u64)__swab32_constant(val) << 32) | __swab32_constant(val>>32); +} +static inline u32 __swab32(u32 val) { + asm("bswapl %0" : "+r"(val)); + return val; +} +static inline u64 __swab64(u64 val) { + union u64_u32_u i, o; + i.val = val; + o.lo = __swab32(i.hi); + o.hi = __swab32(i.lo); + return o.val; +} + +#define swab16(x) __swab16_constant(x) +#define swab32(x) (__builtin_constant_p((u32)(x)) \ + ? __swab32_constant(x) : __swab32(x)) +#define swab64(x) (__builtin_constant_p((u64)(x)) \ + ? __swab64_constant(x) : __swab64(x)) + +static inline u16 cpu_to_le16(u16 x) { + return x; +} +static inline u32 cpu_to_le32(u32 x) { + return x; +} +static inline u64 cpu_to_le64(u64 x) { + return x; +} +static inline u16 le16_to_cpu(u16 x) { + return x; +} +static inline u32 le32_to_cpu(u32 x) { + return x; +} +static inline u64 le64_to_cpu(u64 x) { + return x; +} + +static inline u16 cpu_to_be16(u16 x) { + return swab16(x); +} +static inline u32 cpu_to_be32(u32 x) { + return swab32(x); +} +static inline u64 cpu_to_be64(u64 x) { + return swab64(x); +} +static inline u16 be16_to_cpu(u16 x) { + return swab16(x); +} +static inline u32 be32_to_cpu(u32 x) { + return swab32(x); +} +static inline u64 be64_to_cpu(u64 x) { + return swab64(x); +} + +#endif // byteorder.h diff --git a/qemu/roms/seabios/src/cdrom.c b/qemu/roms/seabios/src/cdrom.c new file mode 100644 index 000000000..92f34f42b --- /dev/null +++ b/qemu/roms/seabios/src/cdrom.c @@ -0,0 +1,281 @@ +// Support for booting from cdroms (the "El Torito" spec). +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "hw/ata.h" // ATA_CMD_REQUEST_SENSE +#include "hw/blockcmd.h" // CDB_CMD_REQUEST_SENSE +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // cdrom_prepboot + +// Locks for removable devices +u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; + + +/**************************************************************** + * CD emulation + ****************************************************************/ + +struct eltorito_s CDEmu VARLOW = { .size=sizeof(CDEmu) }; +struct drive_s *emulated_drive_gf VARLOW; +struct drive_s *cdemu_drive_gf VARFSEG; + +static int +cdemu_read(struct disk_op_s *op) +{ + struct drive_s *drive_gf = GET_LOW(emulated_drive_gf); + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = op->command; + dop.lba = GET_LOW(CDEmu.ilba) + op->lba / 4; + + int count = op->count; + op->count = 0; + u8 *cdbuf_fl = GET_GLOBAL(bounce_buf_fl); + + if (op->lba & 3) { + // Partial read of first block. + dop.count = 1; + dop.buf_fl = cdbuf_fl; + int ret = process_op(&dop); + if (ret) + return ret; + u8 thiscount = 4 - (op->lba & 3); + if (thiscount > count) + thiscount = count; + count -= thiscount; + memcpy_fl(op->buf_fl, cdbuf_fl + (op->lba & 3) * 512, thiscount * 512); + op->buf_fl += thiscount * 512; + op->count += thiscount; + dop.lba++; + } + + if (count > 3) { + // Read n number of regular blocks. + dop.count = count / 4; + dop.buf_fl = op->buf_fl; + int ret = process_op(&dop); + op->count += dop.count * 4; + if (ret) + return ret; + u8 thiscount = count & ~3; + count &= 3; + op->buf_fl += thiscount * 512; + dop.lba += thiscount / 4; + } + + if (count) { + // Partial read on last block. + dop.count = 1; + dop.buf_fl = cdbuf_fl; + int ret = process_op(&dop); + if (ret) + return ret; + u8 thiscount = count; + memcpy_fl(op->buf_fl, cdbuf_fl, thiscount * 512); + op->count += thiscount; + } + + return DISK_RET_SUCCESS; +} + +int +process_cdemu_op(struct disk_op_s *op) +{ + if (!CONFIG_CDROM_EMU) + return 0; + + switch (op->command) { + case CMD_READ: + return cdemu_read(op); + case CMD_WRITE: + case CMD_FORMAT: + return DISK_RET_EWRITEPROTECT; + case CMD_VERIFY: + case CMD_RESET: + case CMD_SEEK: + case CMD_ISREADY: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +void +cdrom_prepboot(void) +{ + if (!CONFIG_CDROM_EMU) + return; + if (!CDCount) + return; + if (create_bounce_buf() < 0) + return; + + struct drive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + free(drive); + return; + } + cdemu_drive_gf = drive; + memset(drive, 0, sizeof(*drive)); + drive->type = DTYPE_CDEMU; + drive->blksize = DISK_SECTOR_SIZE; + drive->sectors = (u64)-1; +} + + +/**************************************************************** + * CD booting + ****************************************************************/ + +int +cdrom_boot(struct drive_s *drive) +{ + ASSERT32FLAT(); + struct disk_op_s dop; + int cdid = getDriveId(EXTTYPE_CD, drive); + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = drive; + if (!dop.drive_gf || cdid < 0) + return 1; + + int ret = scsi_is_ready(&dop); + if (ret) + dprintf(1, "scsi_is_ready returned %d\n", ret); + + // Read the Boot Record Volume Descriptor + u8 buffer[CDROM_SECTOR_SIZE]; + dop.command = CMD_READ; + dop.lba = 0x11; + dop.count = 1; + dop.buf_fl = buffer; + ret = scsi_process_op(&dop); + if (ret) + return 3; + + // Validity checks + if (buffer[0]) + return 4; + if (strcmp((char*)&buffer[1], "CD001\001EL TORITO SPECIFICATION") != 0) + return 5; + + // ok, now we calculate the Boot catalog address + u32 lba = *(u32*)&buffer[0x47]; + + // And we read the Boot Catalog + dop.lba = lba; + dop.count = 1; + ret = scsi_process_op(&dop); + if (ret) + return 7; + + // Validation entry + if (buffer[0x00] != 0x01) + return 8; // Header + if (buffer[0x01] != 0x00) + return 9; // Platform + if (buffer[0x1E] != 0x55) + return 10; // key 1 + if (buffer[0x1F] != 0xAA) + return 10; // key 2 + + // Initial/Default Entry + if (buffer[0x20] != 0x88) + return 11; // Bootable + + // Fill in el-torito cdrom emulation fields. + emulated_drive_gf = drive; + u8 media = buffer[0x21]; + + u16 boot_segment = *(u16*)&buffer[0x22]; + if (!boot_segment) + boot_segment = 0x07C0; + CDEmu.load_segment = boot_segment; + CDEmu.buffer_segment = 0x0000; + + u16 nbsectors = *(u16*)&buffer[0x26]; + CDEmu.sector_count = nbsectors; + + lba = *(u32*)&buffer[0x28]; + CDEmu.ilba = lba; + + CDEmu.controller_index = drive->cntl_id / 2; + CDEmu.device_spec = drive->cntl_id % 2; + + // And we read the image in memory + nbsectors = DIV_ROUND_UP(nbsectors, 4); + dop.lba = lba; + dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); + while (nbsectors) { + int count = nbsectors; + if (count > 64*1024/CDROM_SECTOR_SIZE) + count = 64*1024/CDROM_SECTOR_SIZE; + dop.count = count; + ret = scsi_process_op(&dop); + if (ret) + return 12; + nbsectors -= count; + dop.lba += count; + dop.buf_fl += count*CDROM_SECTOR_SIZE; + } + + if (media == 0) { + // No emulation requested - return success. + CDEmu.emulated_drive = EXTSTART_CD + cdid; + return 0; + } + + // Emulation of a floppy/harddisk requested + if (! CONFIG_CDROM_EMU || !cdemu_drive_gf) + return 13; + + // Set emulated drive id and increase bios installed hardware + // number of devices + if (media < 4) { + // Floppy emulation + CDEmu.emulated_drive = 0x00; + // XXX - get and set actual floppy count. + set_equipment_flags(0x41, 0x41); + + switch (media) { + case 0x01: // 1.2M floppy + CDEmu.chs.sptcyl = 15; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + case 0x02: // 1.44M floppy + CDEmu.chs.sptcyl = 18; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + case 0x03: // 2.88M floppy + CDEmu.chs.sptcyl = 36; + CDEmu.chs.cyllow = 79; + CDEmu.chs.heads = 1; + break; + } + } else { + // Harddrive emulation + CDEmu.emulated_drive = 0x80; + SET_BDA(hdcount, GET_BDA(hdcount) + 1); + + // Peak at partition table to get chs. + struct mbr_s *mbr = MAKE_FLATPTR(boot_segment, 0); + CDEmu.chs = mbr->partitions[0].last; + } + + // everything is ok, so from now on, the emulation is active + CDEmu.media = media; + dprintf(6, "cdemu media=%d\n", media); + + return 0; +} diff --git a/qemu/roms/seabios/src/clock.c b/qemu/roms/seabios/src/clock.c new file mode 100644 index 000000000..9ab0ac026 --- /dev/null +++ b/qemu/roms/seabios/src/clock.c @@ -0,0 +1,462 @@ +// 16bit code to handle system clocks. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "hw/pic.h" // pic_eoi1 +#include "hw/rtc.h" // rtc_read +#include "hw/usb-hid.h" // usb_check_event +#include "output.h" // debug_enter +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // clock_setup + + +/**************************************************************** + * Init + ****************************************************************/ + +static u32 +bcd2bin(u8 val) +{ + return (val & 0xf) + ((val >> 4) * 10); +} + +u8 Century VARLOW; + +void +clock_setup(void) +{ + dprintf(3, "init timer\n"); + pit_setup(); + + rtc_setup(); + rtc_updating(); + u32 seconds = bcd2bin(rtc_read(CMOS_RTC_SECONDS)); + u32 minutes = bcd2bin(rtc_read(CMOS_RTC_MINUTES)); + u32 hours = bcd2bin(rtc_read(CMOS_RTC_HOURS)); + u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000); + SET_BDA(timer_counter, ticks % TICKS_PER_DAY); + + // Setup Century storage + if (CONFIG_QEMU) { + Century = rtc_read(CMOS_CENTURY); + } else { + // Infer current century from the year. + u8 year = rtc_read(CMOS_RTC_YEAR); + if (year > 0x80) + Century = 0x19; + else + Century = 0x20; + } + + enable_hwirq(0, FUNC16(entry_08)); + enable_hwirq(8, FUNC16(entry_70)); +} + + +/**************************************************************** + * Standard clock functions + ****************************************************************/ + +// get current clock count +static void +handle_1a00(struct bregs *regs) +{ + yield(); + u32 ticks = GET_BDA(timer_counter); + regs->cx = ticks >> 16; + regs->dx = ticks; + regs->al = GET_BDA(timer_rollover); + SET_BDA(timer_rollover, 0); // reset flag + set_success(regs); +} + +// Set Current Clock Count +static void +handle_1a01(struct bregs *regs) +{ + u32 ticks = (regs->cx << 16) | regs->dx; + SET_BDA(timer_counter, ticks); + SET_BDA(timer_rollover, 0); // reset flag + // XXX - should use set_code_success()? + regs->ah = 0; + set_success(regs); +} + +// Read CMOS Time +static void +handle_1a02(struct bregs *regs) +{ + if (rtc_updating()) { + set_invalid(regs); + return; + } + + regs->dh = rtc_read(CMOS_RTC_SECONDS); + regs->cl = rtc_read(CMOS_RTC_MINUTES); + regs->ch = rtc_read(CMOS_RTC_HOURS); + regs->dl = rtc_read(CMOS_STATUS_B) & RTC_B_DSE; + regs->ah = 0; + regs->al = regs->ch; + set_success(regs); +} + +// Set CMOS Time +static void +handle_1a03(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1111 1101 0111 1101 0000 0000 + // after 0110 0010 0110 0010 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01100000b) | 00000010b) + if (rtc_updating()) { + rtc_setup(); + // fall through as if an update were not in progress + } + rtc_write(CMOS_RTC_SECONDS, regs->dh); + rtc_write(CMOS_RTC_MINUTES, regs->cl); + rtc_write(CMOS_RTC_HOURS, regs->ch); + // Set Daylight Savings time enabled bit to requested value + u8 val8 = ((rtc_read(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE)) + | RTC_B_24HR | (regs->dl & RTC_B_DSE)); + rtc_write(CMOS_STATUS_B, val8); + regs->ah = 0; + regs->al = val8; // val last written to Reg B + set_success(regs); +} + +// Read CMOS Date +static void +handle_1a04(struct bregs *regs) +{ + regs->ah = 0; + if (rtc_updating()) { + set_invalid(regs); + return; + } + regs->cl = rtc_read(CMOS_RTC_YEAR); + regs->dh = rtc_read(CMOS_RTC_MONTH); + regs->dl = rtc_read(CMOS_RTC_DAY_MONTH); + regs->ch = GET_LOW(Century); + regs->al = regs->ch; + set_success(regs); +} + +// Set CMOS Date +static void +handle_1a05(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0000 0010 0000 0000 + // after 0110 1101 0111 1101 0000 0010 0000 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01111111b) + if (rtc_updating()) { + rtc_setup(); + set_invalid(regs); + return; + } + rtc_write(CMOS_RTC_YEAR, regs->cl); + rtc_write(CMOS_RTC_MONTH, regs->dh); + rtc_write(CMOS_RTC_DAY_MONTH, regs->dl); + SET_LOW(Century, regs->ch); + // clear halt-clock bit + u8 val8 = rtc_read(CMOS_STATUS_B) & ~RTC_B_SET; + rtc_write(CMOS_STATUS_B, val8); + regs->ah = 0; + regs->al = val8; // AL = val last written to Reg B + set_success(regs); +} + +// Set Alarm Time in CMOS +static void +handle_1a06(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 + // before 1101 1111 0101 1111 0000 0000 + // after 0110 1111 0111 1111 0010 0000 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = ((RegB & 01111111b) | 00100000b) + u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B + regs->ax = 0; + if (val8 & RTC_B_AIE) { + // Alarm interrupt enabled already + set_invalid(regs); + return; + } + if (rtc_updating()) { + rtc_setup(); + // fall through as if an update were not in progress + } + rtc_write(CMOS_RTC_SECONDS_ALARM, regs->dh); + rtc_write(CMOS_RTC_MINUTES_ALARM, regs->cl); + rtc_write(CMOS_RTC_HOURS_ALARM, regs->ch); + // enable Status Reg B alarm bit, clear halt clock bit + rtc_write(CMOS_STATUS_B, (val8 & ~RTC_B_SET) | RTC_B_AIE); + set_success(regs); +} + +// Turn off Alarm +static void +handle_1a07(struct bregs *regs) +{ + // Using a debugger, I notice the following masking/setting + // of bits in Status Register B, by setting Reg B to + // a few values and getting its value after INT 1A was called. + // + // try#1 try#2 try#3 try#4 + // before 1111 1101 0111 1101 0010 0000 0010 0010 + // after 0100 0101 0101 0101 0000 0000 0000 0010 + // + // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1 + // My assumption: RegB = (RegB & 01010111b) + u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B + // clear clock-halt bit, disable alarm bit + rtc_write(CMOS_STATUS_B, val8 & ~(RTC_B_SET|RTC_B_AIE)); + regs->ah = 0; + regs->al = val8; // val last written to Reg B + set_success(regs); +} + +// Unsupported +static void +handle_1aXX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT 1Ah Time-of-day Service Entry Point +void VISIBLE16 +handle_1a(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_1a); + switch (regs->ah) { + case 0x00: handle_1a00(regs); break; + case 0x01: handle_1a01(regs); break; + case 0x02: handle_1a02(regs); break; + case 0x03: handle_1a03(regs); break; + case 0x04: handle_1a04(regs); break; + case 0x05: handle_1a05(regs); break; + case 0x06: handle_1a06(regs); break; + case 0x07: handle_1a07(regs); break; + default: handle_1aXX(regs); break; + } +} + +// INT 08h System Timer ISR Entry Point +void VISIBLE16 +handle_08(void) +{ + debug_isr(DEBUG_ISR_08); + + // Update counter + u32 counter = GET_BDA(timer_counter); + counter++; + // compare to one days worth of timer ticks at 18.2 hz + if (counter >= TICKS_PER_DAY) { + // there has been a midnight rollover at this point + counter = 0; + SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1); + } + SET_BDA(timer_counter, counter); + + // Check for internal events. + floppy_tick(); + usb_check_event(); + + // chain to user timer tick INT #0x1c + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x1c, &br); + + pic_eoi1(); +} + + +/**************************************************************** + * IRQ based timer + ****************************************************************/ + +// Calculate the timer value at 'count' number of full timer ticks in +// the future. +u32 +irqtimer_calc_ticks(u32 count) +{ + return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY; +} + +// Return the timer value that is 'msecs' time in the future. +u32 +irqtimer_calc(u32 msecs) +{ + if (!msecs) + return GET_BDA(timer_counter); + return irqtimer_calc_ticks(ticks_from_ms(msecs)); +} + +// Check if the given timer value has passed. +int +irqtimer_check(u32 end) +{ + return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY) + < (TICKS_PER_DAY/2)); +} + + +/**************************************************************** + * Periodic timer + ****************************************************************/ + +static int +set_usertimer(u32 usecs, u16 seg, u16 offset) +{ + if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) + return -1; + + // Interval not already set. + SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte. + SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset)); + SET_BDA(user_wait_timeout, usecs); + rtc_use(); + return 0; +} + +static void +clear_usertimer(void) +{ + if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)) + return; + // Turn off status byte. + SET_BDA(rtc_wait_flag, 0); + rtc_release(); +} + +#define RET_ECLOCKINUSE 0x83 + +// Wait for CX:DX microseconds +void +handle_1586(struct bregs *regs) +{ + // Use the rtc to wait for the specified time. + u8 statusflag = 0; + u32 count = (regs->cx << 16) | regs->dx; + int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); + if (ret) { + set_code_invalid(regs, RET_ECLOCKINUSE); + return; + } + while (!statusflag) + yield_toirq(); + set_success(regs); +} + +// Set Interval requested. +static void +handle_158300(struct bregs *regs) +{ + int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx); + if (ret) + // Interval already set. + set_code_invalid(regs, RET_EUNSUPPORTED); + else + set_success(regs); +} + +// Clear interval requested +static void +handle_158301(struct bregs *regs) +{ + clear_usertimer(); + set_success(regs); +} + +static void +handle_1583XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); + regs->al--; +} + +void +handle_1583(struct bregs *regs) +{ + switch (regs->al) { + case 0x00: handle_158300(regs); break; + case 0x01: handle_158301(regs); break; + default: handle_1583XX(regs); break; + } +} + +#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024) + +// int70h: IRQ8 - CMOS RTC +void VISIBLE16 +handle_70(void) +{ + debug_isr(DEBUG_ISR_70); + + // Check which modes are enabled and have occurred. + u8 registerB = rtc_read(CMOS_STATUS_B); + u8 registerC = rtc_read(CMOS_STATUS_C); + + if (!(registerB & (RTC_B_PIE|RTC_B_AIE))) + goto done; + if (registerC & RTC_B_AIE) { + // Handle Alarm Interrupt. + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x4a, &br); + } + if (!(registerC & RTC_B_PIE)) + goto done; + + // Handle Periodic Interrupt. + + check_preempt(); + + if (!GET_BDA(rtc_wait_flag)) + goto done; + + // Wait Interval (Int 15, AH=83) active. + u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds. + if (time < USEC_PER_RTC) { + // Done waiting - write to specified flag byte. + struct segoff_s segoff = GET_BDA(user_wait_complete_flag); + u16 ptr_seg = segoff.seg; + u8 *ptr_far = (u8*)(segoff.offset+0); + u8 oldval = GET_FARVAR(ptr_seg, *ptr_far); + SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80); + + clear_usertimer(); + } else { + // Continue waiting. + time -= USEC_PER_RTC; + SET_BDA(user_wait_timeout, time); + } + +done: + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/code16gcc.s b/qemu/roms/seabios/src/code16gcc.s new file mode 100644 index 000000000..8f7121b87 --- /dev/null +++ b/qemu/roms/seabios/src/code16gcc.s @@ -0,0 +1 @@ +.code16gcc diff --git a/qemu/roms/seabios/src/config.h b/qemu/roms/seabios/src/config.h new file mode 100644 index 000000000..6da067d0b --- /dev/null +++ b/qemu/roms/seabios/src/config.h @@ -0,0 +1,108 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + +#include "autoconf.h" + +// Configuration definitions. + +//#define BUILD_APPNAME "QEMU" +//#define BUILD_CPUNAME8 "QEMUCPU " +//#define BUILD_APPNAME6 "QEMU " +//#define BUILD_APPNAME4 "QEMU" +#define BUILD_APPNAME "Bochs" +#define BUILD_CPUNAME8 "BOCHSCPU" +#define BUILD_APPNAME6 "BOCHS " +#define BUILD_APPNAME4 "BXPC" + +// Maximum number of map entries in the e820 map +#define BUILD_MAX_E820 32 +// Space to reserve in high-memory for tables +#define BUILD_MAX_HIGHTABLE (256*1024) +// Largest supported externaly facing drive id +#define BUILD_MAX_EXTDRIVE 16 +// Number of bytes the smbios may be and still live in the f-segment +#define BUILD_MAX_SMBIOS_FSEG 600 + +#define BUILD_MODEL_ID 0xFC +#define BUILD_SUBMODEL_ID 0x00 +#define BUILD_BIOS_REVISION 0x01 + +// Various memory addresses used by the code. +#define BUILD_STACK_ADDR 0x7000 +#define BUILD_S3RESUME_STACK_ADDR 0x1000 +#define BUILD_AP_BOOT_ADDR 0x10000 +#define BUILD_EBDA_MINIMUM 0x90000 +#define BUILD_LOWRAM_END 0xa0000 +#define BUILD_ROM_START 0xc0000 +#define BUILD_BIOS_ADDR 0xf0000 +#define BUILD_BIOS_SIZE 0x10000 +#define BUILD_EXTRA_STACK_SIZE 0x800 +// 32KB for shadow ram copying (works around emulator deficiencies) +#define BUILD_BIOS_TMP_ADDR 0x30000 +#define BUILD_SMM_INIT_ADDR 0x30000 +#define BUILD_SMM_ADDR 0xa0000 + +#define BUILD_PCIMEM_START 0xe0000000 +#define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ +#define BUILD_PCIMEM64_START 0x8000000000ULL +#define BUILD_PCIMEM64_END 0x10000000000ULL + +#define BUILD_IOAPIC_ADDR 0xfec00000 +#define BUILD_IOAPIC_ID 0 +#define BUILD_HPET_ADDRESS 0xfed00000 +#define BUILD_APIC_ADDR 0xfee00000 + +// PCI IRQS +#define BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) + +// Important real-mode segments +#define SEG_IVT 0x0000 +#define SEG_BDA 0x0040 +#define SEG_BIOS 0xf000 + +// Segment definitions in protected mode (see rombios32_gdt in misc.c) +#define SEG32_MODE32_CS (1 << 3) +#define SEG32_MODE32_DS (2 << 3) +#define SEG32_MODE16_CS (3 << 3) +#define SEG32_MODE16_DS (4 << 3) +#define SEG32_MODE16BIG_CS (5 << 3) +#define SEG32_MODE16BIG_DS (6 << 3) + +// Debugging levels. If non-zero and CONFIG_DEBUG_LEVEL is greater +// than the specified value, then the corresponding irq handler will +// report every enter event. +#define DEBUG_ISR_02 1 +#define DEBUG_HDL_05 1 +#define DEBUG_ISR_08 20 +#define DEBUG_ISR_09 9 +#define DEBUG_ISR_0e 9 +#define DEBUG_HDL_10 20 +#define DEBUG_HDL_11 2 +#define DEBUG_HDL_12 2 +#define DEBUG_HDL_13 10 +#define DEBUG_HDL_14 2 +#define DEBUG_HDL_15 9 +#define DEBUG_HDL_16 9 +#define DEBUG_HDL_17 2 +#define DEBUG_HDL_18 1 +#define DEBUG_HDL_19 1 +#define DEBUG_HDL_1a 9 +#define DEBUG_HDL_40 1 +#define DEBUG_ISR_70 9 +#define DEBUG_ISR_74 9 +#define DEBUG_ISR_75 1 +#define DEBUG_ISR_76 10 +#define DEBUG_ISR_hwpic1 5 +#define DEBUG_ISR_hwpic2 5 +#define DEBUG_HDL_smi 9 +#define DEBUG_HDL_smp 1 +#define DEBUG_HDL_pnp 1 +#define DEBUG_HDL_pmm 1 +#define DEBUG_HDL_pcibios 9 +#define DEBUG_HDL_apm 9 + +#define DEBUG_unimplemented 2 +#define DEBUG_invalid 3 +#define DEBUG_thread 2 + +#endif // config.h diff --git a/qemu/roms/seabios/src/disk.c b/qemu/roms/seabios/src/disk.c new file mode 100644 index 000000000..0e0af24b3 --- /dev/null +++ b/qemu/roms/seabios/src/disk.c @@ -0,0 +1,748 @@ +// 16bit code to access hard drives. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "hw/ata.h" // ATA_CB_DC +#include "hw/pic.h" // pic_eoi2 +#include "output.h" // debug_enter +#include "stacks.h" // call16_int +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // CDRom_locks + + +/**************************************************************** + * Return status functions + ****************************************************************/ + +static void +__disk_ret(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + if (code) + __set_code_invalid(regs, linecode, fname); + else + set_code_success(regs); +} + +static void +__disk_ret_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + if (regs->dl < EXTSTART_HD) + SET_BDA(floppy_last_status, code); + else + SET_BDA(disk_last_status, code); + __set_code_unimplemented(regs, linecode, fname); +} + +static void +__disk_stub(struct bregs *regs, int lineno, const char *fname) +{ + __warn_unimplemented(regs, lineno, fname); + __disk_ret(regs, DISK_RET_SUCCESS | (lineno << 8), fname); +} + +#define disk_ret(regs, code) \ + __disk_ret((regs), (code) | (__LINE__ << 8), __func__) +#define disk_ret_unimplemented(regs, code) \ + __disk_ret_unimplemented((regs), (code) | (__LINE__ << 8), __func__) +#define DISK_STUB(regs) \ + __disk_stub((regs), __LINE__, __func__) + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Get the cylinders/heads/sectors for the given drive. +static struct chs_s +getLCHS(struct drive_s *drive_gf) +{ + struct chs_s res = { }; + if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) { + // Emulated drive - get info from CDEmu. (It's not possible to + // populate the geometry directly in the driveid because the + // geometry is only known after the bios segment is made + // read-only). + u8 sptcyl = GET_LOW(CDEmu.chs.sptcyl); + res.cylinder = GET_LOW(CDEmu.chs.cyllow) + ((sptcyl << 2) & 0x300) + 1; + res.head = GET_LOW(CDEmu.chs.heads) + 1; + res.sector = sptcyl & 0x3f; + return res; + } + res.cylinder = GET_GLOBALFLAT(drive_gf->lchs.cylinder); + res.head = GET_GLOBALFLAT(drive_gf->lchs.head); + res.sector = GET_GLOBALFLAT(drive_gf->lchs.sector); + return res; +} + +// Perform read/write/verify using old-style chs accesses +static void noinline +basic_access(struct bregs *regs, struct drive_s *drive_gf, u16 command) +{ + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = command; + + u8 count = regs->al; + u16 cylinder = regs->ch | ((((u16)regs->cl) << 2) & 0x300); + u16 sector = regs->cl & 0x3f; + u16 head = regs->dh; + + if (count > 128 || count == 0 || sector == 0) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + dop.count = count; + + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + // sanity check on cyl heads, sec + if (cylinder >= nlc || head >= nlh || sector > nls) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + // translate lchs to lba + dop.lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls) + + (u32)sector - 1); + + dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx); + + int status = send_disk_op(&dop); + + regs->al = dop.count; + + disk_ret(regs, status); +} + +// Perform read/write/verify using new-style "int13ext" accesses. +static void noinline +extended_access(struct bregs *regs, struct drive_s *drive_gf, u16 command) +{ + struct disk_op_s dop; + struct int13ext_s *param_far = (struct int13ext_s*)(regs->si+0); + // Get lba and check. + dop.lba = GET_FARVAR(regs->ds, param_far->lba); + dop.command = command; + dop.drive_gf = drive_gf; + if (dop.lba >= GET_GLOBALFLAT(drive_gf->sectors)) { + warn_invalid(regs); + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + dop.buf_fl = SEGOFF_TO_FLATPTR(GET_FARVAR(regs->ds, param_far->data)); + dop.count = GET_FARVAR(regs->ds, param_far->count); + if (! dop.count) { + // Nothing to do. + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + + int status = send_disk_op(&dop); + + SET_FARVAR(regs->ds, param_far->count, dop.count); + + disk_ret(regs, status); +} + + +/**************************************************************** + * Hard Drive functions + ****************************************************************/ + +// disk controller reset +static void +disk_1300(struct bregs *regs, struct drive_s *drive_gf) +{ + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_RESET; + dop.count = 0; + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// read disk status +static void +disk_1301(struct bregs *regs, struct drive_s *drive_gf) +{ + u8 v; + if (regs->dl < EXTSTART_HD) + // Floppy + v = GET_BDA(floppy_last_status); + else + v = GET_BDA(disk_last_status); + regs->ah = v; + set_cf(regs, v); + // XXX - clear disk_last_status? +} + +// read disk sectors +static void +disk_1302(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_READ); +} + +// write disk sectors +static void +disk_1303(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_WRITE); +} + +// verify disk sectors +static void +disk_1304(struct bregs *regs, struct drive_s *drive_gf) +{ + basic_access(regs, drive_gf, CMD_VERIFY); +} + +// format disk track +static void noinline +disk_1305(struct bregs *regs, struct drive_s *drive_gf) +{ + debug_stub(regs); + + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + u8 count = regs->al; + u8 cylinder = regs->ch; + u8 head = regs->dh; + + if (cylinder >= nlc || head >= nlh || count == 0 || count > nls) { + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_FORMAT; + dop.lba = (((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nls; + dop.count = count; + dop.buf_fl = MAKE_FLATPTR(regs->es, regs->bx); + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// read disk drive parameters +static void noinline +disk_1308(struct bregs *regs, struct drive_s *drive_gf) +{ + // Get logical geometry from table + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + nlc--; + nlh--; + u8 count; + if (regs->dl < EXTSTART_HD) { + // Floppy + count = GET_GLOBAL(FloppyCount); + + if (CONFIG_CDROM_EMU && drive_gf == GET_GLOBAL(cdemu_drive_gf)) + regs->bx = GET_LOW(CDEmu.media) * 2; + else + regs->bx = GET_GLOBALFLAT(drive_gf->floppy_type); + + // set es & di to point to 11 byte diskette param table in ROM + regs->es = SEG_BIOS; + regs->di = (u32)&diskette_param_table2; + } else if (regs->dl < EXTSTART_CD) { + // Hard drive + count = GET_BDA(hdcount); + nlc--; // last sector reserved + } else { + // Not supported on CDROM + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + if (CONFIG_CDROM_EMU && GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); + if (((emudrive ^ regs->dl) & 0x80) == 0) + // Note extra drive due to emulation. + count++; + if (regs->dl < EXTSTART_HD && count > 2) + // Max of two floppy drives. + count = 2; + } + + regs->al = 0; + regs->ch = nlc & 0xff; + regs->cl = ((nlc >> 2) & 0xc0) | (nls & 0x3f); + regs->dh = nlh; + + disk_ret(regs, DISK_RET_SUCCESS); + regs->dl = count; +} + +// initialize drive parameters +static void +disk_1309(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// seek to specified cylinder +static void +disk_130c(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// alternate disk reset +static void +disk_130d(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// check drive ready +static void +disk_1310(struct bregs *regs, struct drive_s *drive_gf) +{ + // should look at 40:8E also??? + + struct disk_op_s dop; + dop.drive_gf = drive_gf; + dop.command = CMD_ISREADY; + dop.count = 0; + int status = send_disk_op(&dop); + disk_ret(regs, status); +} + +// recalibrate +static void +disk_1311(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// controller internal diagnostic +static void +disk_1314(struct bregs *regs, struct drive_s *drive_gf) +{ + DISK_STUB(regs); +} + +// read disk drive size +static void noinline +disk_1315(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); + if (regs->dl < EXTSTART_HD || regs->dl >= EXTSTART_CD) { + // Floppy or cdrom + regs->ah = 1; + return; + } + // Hard drive + + // Get logical geometry from table + struct chs_s chs = getLCHS(drive_gf); + u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector; + + // Compute sector count seen by int13 + u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nls; + regs->cx = lba >> 16; + regs->dx = lba & 0xffff; + regs->ah = 3; // hard disk accessible +} + +static void +disk_1316(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl >= EXTSTART_HD) { + // Hard drive + disk_ret(regs, DISK_RET_EPARAM); + return; + } + disk_ret(regs, DISK_RET_ECHANGED); +} + +// IBM/MS installation check +static void +disk_1341(struct bregs *regs, struct drive_s *drive_gf) +{ + regs->bx = 0xaa55; // install check + regs->cx = 0x0007; // ext disk access and edd, removable supported + disk_ret(regs, DISK_RET_SUCCESS); + regs->ah = 0x30; // EDD 3.0 +} + +// IBM/MS extended read +static void +disk_1342(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_READ); +} + +// IBM/MS extended write +static void +disk_1343(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_WRITE); +} + +// IBM/MS verify +static void +disk_1344(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_VERIFY); +} + +// lock +static void +disk_134500(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks == 0xff) { + regs->al = 1; + disk_ret(regs, DISK_RET_ETOOMANYLOCKS); + return; + } + SET_LOW(CDRom_locks[cdid], locks + 1); + regs->al = 1; + disk_ret(regs, DISK_RET_SUCCESS); +} + +// unlock +static void +disk_134501(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks == 0x00) { + regs->al = 0; + disk_ret(regs, DISK_RET_ENOTLOCKED); + return; + } + locks--; + SET_LOW(CDRom_locks[cdid], locks); + regs->al = (locks ? 1 : 0); + disk_ret(regs, DISK_RET_SUCCESS); +} + +// status +static void +disk_134502(struct bregs *regs, struct drive_s *drive_gf) +{ + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + regs->al = (locks ? 1 : 0); + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_1345XX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret_unimplemented(regs, DISK_RET_EPARAM); +} + +// IBM/MS lock/unlock drive +static void +disk_1345(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Always success for HD + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + + switch (regs->al) { + case 0x00: disk_134500(regs, drive_gf); break; + case 0x01: disk_134501(regs, drive_gf); break; + case 0x02: disk_134502(regs, drive_gf); break; + default: disk_1345XX(regs, drive_gf); break; + } +} + +// IBM/MS eject media +static void noinline +disk_1346(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Volume Not Removable + disk_ret(regs, DISK_RET_ENOTREMOVABLE); + return; + } + + int cdid = regs->dl - EXTSTART_CD; + u8 locks = GET_LOW(CDRom_locks[cdid]); + if (locks != 0) { + disk_ret(regs, DISK_RET_ELOCKED); + return; + } + + // FIXME should handle 0x31 no media in device + // FIXME should handle 0xb5 valid request failed + + // Call removable media eject + struct bregs br; + memset(&br, 0, sizeof(br)); + br.ah = 0x52; + br.dl = regs->dl; + call16_int(0x15, &br); + + if (br.ah || br.flags & F_CF) { + disk_ret(regs, DISK_RET_ELOCKED); + return; + } + disk_ret(regs, DISK_RET_SUCCESS); +} + +// IBM/MS extended seek +static void +disk_1347(struct bregs *regs, struct drive_s *drive_gf) +{ + extended_access(regs, drive_gf, CMD_SEEK); +} + +// IBM/MS get drive parameters +static void +disk_1348(struct bregs *regs, struct drive_s *drive_gf) +{ + int ret = fill_edd(regs->ds, (void*)(regs->si+0), drive_gf); + disk_ret(regs, ret); +} + +// IBM/MS extended media change +static void +disk_1349(struct bregs *regs, struct drive_s *drive_gf) +{ + if (regs->dl < EXTSTART_CD) { + // Always success for HD + disk_ret(regs, DISK_RET_SUCCESS); + return; + } + set_invalid(regs); + // always send changed ?? + regs->ah = DISK_RET_ECHANGED; +} + +static void +disk_134e01(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e03(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e04(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134e06(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_SUCCESS); +} + +static void +disk_134eXX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret(regs, DISK_RET_EPARAM); +} + +// IBM/MS set hardware configuration +static void +disk_134e(struct bregs *regs, struct drive_s *drive_gf) +{ + switch (regs->al) { + case 0x01: disk_134e01(regs, drive_gf); break; + case 0x03: disk_134e03(regs, drive_gf); break; + case 0x04: disk_134e04(regs, drive_gf); break; + case 0x06: disk_134e06(regs, drive_gf); break; + default: disk_134eXX(regs, drive_gf); break; + } +} + +static void +disk_13XX(struct bregs *regs, struct drive_s *drive_gf) +{ + disk_ret_unimplemented(regs, DISK_RET_EPARAM); +} + +static void +disk_13(struct bregs *regs, struct drive_s *drive_gf) +{ + //debug_stub(regs); + + // clear completion flag + SET_BDA(disk_interrupt_flag, 0); + + switch (regs->ah) { + case 0x00: disk_1300(regs, drive_gf); break; + case 0x01: disk_1301(regs, drive_gf); break; + case 0x02: disk_1302(regs, drive_gf); break; + case 0x03: disk_1303(regs, drive_gf); break; + case 0x04: disk_1304(regs, drive_gf); break; + case 0x05: disk_1305(regs, drive_gf); break; + case 0x08: disk_1308(regs, drive_gf); break; + case 0x09: disk_1309(regs, drive_gf); break; + case 0x0c: disk_130c(regs, drive_gf); break; + case 0x0d: disk_130d(regs, drive_gf); break; + case 0x10: disk_1310(regs, drive_gf); break; + case 0x11: disk_1311(regs, drive_gf); break; + case 0x14: disk_1314(regs, drive_gf); break; + case 0x15: disk_1315(regs, drive_gf); break; + case 0x16: disk_1316(regs, drive_gf); break; + case 0x41: disk_1341(regs, drive_gf); break; + case 0x42: disk_1342(regs, drive_gf); break; + case 0x43: disk_1343(regs, drive_gf); break; + case 0x44: disk_1344(regs, drive_gf); break; + case 0x45: disk_1345(regs, drive_gf); break; + case 0x46: disk_1346(regs, drive_gf); break; + case 0x47: disk_1347(regs, drive_gf); break; + case 0x48: disk_1348(regs, drive_gf); break; + case 0x49: disk_1349(regs, drive_gf); break; + case 0x4e: disk_134e(regs, drive_gf); break; + default: disk_13XX(regs, drive_gf); break; + } +} + +static void +floppy_13(struct bregs *regs, struct drive_s *drive_gf) +{ + // Only limited commands are supported on floppies. + switch (regs->ah) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x08: + case 0x15: + case 0x16: + disk_13(regs, drive_gf); + break; + default: disk_13XX(regs, drive_gf); break; + } +} + +// ElTorito - Terminate disk emu +static void +cdemu_134b(struct bregs *regs) +{ + memcpy_far(regs->ds, (void*)(regs->si+0), SEG_LOW, &CDEmu, sizeof(CDEmu)); + + // If we have to terminate emulation + if (regs->al == 0x00) { + // FIXME ElTorito Various. Should be handled accordingly to spec + SET_LOW(CDEmu.media, 0x00); // bye bye + + // XXX - update floppy/hd count. + } + + disk_ret(regs, DISK_RET_SUCCESS); +} + + +/**************************************************************** + * Entry points + ****************************************************************/ + +static void +handle_legacy_disk(struct bregs *regs, u8 extdrive) +{ + if (! CONFIG_DRIVES) { + // XXX - support handle_1301 anyway? + disk_ret(regs, DISK_RET_EPARAM); + return; + } + + if (extdrive < EXTSTART_HD) { + struct drive_s *drive_gf = getDrive(EXTTYPE_FLOPPY, extdrive); + if (!drive_gf) + goto fail; + floppy_13(regs, drive_gf); + return; + } + + struct drive_s *drive_gf; + if (extdrive >= EXTSTART_CD) + drive_gf = getDrive(EXTTYPE_CD, extdrive - EXTSTART_CD); + else + drive_gf = getDrive(EXTTYPE_HD, extdrive - EXTSTART_HD); + if (!drive_gf) + goto fail; + disk_13(regs, drive_gf); + return; + +fail: + // XXX - support 1301/1308/1315 anyway? + disk_ret(regs, DISK_RET_EPARAM); +} + +void VISIBLE16 +handle_40(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_40); + handle_legacy_disk(regs, regs->dl); +} + +// INT 13h Fixed Disk Services Entry Point +void VISIBLE16 +handle_13(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_13); + u8 extdrive = regs->dl; + + if (CONFIG_CDROM_EMU) { + if (regs->ah == 0x4b) { + cdemu_134b(regs); + return; + } + if (GET_LOW(CDEmu.media)) { + u8 emudrive = GET_LOW(CDEmu.emulated_drive); + if (extdrive == emudrive) { + // Access to an emulated drive. + struct drive_s *cdemu_gf = GET_GLOBAL(cdemu_drive_gf); + if (regs->ah > 0x16) { + // Only old-style commands supported. + disk_13XX(regs, cdemu_gf); + return; + } + disk_13(regs, cdemu_gf); + return; + } + if (extdrive < EXTSTART_CD && ((emudrive ^ extdrive) & 0x80) == 0) + // Adjust id to make room for emulated drive. + extdrive--; + } + } + handle_legacy_disk(regs, extdrive); +} + +// record completion in BIOS task complete flag +void VISIBLE16 +handle_76(void) +{ + debug_isr(DEBUG_ISR_76); + SET_BDA(disk_interrupt_flag, 0xff); + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/entryfuncs.S b/qemu/roms/seabios/src/entryfuncs.S new file mode 100644 index 000000000..7368bb6d5 --- /dev/null +++ b/qemu/roms/seabios/src/entryfuncs.S @@ -0,0 +1,165 @@ +// Macros for entering C code +// +// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +/**************************************************************** + * Macros for save and restore of 'struct bregs' registers + ****************************************************************/ + +#define PUSHBREGS_size 32 + + // Save registers (matches struct bregs) to stack + .macro PUSHBREGS + pushl %eax + pushl %ecx + pushl %edx + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + pushw %es + pushw %ds + .endm + + // Restore registers (from struct bregs) from stack + .macro POPBREGS + popw %ds + popw %es + popl %edi + popl %esi + popl %ebp + popl %ebx + popl %edx + popl %ecx + popl %eax + .endm + + // Save registers to struct bregs at %ds:%eax. The caller + // should "pushw %ds ; pushl %eax" prior to calling - this macro + // will pop them off. + .macro SAVEBREGS_POP_DSEAX + popl BREGS_eax(%eax) + popw BREGS_ds(%eax) + movl %edi, BREGS_edi(%eax) + movl %esi, BREGS_esi(%eax) + movl %ebp, BREGS_ebp(%eax) + movl %ebx, BREGS_ebx(%eax) + movl %edx, BREGS_edx(%eax) + movl %ecx, BREGS_ecx(%eax) + movw %es, BREGS_es(%eax) + .endm + + // Restore registers from struct bregs at %ds:%eax + .macro RESTOREBREGS_DSEAX + movl BREGS_edi(%eax), %edi + movl BREGS_esi(%eax), %esi + movl BREGS_ebp(%eax), %ebp + movl BREGS_ebx(%eax), %ebx + movl BREGS_edx(%eax), %edx + movl BREGS_ecx(%eax), %ecx + movw BREGS_es(%eax), %es + pushl BREGS_eax(%eax) + movw BREGS_ds(%eax), %ds + popl %eax + .endm + + +/**************************************************************** + * Entry macros + ****************************************************************/ + + // Call a C function - this does the minimal work necessary to + // call into C. It sets up %ds, backs up %es, and backs up + // those registers that are call clobbered by the C compiler. + .macro ENTRY cfunc + cli // In case something far-calls instead of using "int" + cld + pushl %eax // Save registers clobbered by C code + pushl %ecx + pushl %edx + pushw %es + pushw %ds + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp + calll \cfunc + popl %esp // Restore %esp (including high bits) + popw %ds // Restore registers saved above + popw %es + popl %edx + popl %ecx + popl %eax + .endm + + // Call a C function with current register list as an + // argument. This backs up the registers and sets %eax + // to point to the backup. On return, the registers are + // restored from the structure. + .macro ENTRY_ARG cfunc + cli + cld + PUSHBREGS + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + movl %esp, %ebx // Backup %esp, then zero high bits + movzwl %sp, %esp + movl %esp, %eax // First arg is pointer to struct bregs + calll \cfunc + movl %ebx, %esp // Restore %esp (including high bits) + POPBREGS + .endm + + // As above, but get calling function from stack. + .macro ENTRY_ARG_ST + cli + cld + pushl %ecx + pushl %edx + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + pushw %es + pushw %ds + movw %ss, %cx // Move %ss to %ds + movw %cx, %ds + movl %esp, %ebx // Backup %esp, then zero high bits + movzwl %sp, %esp + movl 28(%esp), %ecx // Get calling function + movl %eax, 28(%esp) // Save %eax + movl %esp, %eax // First arg is pointer to struct bregs + calll *%ecx + movl %ebx, %esp // Restore %esp (including high bits) + POPBREGS + .endm + + // Same as ENTRY_ARG, but don't mangle %esp + .macro ENTRY_ARG_ESP cfunc + cli + cld + PUSHBREGS + movw %ss, %ax // Move %ss to %ds + movw %ax, %ds + movl %esp, %eax // First arg is pointer to struct bregs + calll \cfunc + POPBREGS + .endm + + // Reset stack, transition to 32bit mode, and call a C function. + .macro ENTRY_INTO32 cfunc + xorw %dx, %dx + movw %dx, %ss + movl $ BUILD_STACK_ADDR , %esp + movl $ \cfunc , %edx + jmp transition32 + .endm + + // Declare a function + .macro DECLFUNC func + .section .text.asm.\func + .global \func + .endm diff --git a/qemu/roms/seabios/src/farptr.h b/qemu/roms/seabios/src/farptr.h new file mode 100644 index 000000000..0a4db68bf --- /dev/null +++ b/qemu/roms/seabios/src/farptr.h @@ -0,0 +1,208 @@ +// Code to access multiple segments within gcc. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __FARPTR_H +#define __FARPTR_H + +#include "x86.h" // insb + +// Dummy definitions used to make sure gcc understands dependencies +// between SET_SEG and GET/READ/WRITE_SEG macros. +extern u16 __segment_ES, __segment_CS, __segment_DS, __segment_SS; +extern u16 __segment_FS, __segment_GS; + +// Low level macros for reading/writing memory via a segment selector. +#define READ8_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movb %%" #SEG ":%1, %b0" : "=Qi"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ16_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movw %%" #SEG ":%1, %w0" : "=ri"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ32_SEG(prefix, SEG, value, var) \ + __asm__(prefix "movl %%" #SEG ":%1, %0" : "=ri"(value) \ + : "m"(var), "m"(__segment_ ## SEG)) +#define READ64_SEG(prefix, SEG, value, var) do { \ + union u64_u32_u __value; \ + union u64_u32_u *__r64_ptr = (union u64_u32_u *)&(var); \ + READ32_SEG(prefix, SEG, __value.lo, __r64_ptr->lo); \ + READ32_SEG(prefix, SEG, __value.hi, __r64_ptr->hi); \ + *(u64*)&(value) = __value.val; \ + } while (0) +#define WRITE8_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movb %b1, %%" #SEG ":%0" : "=m"(var) \ + : "Q"(value), "m"(__segment_ ## SEG)) +#define WRITE16_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movw %w1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) +#define WRITE32_SEG(prefix, SEG, var, value) \ + __asm__(prefix "movl %1, %%" #SEG ":%0" : "=m"(var) \ + : "r"(value), "m"(__segment_ ## SEG)) +#define WRITE64_SEG(prefix, SEG, var, value) do { \ + union u64_u32_u __value; \ + union u64_u32_u *__w64_ptr = (union u64_u32_u *)&(var); \ + typeof(var) __value_tmp = (value); \ + __value.val = *(u64*)&__value_tmp; \ + WRITE32_SEG(prefix, SEG, __w64_ptr->lo, __value.lo); \ + WRITE32_SEG(prefix, SEG, __w64_ptr->hi, __value.hi); \ + } while (0) + +// Macros for automatically choosing the appropriate memory size +// access method. +extern void __force_link_error__unknown_type(void); + +#define __GET_VAR(prefix, seg, var) ({ \ + typeof(var) __val; \ + if (sizeof(__val) == 1) \ + READ8_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 2) \ + READ16_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 4) \ + READ32_SEG(prefix, seg, __val, var); \ + else if (sizeof(__val) == 8) \ + READ64_SEG(prefix, seg, __val, var); \ + else \ + __force_link_error__unknown_type(); \ + __val; }) + +#define __SET_VAR(prefix, seg, var, val) do { \ + if (sizeof(var) == 1) \ + WRITE8_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 2) \ + WRITE16_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 4) \ + WRITE32_SEG(prefix, seg, var, (val)); \ + else if (sizeof(var) == 8) \ + WRITE64_SEG(prefix, seg, var, (val)); \ + else \ + __force_link_error__unknown_type(); \ + } while (0) + +#define DECL_SEGFUNCS(SEG) \ +static inline void __set_seg_##SEG(u16 seg) { \ + __asm__("movw %w1, %%" #SEG : "=m"(__segment_##SEG) \ + : "rm"(seg)); \ +} \ +static inline u16 __get_seg_##SEG(void) { \ + u16 res; \ + __asm__("movw %%" #SEG ", %w0" : "=rm"(res) \ + : "m"(__segment_##SEG)); \ + return res; \ +} +DECL_SEGFUNCS(CS) +DECL_SEGFUNCS(DS) +DECL_SEGFUNCS(ES) +DECL_SEGFUNCS(FS) +DECL_SEGFUNCS(GS) +DECL_SEGFUNCS(SS) + +// Low level macros for getting/setting a segment register. +#define __SET_SEG(SEG, value) \ + __set_seg_##SEG(value) +#define __GET_SEG(SEG) \ + __get_seg_##SEG() + +// Macros for accessing a variable in another segment. (They +// automatically update the %es segment and then make the appropriate +// access.) +#define __GET_FARVAR(seg, var) ({ \ + SET_SEG(ES, (seg)); \ + GET_VAR(ES, (var)); }) +#define __SET_FARVAR(seg, var, val) do { \ + typeof(var) __sfv_val = (val); \ + SET_SEG(ES, (seg)); \ + SET_VAR(ES, (var), __sfv_val); \ + } while (0) + +// Macros for accesssing a 32bit flat mode pointer from 16bit real +// mode. (They automatically update the %es segment, break the +// pointer into segment/offset, and then make the access.) +#define __GET_FLATPTR(ptr) ({ \ + typeof(&(ptr)) __ptr = &(ptr); \ + GET_FARVAR(FLATPTR_TO_SEG(__ptr) \ + , *(typeof(__ptr))FLATPTR_TO_OFFSET(__ptr)); }) +#define __SET_FLATPTR(ptr, val) do { \ + typeof (&(ptr)) __ptr = &(ptr); \ + SET_FARVAR(FLATPTR_TO_SEG(__ptr) \ + , *(typeof(__ptr))FLATPTR_TO_OFFSET(__ptr) \ + , (val)); \ + } while (0) + +// Macros for converting to/from 32bit flat mode pointers to their +// equivalent 16bit segment/offset values. +#define FLATPTR_TO_SEG(p) (((u32)(p)) >> 4) +#define FLATPTR_TO_OFFSET(p) (((u32)(p)) & 0xf) +#define MAKE_FLATPTR(seg,off) ((void*)(((u32)(seg)<<4)+(u32)(off))) + + +#if MODESEGMENT == 1 + +// Definitions when using segmented mode. +#define GET_FARVAR(seg, var) __GET_FARVAR((seg), (var)) +#define SET_FARVAR(seg, var, val) __SET_FARVAR((seg), (var), (val)) +#define GET_VAR(seg, var) __GET_VAR("", seg, (var)) +#define SET_VAR(seg, var, val) __SET_VAR("", seg, (var), (val)) +#define SET_SEG(SEG, value) __SET_SEG(SEG, (value)) +#define GET_SEG(SEG) __GET_SEG(SEG) +#define GET_FLATPTR(ptr) __GET_FLATPTR(ptr) +#define SET_FLATPTR(ptr, val) __SET_FLATPTR((ptr), (val)) + +static inline void insb_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insb(port, (u8*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void insw_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insw(port, (u16*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void insl_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + insl(port, (u32*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsb_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsb(port, (u8*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsw_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsw(port, (u16*)FLATPTR_TO_OFFSET(ptr_fl), count); +} +static inline void outsl_fl(u16 port, void *ptr_fl, u16 count) { + SET_SEG(ES, FLATPTR_TO_SEG(ptr_fl)); + outsl(port, (u32*)FLATPTR_TO_OFFSET(ptr_fl), count); +} + +#else + +// In 32-bit flat mode there is no need to mess with the segments. +#define GET_FARVAR(seg, var) \ + (*((typeof(&(var)))MAKE_FLATPTR((seg), &(var)))) +#define SET_FARVAR(seg, var, val) \ + do { GET_FARVAR((seg), (var)) = (val); } while (0) +#define GET_VAR(seg, var) (var) +#define SET_VAR(seg, var, val) do { (var) = (val); } while (0) +#define SET_SEG(SEG, value) ((void)(value)) +#define GET_SEG(SEG) 0 +#define GET_FLATPTR(ptr) (ptr) +#define SET_FLATPTR(ptr, val) do { (ptr) = (val); } while (0) + +#define insb_fl(port, ptr_fl, count) insb(port, ptr_fl, count) +#define insw_fl(port, ptr_fl, count) insw(port, ptr_fl, count) +#define insl_fl(port, ptr_fl, count) insl(port, ptr_fl, count) +#define outsb_fl(port, ptr_fl, count) outsb(port, ptr_fl, count) +#define outsw_fl(port, ptr_fl, count) outsw(port, ptr_fl, count) +#define outsl_fl(port, ptr_fl, count) outsl(port, ptr_fl, count) + +#endif + +#define SEGOFF(s,o) ({struct segoff_s __so; __so.offset=(o); __so.seg=(s); __so;}) + +static inline struct segoff_s FLATPTR_TO_SEGOFF(void *p) { + return SEGOFF(FLATPTR_TO_SEG(p), FLATPTR_TO_OFFSET(p)); +} +static inline void *SEGOFF_TO_FLATPTR(struct segoff_s so) { + return MAKE_FLATPTR(so.seg, so.offset); +} + +#endif // farptr.h diff --git a/qemu/roms/seabios/src/font.c b/qemu/roms/seabios/src/font.c new file mode 100644 index 000000000..67e5d46b7 --- /dev/null +++ b/qemu/roms/seabios/src/font.c @@ -0,0 +1,139 @@ +#include "types.h" // u8 + +// Character Font for 320x200 & 640x200 Graphics (lower 128 characters) + +/* + * This font comes from the fntcol16.zip package (c) by Joseph Gil + * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * This font is public domain + */ +u8 vgafont8[128*8] VARFSEGFIXED(0xfa6e) = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, +}; diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl new file mode 100644 index 000000000..0f3e83b14 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-cpu-hotplug.dsl @@ -0,0 +1,78 @@ +/**************************************************************** + * CPU hotplug + ****************************************************************/ + +Scope(\_SB) { + /* Objects filled in by run-time generated SSDT */ + External(NTFY, MethodObj) + External(CPON, PkgObj) + + /* Methods called by run-time generated SSDT Processor objects */ + Method(CPMA, 1, NotSerialized) { + // _MAT method - create an madt apic buffer + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + // Local1 = Buffer (in madt apic form) to return + Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1) + // Update the processor id, lapic id, and enable/disable status + Store(Arg0, Index(Local1, 2)) + Store(Arg0, Index(Local1, 3)) + Store(Local0, Index(Local1, 4)) + Return (Local1) + } + Method(CPST, 1, NotSerialized) { + // _STA method - return ON status of cpu + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + If (Local0) { + Return (0xF) + } Else { + Return (0x0) + } + } + Method(CPEJ, 2, NotSerialized) { + // _EJ0 method - eject callback + Sleep(200) + } + + /* CPU hotplug notify method */ + OperationRegion(PRST, SystemIO, 0xaf00, 32) + Field(PRST, ByteAcc, NoLock, Preserve) { + PRS, 256 + } + Method(PRSC, 0) { + // Local5 = active cpu bitmap + Store(PRS, Local5) + // Local2 = last read byte from bitmap + Store(Zero, Local2) + // Local0 = Processor ID / APIC ID iterator + Store(Zero, Local0) + While (LLess(Local0, SizeOf(CPON))) { + // Local1 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Local0)), Local1) + If (And(Local0, 0x07)) { + // Shift down previously read bitmap byte + ShiftRight(Local2, 1, Local2) + } Else { + // Read next byte from cpu bitmap + Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2) + } + // Local3 = active state for this cpu + Store(And(Local2, 1), Local3) + + If (LNotEqual(Local1, Local3)) { + // State change - update CPON with new state + Store(Local3, Index(CPON, Local0)) + // Do CPU notify + If (LEqual(Local3, 1)) { + NTFY(Local0, 1) + } Else { + NTFY(Local0, 3) + } + } + Increment(Local0) + } + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl new file mode 100644 index 000000000..276321f61 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-dbug.dsl @@ -0,0 +1,26 @@ +/**************************************************************** + * Debugging + ****************************************************************/ + +Scope(\) { + /* Debug Output */ + OperationRegion(DBG, SystemIO, 0x0402, 0x01) + Field(DBG, ByteAcc, NoLock, Preserve) { + DBGB, 8, + } + + /* Debug method - use this method to send output to the QEMU + * BIOS debug port. This method handles strings, integers, + * and buffers. For example: DBUG("abc") DBUG(0x123) */ + Method(DBUG, 1) { + ToHexString(Arg0, Local0) + ToBuffer(Local0, Local0) + Subtract(SizeOf(Local0), 1, Local1) + Store(Zero, Local2) + While (LLess(Local2, Local1)) { + Store(DerefOf(Index(Local0, Local2)), DBGB) + Increment(Local2) + } + Store(0x0A, DBGB) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl new file mode 100644 index 000000000..f33e52795 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-hpet.dsl @@ -0,0 +1,36 @@ +/**************************************************************** + * HPET + ****************************************************************/ + +Scope(\_SB) { + Device(HPET) { + Name(_HID, EISAID("PNP0103")) + Name(_UID, 0) + OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400) + Field(HPTM, DWordAcc, Lock, Preserve) { + VEND, 32, + PRD, 32, + } + Method(_STA, 0, NotSerialized) { + Store(VEND, Local0) + Store(PRD, Local1) + ShiftRight(Local0, 16, Local0) + If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) { + Return (0x0) + } + If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) { + Return (0x0) + } + Return (0x0F) + } + Name(_CRS, ResourceTemplate() { +#if 0 /* This makes WinXP BSOD for not yet figured reasons. */ + IRQNoFlags() {2, 8} +#endif + Memory32Fixed(ReadOnly, + 0xFED00000, // Address Base + 0x00000400, // Address Length + ) + }) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl new file mode 100644 index 000000000..23761dbba --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-isa.dsl @@ -0,0 +1,102 @@ +/* Common legacy ISA style devices. */ +Scope(\_SB.PCI0.ISA) { + + Device(RTC) { + Name(_HID, EisaId("PNP0B00")) + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0070, 0x0070, 0x10, 0x02) + IRQNoFlags() { 8 } + IO(Decode16, 0x0072, 0x0072, 0x02, 0x06) + }) + } + + Device(KBD) { + Name(_HID, EisaId("PNP0303")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0060, 0x0060, 0x01, 0x01) + IO(Decode16, 0x0064, 0x0064, 0x01, 0x01) + IRQNoFlags() { 1 } + }) + } + + Device(MOU) { + Name(_HID, EisaId("PNP0F13")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IRQNoFlags() { 12 } + }) + } + + Device(FDC0) { + Name(_HID, EisaId("PNP0700")) + Method(_STA, 0, NotSerialized) { + Store(FDEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04) + IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01) + IRQNoFlags() { 6 } + DMA(Compatibility, NotBusMaster, Transfer8) { 2 } + }) + } + + Device(LPT) { + Name(_HID, EisaId("PNP0400")) + Method(_STA, 0, NotSerialized) { + Store(LPEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0378, 0x0378, 0x08, 0x08) + IRQNoFlags() { 7 } + }) + } + + Device(COM1) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x01) + Method(_STA, 0, NotSerialized) { + Store(CAEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08) + IRQNoFlags() { 4 } + }) + } + + Device(COM2) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x02) + Method(_STA, 0, NotSerialized) { + Store(CBEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08) + IRQNoFlags() { 3 } + }) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl new file mode 100644 index 000000000..d4218914f --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt-pci-crs.dsl @@ -0,0 +1,90 @@ +/* PCI CRS (current resources) definition. */ +Scope(\_SB.PCI0) { + + Name(CRES, ResourceTemplate() { + WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x00FF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0100, // Address Length + ,, ) + IO(Decode16, + 0x0CF8, // Address Range Minimum + 0x0CF8, // Address Range Maximum + 0x01, // Address Alignment + 0x08, // Address Length + ) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x0CF7, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0CF8, // Address Length + ,, , TypeStatic) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0D00, // Address Range Minimum + 0xFFFF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0xF300, // Address Length + ,, , TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x000A0000, // Address Range Minimum + 0x000BFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x00020000, // Address Length + ,, , AddressRangeMemory, TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0xE0000000, // Address Range Minimum + 0xFEBFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x1EC00000, // Address Length + ,, PW32, AddressRangeMemory, TypeStatic) + }) + + Name(CR64, ResourceTemplate() { + QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x8000000000, // Address Range Minimum + 0xFFFFFFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x8000000000, // Address Length + ,, PW64, AddressRangeMemory, TypeStatic) + }) + + Method(_CRS, 0) { + /* Fields provided by dynamically created ssdt */ + External(P0S, IntObj) + External(P0E, IntObj) + External(P1V, IntObj) + External(P1S, BuffObj) + External(P1E, BuffObj) + External(P1L, BuffObj) + + /* fixup 32bit pci io window */ + CreateDWordField(CRES, \_SB.PCI0.PW32._MIN, PS32) + CreateDWordField(CRES, \_SB.PCI0.PW32._MAX, PE32) + CreateDWordField(CRES, \_SB.PCI0.PW32._LEN, PL32) + Store(P0S, PS32) + Store(P0E, PE32) + Store(Add(Subtract(P0E, P0S), 1), PL32) + + If (LEqual(P1V, Zero)) { + Return (CRES) + } + + /* fixup 64bit pci io window */ + CreateQWordField(CR64, \_SB.PCI0.PW64._MIN, PS64) + CreateQWordField(CR64, \_SB.PCI0.PW64._MAX, PE64) + CreateQWordField(CR64, \_SB.PCI0.PW64._LEN, PL64) + Store(P1S, PS64) + Store(P1E, PE64) + Store(P1L, PL64) + /* add window and return result */ + ConcatenateResTemplate(CRES, CR64, Local0) + Return (Local0) + } +} diff --git a/qemu/roms/seabios/src/fw/acpi-dsdt.dsl b/qemu/roms/seabios/src/fw/acpi-dsdt.dsl new file mode 100644 index 000000000..3556dcaf1 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi-dsdt.dsl @@ -0,0 +1,342 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ACPI_EXTRACT_ALL_CODE AmlCode + +DefinitionBlock ( + "acpi-dsdt.aml", // Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00020000) + OperationRegion(PCIC, PCI_Config, Zero, 0x4) + Field(PCIC, DWordAcc, NoLock, Preserve) { + VEND, 32 + } + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + If (LEqual(VEND, 0x1001b36)) { + Return (0x03) // QXL + } Else { + Return (0x00) + } + } + } + } + + +/**************************************************************** + * PIIX4 PM + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(PX13) { + Name(_ADR, 0x00010003) + OperationRegion(P13C, PCI_Config, 0x00, 0xff) + } + } + + +/**************************************************************** + * PIIX3 ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(ISA) { + Name(_ADR, 0x00010000) + + /* PIIX PCI to ISA irq remapping */ + OperationRegion(P40C, PCI_Config, 0x60, 0x04) + + /* enable bits */ + Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) { + Offset(0x5f), + , 7, + LPEN, 1, // LPT + Offset(0x67), + , 3, + CAEN, 1, // COM1 + , 3, + CBEN, 1, // COM2 + } + Name(FDEN, 1) + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + Scope(\_SB.PCI0) { + OperationRegion(PCST, SystemIO, 0xae00, 0x08) + Field(PCST, DWordAcc, NoLock, WriteAsZeros) { + PCIU, 32, + PCID, 32, + } + + OperationRegion(SEJ, SystemIO, 0xae08, 0x04) + Field(SEJ, DWordAcc, NoLock, WriteAsZeros) { + B0EJ, 32, + } + + /* Methods called by bulk generated PCI devices below */ + + /* Methods called by hotplug devices */ + Method(PCEJ, 1, NotSerialized) { + // _EJ0 method - eject callback + Store(ShiftLeft(1, Arg0), B0EJ) + } + + /* Hotplug notification method supplied by SSDT */ + External(\_SB.PCI0.PCNT, MethodObj) + + /* PCI hotplug notify method */ + Method(PCNF, 0) { + // Local0 = iterator + Store(Zero, Local0) + While (LLess(Local0, 31)) { + Increment(Local0) + If (And(PCIU, ShiftLeft(1, Local0))) { + PCNT(Local0, 1) + } + If (And(PCID, ShiftLeft(1, Local0))) { + PCNT(Local0, 3) + } + } + } + } + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + Scope(\_SB) { + Scope(PCI0) { + Name(_PRT, Package() { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + +#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC) +#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB) + + prt_slot0(0x0000), + /* Device 1 is power mgmt device, and can only use irq 9 */ + prt_slot(0x0001, LNKS, LNKB, LNKC, LNKD), + prt_slot2(0x0002), + prt_slot3(0x0003), + prt_slot0(0x0004), + prt_slot1(0x0005), + prt_slot2(0x0006), + prt_slot3(0x0007), + prt_slot0(0x0008), + prt_slot1(0x0009), + prt_slot2(0x000a), + prt_slot3(0x000b), + prt_slot0(0x000c), + prt_slot1(0x000d), + prt_slot2(0x000e), + prt_slot3(0x000f), + prt_slot0(0x0010), + prt_slot1(0x0011), + prt_slot2(0x0012), + prt_slot3(0x0013), + prt_slot0(0x0014), + prt_slot1(0x0015), + prt_slot2(0x0016), + prt_slot3(0x0017), + prt_slot0(0x0018), + prt_slot1(0x0019), + prt_slot2(0x001a), + prt_slot3(0x001b), + prt_slot0(0x001c), + prt_slot1(0x001d), + prt_slot2(0x001e), + prt_slot3(0x001f), + }) + } + + Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) { + PRQ0, 8, + PRQ1, 8, + PRQ2, 8, + PRQ3, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, Serialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + If (LLess(Arg0, 0x80)) { + Store(Arg0, PRRI) + } + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQ0) + define_link(LNKB, 1, PRQ1) + define_link(LNKC, 2, PRQ2) + define_link(LNKD, 3, PRQ3) + + Device(LNKS) { + Name(_HID, EISAID("PNP0C0F")) + Name(_UID, 4) + Name(_PRS, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 9 } + }) + + // The SCI cannot be disabled and is always attached to GSI 9, + // so these are no-ops. We only need this link to override the + // polarity to active high and match the content of the MADT. + Method(_STA, 0, NotSerialized) { Return (0x0b) } + Method(_DIS, 0, NotSerialized) { } + Method(_CRS, 0, NotSerialized) { Return (_PRS) } + Method(_SRS, 1, NotSerialized) { } + } + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_E01) { + // PCI hotplug event + \_SB.PCI0.PCNF() + } + Method(_E02) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/qemu/roms/seabios/src/fw/acpi.c b/qemu/roms/seabios/src/fw/acpi.c new file mode 100644 index 000000000..ecd1adcf1 --- /dev/null +++ b/qemu/roms/seabios/src/fw/acpi.c @@ -0,0 +1,685 @@ +// Support for generating ACPI tables (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // cpu_to_le16 +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_find_init_device +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_INTERRUPT_LINE +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "std/acpi.h" // struct rsdp_descriptor +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // readl + +#include "src/fw/acpi-dsdt.hex" + +static void +build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) +{ + h->signature = cpu_to_le32(sig); + h->length = cpu_to_le32(len); + h->revision = rev; + memcpy(h->oem_id, BUILD_APPNAME6, 6); + memcpy(h->oem_table_id, BUILD_APPNAME4, 4); + memcpy(h->oem_table_id + 4, (void*)&sig, 4); + h->oem_revision = cpu_to_le32(1); + memcpy(h->asl_compiler_id, BUILD_APPNAME4, 4); + h->asl_compiler_revision = cpu_to_le32(1); + h->checksum -= checksum(h, len); +} + +static void piix4_fadt_setup(struct pci_device *pci, void *arg) +{ + struct fadt_descriptor_rev1 *fadt = arg; + + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(PIIX_PM_INTRRUPT); + fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); + fadt->acpi_enable = PIIX_ACPI_ENABLE; + fadt->acpi_disable = PIIX_ACPI_DISABLE; + fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); + fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); + fadt->gpe0_blk = cpu_to_le32(PIIX_GPE0_BLK); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = PIIX_GPE0_BLK_LEN; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void ich9_lpc_fadt_setup(struct pci_device *dev, void *arg) +{ + struct fadt_descriptor_rev1 *fadt = arg; + + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(9); + fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); + fadt->acpi_enable = ICH9_ACPI_ENABLE; + fadt->acpi_disable = ICH9_ACPI_DISABLE; + fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); + fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); + fadt->gpe0_blk = cpu_to_le32(acpi_pm_base + ICH9_PMIO_GPE0_STS); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + fadt->flags = cpu_to_le32(ACPI_FADT_F_WBINVD | + ACPI_FADT_F_PROC_C1 | + ACPI_FADT_F_SLP_BUTTON | + ACPI_FADT_F_RTC_S4 | + ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + +static const struct pci_device_id fadt_init_tbl[] = { + /* PIIX4 Power Management device (for ACPI) */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + piix4_fadt_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + ich9_lpc_fadt_setup), + PCI_DEVICE_END +}; + +static void fill_dsdt(struct fadt_descriptor_rev1 *fadt, void *dsdt) +{ + if (fadt->dsdt) { + free((void *)le32_to_cpu(fadt->dsdt)); + } + fadt->dsdt = cpu_to_le32((u32)dsdt); + fadt->checksum -= checksum(fadt, sizeof(*fadt)); + dprintf(1, "ACPI DSDT=%p\n", dsdt); +} + +static void * +build_fadt(struct pci_device *pci) +{ + struct fadt_descriptor_rev1 *fadt = malloc_high(sizeof(*fadt)); + struct facs_descriptor_rev1 *facs = memalign_high(64, sizeof(*facs)); + + if (!fadt || !facs) { + warn_noalloc(); + return NULL; + } + + /* FACS */ + memset(facs, 0, sizeof(*facs)); + facs->signature = cpu_to_le32(FACS_SIGNATURE); + facs->length = cpu_to_le32(sizeof(*facs)); + + /* FADT */ + memset(fadt, 0, sizeof(*fadt)); + fadt->firmware_ctrl = cpu_to_le32((u32)facs); + fadt->dsdt = 0; /* dsdt will be filled later in acpi_setup() + by fill_dsdt() */ + pci_init_device(fadt_init_tbl, pci, fadt); + + build_header((void*)fadt, FACP_SIGNATURE, sizeof(*fadt), 1); + + return fadt; +} + +static void* +build_madt(void) +{ + int madt_size = (sizeof(struct multiple_apic_table) + + sizeof(struct madt_processor_apic) * MaxCountCPUs + + sizeof(struct madt_io_apic) + + sizeof(struct madt_intsrcovr) * 16 + + sizeof(struct madt_local_nmi)); + + struct multiple_apic_table *madt = malloc_high(madt_size); + if (!madt) { + warn_noalloc(); + return NULL; + } + memset(madt, 0, madt_size); + madt->local_apic_address = cpu_to_le32(BUILD_APIC_ADDR); + madt->flags = cpu_to_le32(1); + struct madt_processor_apic *apic = (void*)&madt[1]; + int i; + for (i=0; i<MaxCountCPUs; i++) { + apic->type = APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = i; + apic->local_apic_id = i; + if (apic_id_is_present(apic->local_apic_id)) + apic->flags = cpu_to_le32(1); + else + apic->flags = cpu_to_le32(0); + apic++; + } + struct madt_io_apic *io_apic = (void*)apic; + io_apic->type = APIC_IO; + io_apic->length = sizeof(*io_apic); + io_apic->io_apic_id = BUILD_IOAPIC_ID; + io_apic->address = cpu_to_le32(BUILD_IOAPIC_ADDR); + io_apic->interrupt = cpu_to_le32(0); + + struct madt_intsrcovr *intsrcovr = (void*)&io_apic[1]; + if (romfile_loadint("etc/irq0-override", 0)) { + memset(intsrcovr, 0, sizeof(*intsrcovr)); + intsrcovr->type = APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = 0; + intsrcovr->gsi = cpu_to_le32(2); + intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ + intsrcovr++; + } + for (i = 1; i < 16; i++) { + if (!(BUILD_PCI_IRQS & (1 << i))) + /* No need for a INT source override structure. */ + continue; + memset(intsrcovr, 0, sizeof(*intsrcovr)); + intsrcovr->type = APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = i; + intsrcovr->gsi = cpu_to_le32(i); + intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ + intsrcovr++; + } + + struct madt_local_nmi *local_nmi = (void*)intsrcovr; + local_nmi->type = APIC_LOCAL_NMI; + local_nmi->length = sizeof(*local_nmi); + local_nmi->processor_id = 0xff; /* all processors */ + local_nmi->flags = cpu_to_le16(0); + local_nmi->lint = 1; /* LINT1 */ + local_nmi++; + + build_header((void*)madt, APIC_SIGNATURE, (void*)local_nmi - (void*)madt, 1); + return madt; +} + +// Encode a hex value +static inline char getHex(u32 val) { + val &= 0x0f; + return (val <= 9) ? ('0' + val) : ('A' + val - 10); +} + +// Encode a length in an SSDT. +static u8 * +encodeLen(u8 *ssdt_ptr, int length, int bytes) +{ + switch (bytes) { + default: + case 4: ssdt_ptr[3] = ((length >> 20) & 0xff); + case 3: ssdt_ptr[2] = ((length >> 12) & 0xff); + case 2: ssdt_ptr[1] = ((length >> 4) & 0xff); + ssdt_ptr[0] = (((bytes-1) & 0x3) << 6) | (length & 0x0f); + break; + case 1: ssdt_ptr[0] = length & 0x3f; + } + return ssdt_ptr + bytes; +} + +#include "src/fw/ssdt-proc.hex" + +/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ +#define PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) +#define PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) +#define PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) +#define PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) +#define PROC_AML (ssdp_proc_aml + *ssdt_proc_start) + +/* 0x5B 0x82 DeviceOp PkgLength NameString */ +#define PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) +#define PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) +#define PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) +#define PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) +#define PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) +#define PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) +#define PCI_SLOTS 32 + +#define SSDT_SIGNATURE 0x54445353 // SSDT +#define SSDT_HEADER_LENGTH 36 + +#include "src/fw/ssdt-misc.hex" +#include "src/fw/ssdt-pcihp.hex" + +#define PCI_RMV_BASE 0xae0c + +static u8* +build_notify(u8 *ssdt_ptr, const char *name, int skip, int count, + const char *target, int ofs) +{ + count -= skip; + + *(ssdt_ptr++) = 0x14; // MethodOp + ssdt_ptr = encodeLen(ssdt_ptr, 2+5+(12*count), 2); + memcpy(ssdt_ptr, name, 4); + ssdt_ptr += 4; + *(ssdt_ptr++) = 0x02; // MethodOp + + int i; + for (i = skip; count-- > 0; i++) { + *(ssdt_ptr++) = 0xA0; // IfOp + ssdt_ptr = encodeLen(ssdt_ptr, 11, 1); + *(ssdt_ptr++) = 0x93; // LEqualOp + *(ssdt_ptr++) = 0x68; // Arg0Op + *(ssdt_ptr++) = 0x0A; // BytePrefix + *(ssdt_ptr++) = i; + *(ssdt_ptr++) = 0x86; // NotifyOp + memcpy(ssdt_ptr, target, 4); + ssdt_ptr[ofs] = getHex(i >> 4); + ssdt_ptr[ofs + 1] = getHex(i); + ssdt_ptr += 4; + *(ssdt_ptr++) = 0x69; // Arg1Op + } + return ssdt_ptr; +} + +static void patch_pcihp(int slot, u8 *ssdt_ptr, u32 eject) +{ + ssdt_ptr[PCIHP_OFFSET_HEX] = getHex(slot >> 4); + ssdt_ptr[PCIHP_OFFSET_HEX+1] = getHex(slot); + ssdt_ptr[PCIHP_OFFSET_ID] = slot; + ssdt_ptr[PCIHP_OFFSET_ADR + 2] = slot; + + /* Runtime patching of EJ0: to disable hotplug for a slot, + * replace the method name: _EJ0 by EJ0_. */ + /* Sanity check */ + if (memcmp(ssdt_ptr + PCIHP_OFFSET_EJ0, "_EJ0", 4)) { + warn_internalerror(); + } + if (!eject) { + memcpy(ssdt_ptr + PCIHP_OFFSET_EJ0, "EJ0_", 4); + } +} + +static void* +build_ssdt(void) +{ + int acpi_cpus = MaxCountCPUs > 0xff ? 0xff : MaxCountCPUs; + int length = (sizeof(ssdp_misc_aml) // _S3_ / _S4_ / _S5_ + + (1+3+4) // Scope(_SB_) + + (acpi_cpus * PROC_SIZEOF) // procs + + (1+2+5+(12*acpi_cpus)) // NTFY + + (6+2+1+(1*acpi_cpus)) // CPON + + (1+3+4) // Scope(PCI0) + + ((PCI_SLOTS - 1) * PCIHP_SIZEOF) // slots + + (1+2+5+(12*(PCI_SLOTS - 1)))); // PCNT + u8 *ssdt = malloc_high(length); + if (! ssdt) { + warn_noalloc(); + return NULL; + } + u8 *ssdt_ptr = ssdt; + + // Copy header and encode fwcfg values in the S3_ / S4_ / S5_ packages + int sys_state_size; + char *sys_states = romfile_loadfile("etc/system-states", &sys_state_size); + if (!sys_states || sys_state_size != 6) + sys_states = (char[]){128, 0, 0, 129, 128, 128}; + + memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); + if (!(sys_states[3] & 128)) + ssdt_ptr[acpi_s3_name[0]] = 'X'; + if (!(sys_states[4] & 128)) + ssdt_ptr[acpi_s4_name[0]] = 'X'; + else + ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt[acpi_s4_pkg[0] + 3] = sys_states[4] & 127; + + // store pci io windows + *(u32*)&ssdt_ptr[acpi_pci32_start[0]] = cpu_to_le32(pcimem_start); + *(u32*)&ssdt_ptr[acpi_pci32_end[0]] = cpu_to_le32(pcimem_end - 1); + if (pcimem64_start) { + ssdt_ptr[acpi_pci64_valid[0]] = 1; + *(u64*)&ssdt_ptr[acpi_pci64_start[0]] = cpu_to_le64(pcimem64_start); + *(u64*)&ssdt_ptr[acpi_pci64_end[0]] = cpu_to_le64(pcimem64_end - 1); + *(u64*)&ssdt_ptr[acpi_pci64_length[0]] = cpu_to_le64( + pcimem64_end - pcimem64_start); + } else { + ssdt_ptr[acpi_pci64_valid[0]] = 0; + } + + int pvpanic_port = romfile_loadint("etc/pvpanic-port", 0x0); + *(u16 *)(ssdt_ptr + *ssdt_isa_pest) = pvpanic_port; + + ssdt_ptr += sizeof(ssdp_misc_aml); + + // build Scope(_SB_) header + *(ssdt_ptr++) = 0x10; // ScopeOp + ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); + *(ssdt_ptr++) = '_'; + *(ssdt_ptr++) = 'S'; + *(ssdt_ptr++) = 'B'; + *(ssdt_ptr++) = '_'; + + // build Processor object for each processor + int i; + for (i=0; i<acpi_cpus; i++) { + memcpy(ssdt_ptr, PROC_AML, PROC_SIZEOF); + ssdt_ptr[PROC_OFFSET_CPUHEX] = getHex(i >> 4); + ssdt_ptr[PROC_OFFSET_CPUHEX+1] = getHex(i); + ssdt_ptr[PROC_OFFSET_CPUID1] = i; + ssdt_ptr[PROC_OFFSET_CPUID2] = i; + ssdt_ptr += PROC_SIZEOF; + } + + // build "Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}" + // Arg0 = Processor ID = APIC ID + ssdt_ptr = build_notify(ssdt_ptr, "NTFY", 0, acpi_cpus, "CP00", 2); + + // build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + *(ssdt_ptr++) = 0x08; // NameOp + *(ssdt_ptr++) = 'C'; + *(ssdt_ptr++) = 'P'; + *(ssdt_ptr++) = 'O'; + *(ssdt_ptr++) = 'N'; + *(ssdt_ptr++) = 0x12; // PackageOp + ssdt_ptr = encodeLen(ssdt_ptr, 2+1+(1*acpi_cpus), 2); + *(ssdt_ptr++) = acpi_cpus; + for (i=0; i<acpi_cpus; i++) + *(ssdt_ptr++) = (apic_id_is_present(i)) ? 0x01 : 0x00; + + // build Scope(PCI0) opcode + *(ssdt_ptr++) = 0x10; // ScopeOp + ssdt_ptr = encodeLen(ssdt_ptr, length - (ssdt_ptr - ssdt), 3); + *(ssdt_ptr++) = 'P'; + *(ssdt_ptr++) = 'C'; + *(ssdt_ptr++) = 'I'; + *(ssdt_ptr++) = '0'; + + // build Device object for each slot + u32 rmvc_pcrm = inl(PCI_RMV_BASE); + for (i=1; i<PCI_SLOTS; i++) { + u32 eject = rmvc_pcrm & (0x1 << i); + memcpy(ssdt_ptr, PCIHP_AML, PCIHP_SIZEOF); + patch_pcihp(i, ssdt_ptr, eject != 0); + ssdt_ptr += PCIHP_SIZEOF; + } + + ssdt_ptr = build_notify(ssdt_ptr, "PCNT", 1, PCI_SLOTS, "S00_", 1); + + build_header((void*)ssdt, SSDT_SIGNATURE, ssdt_ptr - ssdt, 1); + + //hexdump(ssdt, ssdt_ptr - ssdt); + + return ssdt; +} + +#define HPET_ID 0x000 +#define HPET_PERIOD 0x004 + +static void* +build_hpet(void) +{ + struct acpi_20_hpet *hpet; + const void *hpet_base = (void *)BUILD_HPET_ADDRESS; + u32 hpet_vendor = readl(hpet_base + HPET_ID) >> 16; + u32 hpet_period = readl(hpet_base + HPET_PERIOD); + + if (hpet_vendor == 0 || hpet_vendor == 0xffff || + hpet_period == 0 || hpet_period > 100000000) + return NULL; + + hpet = malloc_high(sizeof(*hpet)); + if (!hpet) { + warn_noalloc(); + return NULL; + } + + memset(hpet, 0, sizeof(*hpet)); + /* Note timer_block_id value must be kept in sync with value advertised by + * emulated hpet + */ + hpet->timer_block_id = cpu_to_le32(0x8086a201); + hpet->addr.address = cpu_to_le64(BUILD_HPET_ADDRESS); + build_header((void*)hpet, HPET_SIGNATURE, sizeof(*hpet), 1); + + return hpet; +} + +static void +acpi_build_srat_memory(struct srat_memory_affinity *numamem, + u64 base, u64 len, int node, int enabled) +{ + numamem->type = SRAT_MEMORY; + numamem->length = sizeof(*numamem); + memset(numamem->proximity, 0, 4); + numamem->proximity[0] = node; + numamem->flags = cpu_to_le32(!!enabled); + numamem->base_addr = cpu_to_le64(base); + numamem->range_length = cpu_to_le64(len); +} + +static void * +build_srat(void) +{ + int numadatasize, numacpusize; + u64 *numadata = romfile_loadfile("etc/numa-nodes", &numadatasize); + u64 *numacpumap = romfile_loadfile("etc/numa-cpu-map", &numacpusize); + if (!numadata || !numacpumap) + goto fail; + int max_cpu = numacpusize / sizeof(u64); + int nb_numa_nodes = numadatasize / sizeof(u64); + + struct system_resource_affinity_table *srat; + int srat_size = sizeof(*srat) + + sizeof(struct srat_processor_affinity) * max_cpu + + sizeof(struct srat_memory_affinity) * (nb_numa_nodes + 2); + + srat = malloc_high(srat_size); + if (!srat) { + warn_noalloc(); + goto fail; + } + + memset(srat, 0, srat_size); + srat->reserved1=cpu_to_le32(1); + struct srat_processor_affinity *core = (void*)(srat + 1); + int i; + u64 curnode; + + for (i = 0; i < max_cpu; ++i) { + core->type = SRAT_PROCESSOR; + core->length = sizeof(*core); + core->local_apic_id = i; + curnode = *numacpumap++; + core->proximity_lo = curnode; + memset(core->proximity_hi, 0, 3); + core->local_sapic_eid = 0; + if (apic_id_is_present(i)) + core->flags = cpu_to_le32(1); + else + core->flags = cpu_to_le32(0); + core++; + } + + + /* the memory map is a bit tricky, it contains at least one hole + * from 640k-1M and possibly another one from 3.5G-4G. + */ + struct srat_memory_affinity *numamem = (void*)core; + int slots = 0; + u64 mem_len, mem_base, next_base = 0; + + acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); + next_base = 1024 * 1024; + numamem++; + slots++; + for (i = 1; i < nb_numa_nodes + 1; ++i) { + mem_base = next_base; + mem_len = *numadata++; + if (i == 1) + mem_len -= 1024 * 1024; + next_base = mem_base + mem_len; + + /* Cut out the PCI hole */ + if (mem_base <= RamSize && next_base > RamSize) { + mem_len -= next_base - RamSize; + if (mem_len > 0) { + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; + slots++; + } + mem_base = 1ULL << 32; + mem_len = next_base - RamSize; + next_base += (1ULL << 32) - RamSize; + } + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + numamem++; + slots++; + } + for (; slots < nb_numa_nodes + 2; slots++) { + acpi_build_srat_memory(numamem, 0, 0, 0, 0); + numamem++; + } + + build_header((void*)srat, SRAT_SIGNATURE, srat_size, 1); + + free(numadata); + free(numacpumap); + return srat; +fail: + free(numadata); + free(numacpumap); + return NULL; +} + +static void * +build_mcfg_q35(void) +{ + struct acpi_table_mcfg *mcfg; + + int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); + mcfg = malloc_high(len); + if (!mcfg) { + warn_noalloc(); + return NULL; + } + memset(mcfg, 0, len); + mcfg->allocation[0].address = cpu_to_le64(Q35_HOST_BRIDGE_PCIEXBAR_ADDR); + mcfg->allocation[0].pci_segment = cpu_to_le16(Q35_HOST_PCIE_PCI_SEGMENT); + mcfg->allocation[0].start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER; + mcfg->allocation[0].end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER; + + build_header((void *)mcfg, MCFG_SIGNATURE, len, 1); + return mcfg; +} + +static const struct pci_device_id acpi_find_tbl[] = { + /* PIIX4 Power Management device. */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL), + PCI_DEVICE_END, +}; + +#define MAX_ACPI_TABLES 20 +void +acpi_setup(void) +{ + if (! CONFIG_ACPI) + return; + + dprintf(3, "init ACPI tables\n"); + + // This code is hardcoded for PIIX4 Power Management device. + struct pci_device *pci = pci_find_init_device(acpi_find_tbl, NULL); + if (!pci) + // Device not found + return; + + // Build ACPI tables + u32 tables[MAX_ACPI_TABLES], tbl_idx = 0; + +#define ACPI_INIT_TABLE(X) \ + do { \ + tables[tbl_idx] = cpu_to_le32((u32)(X)); \ + if (le32_to_cpu(tables[tbl_idx])) \ + tbl_idx++; \ + } while(0) + + struct fadt_descriptor_rev1 *fadt = build_fadt(pci); + ACPI_INIT_TABLE(fadt); + ACPI_INIT_TABLE(build_ssdt()); + ACPI_INIT_TABLE(build_madt()); + ACPI_INIT_TABLE(build_hpet()); + ACPI_INIT_TABLE(build_srat()); + if (pci->device == PCI_DEVICE_ID_INTEL_ICH9_LPC) + ACPI_INIT_TABLE(build_mcfg_q35()); + + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("acpi/", file); + if (!file) + break; + struct acpi_table_header *table = malloc_high(file->size); + if (!table) { + warn_noalloc(); + continue; + } + int ret = file->copy(file, table, file->size); + if (ret <= sizeof(*table)) + continue; + if (table->signature == DSDT_SIGNATURE) { + if (fadt) { + fill_dsdt(fadt, table); + } + } else { + ACPI_INIT_TABLE(table); + } + if (tbl_idx == MAX_ACPI_TABLES) { + warn_noalloc(); + break; + } + } + + if (CONFIG_ACPI_DSDT && fadt && !fadt->dsdt) { + /* default DSDT */ + struct acpi_table_header *dsdt = malloc_high(sizeof(AmlCode)); + if (!dsdt) { + warn_noalloc(); + return; + } + memcpy(dsdt, AmlCode, sizeof(AmlCode)); + fill_dsdt(fadt, dsdt); + /* Strip out compiler-generated header if any */ + memset(dsdt, 0, sizeof *dsdt); + build_header(dsdt, DSDT_SIGNATURE, sizeof(AmlCode), 1); + } + + // Build final rsdt table + struct rsdt_descriptor_rev1 *rsdt; + size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; + rsdt = malloc_high(rsdt_len); + if (!rsdt) { + warn_noalloc(); + return; + } + memset(rsdt, 0, rsdt_len); + memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx); + build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1); + + // Build rsdp pointer table + struct rsdp_descriptor rsdp; + memset(&rsdp, 0, sizeof(rsdp)); + rsdp.signature = cpu_to_le64(RSDP_SIGNATURE); + memcpy(rsdp.oem_id, BUILD_APPNAME6, 6); + rsdp.rsdt_physical_address = cpu_to_le32((u32)rsdt); + rsdp.checksum -= checksum(&rsdp, 20); + copy_acpi_rsdp(&rsdp); +} diff --git a/qemu/roms/seabios/src/fw/biostables.c b/qemu/roms/seabios/src/fw/biostables.c new file mode 100644 index 000000000..50a891be8 --- /dev/null +++ b/qemu/roms/seabios/src/fw/biostables.c @@ -0,0 +1,461 @@ +// Support for manipulating bios tables (pir, mptable, acpi, smbios). +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le32_to_cpu +#include "config.h" // CONFIG_* +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "hw/pci.h" // pci_config_writeb +#include "std/acpi.h" // struct rsdp_descriptor +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "std/pirtable.h" // struct pir_header +#include "std/smbios.h" // struct smbios_entry_point +#include "romfile.h" +#include "string.h" // memcpy +#include "util.h" // copy_table +#include "x86.h" // outb + +struct pir_header *PirAddr VARFSEG; + +void +copy_pir(void *pos) +{ + struct pir_header *p = pos; + if (p->signature != PIR_SIGNATURE) + return; + if (PirAddr) + return; + if (p->size < sizeof(*p)) + return; + if (checksum(pos, p->size) != 0) + return; + void *newpos = malloc_fseg(p->size); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying PIR from %p to %p\n", pos, newpos); + memcpy(newpos, pos, p->size); + PirAddr = newpos; +} + +void +copy_mptable(void *pos) +{ + struct mptable_floating_s *p = pos; + if (p->signature != MPTABLE_SIGNATURE) + return; + if (!p->physaddr) + return; + if (checksum(pos, sizeof(*p)) != 0) + return; + u32 length = p->length * 16; + u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length; + // Allocate final memory location. (In theory the config + // structure can go in high memory, but Linux kernels before + // v2.6.30 crash with that.) + struct mptable_floating_s *newpos = malloc_fseg(length + mpclength); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos); + memcpy(newpos, pos, length); + newpos->physaddr = (u32)newpos + length; + newpos->checksum -= checksum(newpos, sizeof(*newpos)); + memcpy((void*)newpos + length, (void*)p->physaddr, mpclength); +} + + +/**************************************************************** + * ACPI + ****************************************************************/ + +static int +get_acpi_rsdp_length(void *pos, unsigned size) +{ + struct rsdp_descriptor *p = pos; + if (p->signature != RSDP_SIGNATURE) + return -1; + u32 length = 20; + if (length > size) + return -1; + if (checksum(pos, length) != 0) + return -1; + if (p->revision > 1) { + length = p->length; + if (length > size) + return -1; + if (checksum(pos, length) != 0) + return -1; + } + return length; +} + +struct rsdp_descriptor *RsdpAddr; + +void +copy_acpi_rsdp(void *pos) +{ + if (RsdpAddr) + return; + int length = get_acpi_rsdp_length(pos, -1); + if (length < 0) + return; + void *newpos = malloc_fseg(length); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos); + memcpy(newpos, pos, length); + RsdpAddr = newpos; +} + +void *find_acpi_rsdp(void) +{ + extern u8 zonefseg_start[], zonefseg_end[]; + unsigned long start = (unsigned long)zonefseg_start; + unsigned long end = (unsigned long)zonefseg_end; + unsigned long pos; + + for (pos = ALIGN(start, 0x10); pos <= ALIGN_DOWN(end, 0x10); pos += 0x10) + if (get_acpi_rsdp_length((void *)pos, end - pos) >= 0) + return (void *)pos; + + return NULL; +} + +static struct fadt_descriptor_rev1 * +find_fadt(void) +{ + dprintf(4, "rsdp=%p\n", RsdpAddr); + if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) + return NULL; + struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; + dprintf(4, "rsdt=%p\n", rsdt); + if (!rsdt || rsdt->signature != RSDT_SIGNATURE) + return NULL; + void *end = (void*)rsdt + rsdt->length; + int i; + for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) { + struct fadt_descriptor_rev1 *fadt = (void*)rsdt->table_offset_entry[i]; + if (!fadt || fadt->signature != FACP_SIGNATURE) + continue; + dprintf(4, "fadt=%p\n", fadt); + return fadt; + } + dprintf(4, "no fadt found\n"); + return NULL; +} + +u32 +find_resume_vector(void) +{ + struct fadt_descriptor_rev1 *fadt = find_fadt(); + if (!fadt) + return 0; + struct facs_descriptor_rev1 *facs = (void*)fadt->firmware_ctrl; + dprintf(4, "facs=%p\n", facs); + if (! facs || facs->signature != FACS_SIGNATURE) + return 0; + // Found it. + dprintf(4, "resume addr=%d\n", facs->firmware_waking_vector); + return facs->firmware_waking_vector; +} + +static struct acpi_20_generic_address acpi_reset_reg; +static u8 acpi_reset_val; +u32 acpi_pm1a_cnt VARFSEG; +u16 acpi_pm_base = 0xb000; + +#define acpi_ga_to_bdf(addr) pci_to_bdf(0, (addr >> 32) & 0xffff, (addr >> 16) & 0xffff) + +void +acpi_reboot(void) +{ + // Check it passed the sanity checks in acpi_set_reset_reg() and was set + if (acpi_reset_reg.register_bit_width != 8) + return; + + u64 addr = le64_to_cpu(acpi_reset_reg.address); + + dprintf(1, "ACPI hard reset %d:%llx (%x)\n", + acpi_reset_reg.address_space_id, addr, acpi_reset_val); + + switch (acpi_reset_reg.address_space_id) { + case 0: // System Memory + writeb((void *)(u32)addr, acpi_reset_val); + break; + case 1: // System I/O + outb(acpi_reset_val, addr); + break; + case 2: // PCI config space + pci_config_writeb(acpi_ga_to_bdf(addr), addr & 0xffff, acpi_reset_val); + break; + } +} + +static void +acpi_set_reset_reg(struct acpi_20_generic_address *reg, u8 val) +{ + if (!reg || reg->address_space_id > 2 || + reg->register_bit_width != 8 || reg->register_bit_offset) + return; + + acpi_reset_reg = *reg; + acpi_reset_val = val; +} + +void +find_acpi_features(void) +{ + struct fadt_descriptor_rev1 *fadt = find_fadt(); + if (!fadt) + return; + u32 pm_tmr = le32_to_cpu(fadt->pm_tmr_blk); + u32 pm1a_cnt = le32_to_cpu(fadt->pm1a_cnt_blk); + dprintf(4, "pm_tmr_blk=%x\n", pm_tmr); + if (pm_tmr) + pmtimer_setup(pm_tmr); + if (pm1a_cnt) + acpi_pm1a_cnt = pm1a_cnt; + + // Theoretically we should check the 'reset_reg_sup' flag, but Windows + // doesn't and thus nobody seems to *set* it. If the table is large enough + // to include it, let the sanity checks in acpi_set_reset_reg() suffice. + if (fadt->length >= 129) { + void *p = fadt; + acpi_set_reset_reg(p + 116, *(u8 *)(p + 128)); + } +} + + +/**************************************************************** + * SMBIOS + ****************************************************************/ + +// Iterator for each sub-table in the smbios blob. +void * +smbios_next(struct smbios_entry_point *smbios, void *prev) +{ + if (!smbios) + return NULL; + void *start = (void*)smbios->structure_table_address; + void *end = start + smbios->structure_table_length; + + if (!prev) { + prev = start; + } else { + struct smbios_structure_header *hdr = prev; + if (prev + sizeof(*hdr) > end) + return NULL; + prev += hdr->length + 2; + while (prev < end && (*(u8*)(prev-1) != '\0' || *(u8*)(prev-2) != '\0')) + prev++; + } + struct smbios_structure_header *hdr = prev; + if (prev >= end || prev + sizeof(*hdr) >= end || prev + hdr->length >= end) + return NULL; + return prev; +} + +struct smbios_entry_point *SMBiosAddr; + +void +copy_smbios(void *pos) +{ + if (SMBiosAddr) + return; + struct smbios_entry_point *p = pos; + if (memcmp(p->anchor_string, "_SM_", 4)) + return; + if (checksum(pos, 0x10) != 0) + return; + if (memcmp(p->intermediate_anchor_string, "_DMI_", 5)) + return; + if (checksum(pos+0x10, p->length-0x10) != 0) + return; + struct smbios_entry_point *newpos = malloc_fseg(p->length); + if (!newpos) { + warn_noalloc(); + return; + } + dprintf(1, "Copying SMBIOS entry point from %p to %p\n", pos, newpos); + memcpy(newpos, pos, p->length); + SMBiosAddr = newpos; +} + +void +display_uuid(void) +{ + struct smbios_type_1 *tbl = smbios_next(SMBiosAddr, NULL); + int minlen = offsetof(struct smbios_type_1, uuid) + sizeof(tbl->uuid); + for (; tbl; tbl = smbios_next(SMBiosAddr, tbl)) + if (tbl->header.type == 1 && tbl->header.length >= minlen) { + u8 *uuid = tbl->uuid; + u8 empty_uuid[sizeof(tbl->uuid)] = { 0 }; + if (memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0) + return; + + printf("Machine UUID" + " %02x%02x%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x" + "-%02x%02x%02x%02x%02x%02x\n" + , uuid[ 0], uuid[ 1], uuid[ 2], uuid[ 3] + , uuid[ 4], uuid[ 5] + , uuid[ 6], uuid[ 7] + , uuid[ 8], uuid[ 9] + , uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + return; + } +} + +#define set_str_field_or_skip(type, field, value) \ + do { \ + int size = (value != NULL) ? strlen(value) + 1 : 0; \ + if (size > 1) { \ + memcpy(end, value, size); \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ + } while (0) + +static void * +smbios_new_type_0(void *start, + const char *vendor, const char *version, const char *date) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + set_str_field_or_skip(0, vendor_str, vendor); + set_str_field_or_skip(0, bios_version_str, version); + p->bios_starting_address_segment = 0xe800; + set_str_field_or_skip(0, bios_release_date_str, date); + + p->bios_rom_size = 0; /* FIXME */ + + /* BIOS characteristics not supported */ + memset(p->bios_characteristics, 0, 8); + p->bios_characteristics[0] = 0x08; + + /* Enable targeted content distribution (needed for SVVP) */ + p->bios_characteristics_extension_bytes[0] = 0; + p->bios_characteristics_extension_bytes[1] = 4; + + p->system_bios_major_release = 0; + p->system_bios_minor_release = 0; + p->embedded_controller_major_release = 0xFF; + p->embedded_controller_minor_release = 0xFF; + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +#define BIOS_NAME "SeaBIOS" +#define BIOS_DATE "04/01/2014" + +static int +smbios_romfile_setup(void) +{ + struct romfile_s *f_anchor = romfile_find("etc/smbios/smbios-anchor"); + struct romfile_s *f_tables = romfile_find("etc/smbios/smbios-tables"); + struct smbios_entry_point ep; + struct smbios_type_0 *t0; + u16 qtables_len, need_t0 = 1; + u8 *qtables, *tables; + + if (!f_anchor || !f_tables || f_anchor->size != sizeof(ep)) + return 0; + + f_anchor->copy(f_anchor, &ep, f_anchor->size); + + if (f_tables->size != ep.structure_table_length) + return 0; + + qtables = malloc_tmphigh(f_tables->size); + if (!qtables) { + warn_noalloc(); + return 0; + } + f_tables->copy(f_tables, qtables, f_tables->size); + ep.structure_table_address = (u32)qtables; /* for smbios_next(), below */ + + /* did we get a type 0 structure ? */ + for (t0 = smbios_next(&ep, NULL); t0; t0 = smbios_next(&ep, t0)) + if (t0->header.type == 0) { + need_t0 = 0; + break; + } + + qtables_len = ep.structure_table_length; + if (need_t0) { + /* common case: add our own type 0, with 3 strings and 4 '\0's */ + u16 t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) + + strlen(VERSION) + strlen(BIOS_DATE) + 4; + ep.structure_table_length += t0_len; + if (t0_len > ep.max_structure_size) + ep.max_structure_size = t0_len; + ep.number_of_structures++; + } + + /* allocate final blob and record its address in the entry point */ + if (ep.structure_table_length > BUILD_MAX_SMBIOS_FSEG) + tables = malloc_high(ep.structure_table_length); + else + tables = malloc_fseg(ep.structure_table_length); + if (!tables) { + warn_noalloc(); + free(qtables); + return 0; + } + ep.structure_table_address = (u32)tables; + + /* populate final blob */ + if (need_t0) + tables = smbios_new_type_0(tables, BIOS_NAME, VERSION, BIOS_DATE); + memcpy(tables, qtables, qtables_len); + free(qtables); + + /* finalize entry point */ + ep.checksum -= checksum(&ep, 0x10); + ep.intermediate_checksum -= checksum((void *)&ep + 0x10, ep.length - 0x10); + + copy_smbios(&ep); + return 1; +} + +void +smbios_setup(void) +{ + if (smbios_romfile_setup()) + return; + smbios_legacy_setup(); +} + +void +copy_table(void *pos) +{ + copy_pir(pos); + copy_mptable(pos); + copy_acpi_rsdp(pos); + copy_smbios(pos); +} diff --git a/qemu/roms/seabios/src/fw/coreboot.c b/qemu/roms/seabios/src/fw/coreboot.c new file mode 100644 index 000000000..8fd84493b --- /dev/null +++ b/qemu/roms/seabios/src/fw/coreboot.c @@ -0,0 +1,553 @@ +// Coreboot interface support. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // MAXDESCSIZE +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_probe_devices +#include "lzmadecode.h" // LzmaDecode +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // coreboot_preinit + + +/**************************************************************** + * Memory map + ****************************************************************/ + +struct cb_header { + u32 signature; + u32 header_bytes; + u32 header_checksum; + u32 table_bytes; + u32 table_checksum; + u32 table_entries; +}; + +#define CB_SIGNATURE 0x4f49424C // "LBIO" + +struct cb_memory_range { + u64 start; + u64 size; + u32 type; +}; + +#define CB_MEM_TABLE 16 + +struct cb_memory { + u32 tag; + u32 size; + struct cb_memory_range map[0]; +}; + +#define CB_TAG_MEMORY 0x01 + +#define MEM_RANGE_COUNT(_rec) \ + (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0])) + +struct cb_mainboard { + u32 tag; + u32 size; + u8 vendor_idx; + u8 part_idx; + char strings[0]; +}; + +#define CB_TAG_MAINBOARD 0x0003 + +struct cb_forward { + u32 tag; + u32 size; + u64 forward; +}; + +#define CB_TAG_FORWARD 0x11 + +struct cb_cbmem_ref { + u32 tag; + u32 size; + u64 cbmem_addr; +}; + +#define CB_TAG_CBMEM_CONSOLE 0x17 + +struct cbmem_console { + u32 buffer_size; + u32 buffer_cursor; + u8 buffer_body[0]; +} PACKED; +static struct cbmem_console *cbcon = NULL; + +static u16 +ipchksum(char *buf, int count) +{ + u16 *p = (u16*)buf; + u32 sum = 0; + while (count > 1) { + sum += GET_FARVAR(0, *p); + p++; + count -= 2; + } + if (count) + sum += GET_FARVAR(0, *(u8*)p); + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return ~sum; +} + +// Try to locate the coreboot header in a given address range. +static struct cb_header * +find_cb_header(u32 addr, int len) +{ + u32 end = addr + len; + for (; addr < end; addr += 16) { + struct cb_header *cbh = (void*)addr; + if (GET_FARVAR(0, cbh->signature) != CB_SIGNATURE) + continue; + u32 tsize = GET_FARVAR(0, cbh->table_bytes); + if (! tsize) + continue; + if (ipchksum((void*)addr, sizeof(*cbh)) != 0) + continue; + if (ipchksum((void*)addr + sizeof(*cbh), tsize) + != GET_FARVAR(0, cbh->table_checksum)) + continue; + return cbh; + } + return NULL; +} + +// Try to find the coreboot memory table in the given coreboot table. +void * +find_cb_subtable(struct cb_header *cbh, u32 tag) +{ + char *tbl = (char *)cbh + sizeof(*cbh); + u32 count = GET_FARVAR(0, cbh->table_entries); + int i; + for (i=0; i<count; i++) { + struct cb_memory *cbm = (void*)tbl; + tbl += GET_FARVAR(0, cbm->size); + if (GET_FARVAR(0, cbm->tag) == tag) + return cbm; + } + return NULL; +} + +struct cb_header * +find_cb_table(void) +{ + struct cb_header *cbh = find_cb_header(0, 0x1000); + if (!cbh) + return NULL; + struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD); + if (cbf) { + dprintf(3, "Found coreboot table forwarder.\n"); + cbh = find_cb_header(GET_FARVAR(0, cbf->forward), 0x100); + if (!cbh) + return NULL; + } + return cbh; +} + +static struct cb_memory *CBMemTable; +const char *CBvendor = "", *CBpart = ""; + +// Populate max ram and e820 map info by scanning for a coreboot table. +void +coreboot_preinit(void) +{ + if (!CONFIG_COREBOOT) + return; + + dprintf(3, "Attempting to find coreboot table\n"); + + // Find coreboot table. + struct cb_header *cbh = find_cb_table(); + if (!cbh) + goto fail; + dprintf(3, "Now attempting to find coreboot memory map\n"); + struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY); + if (!cbm) + goto fail; + + int i, count = MEM_RANGE_COUNT(cbm); + for (i=0; i<count; i++) { + struct cb_memory_range *m = &cbm->map[i]; + u32 type = m->type; + if (type == CB_MEM_TABLE) + type = E820_RESERVED; + add_e820(m->start, m->size, type); + } + + // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this + // confuses grub. So, override it. + add_e820(0, 16*1024, E820_RAM); + + struct cb_cbmem_ref *cbref = find_cb_subtable(cbh, CB_TAG_CBMEM_CONSOLE); + if (cbref) { + cbcon = (void*)(u32)cbref->cbmem_addr; + debug_banner(); + dprintf(1, "Found coreboot cbmem console @ %llx\n", cbref->cbmem_addr); + } + + struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD); + if (cbmb) { + CBvendor = &cbmb->strings[cbmb->vendor_idx]; + CBpart = &cbmb->strings[cbmb->part_idx]; + dprintf(1, "Found mainboard %s %s\n", CBvendor, CBpart); + } + + return; + +fail: + // No table found.. Use 16Megs as a dummy value. + dprintf(1, "Unable to find coreboot table!\n"); + add_e820(0, 16*1024*1024, E820_RAM); + return; +} + +void coreboot_debug_putc(char c) +{ + if (!CONFIG_DEBUG_COREBOOT) + return; + if (!cbcon) + return; + u32 cursor = cbcon->buffer_cursor++; + if (cursor < cbcon->buffer_size) + cbcon->buffer_body[cursor] = c; +} + +/**************************************************************** + * BIOS table copying + ****************************************************************/ + +// Attempt to find (and relocate) any standard bios tables found in a +// given address range. +static void +scan_tables(u32 start, u32 size) +{ + void *p = (void*)ALIGN(start, 16); + void *end = (void*)start + size; + for (; p<end; p += 16) + copy_table(p); +} + +void +coreboot_platform_setup(void) +{ + if (!CONFIG_COREBOOT) + return; + pci_probe_devices(); + + struct cb_memory *cbm = CBMemTable; + if (!cbm) + return; + + dprintf(3, "Relocating coreboot bios tables\n"); + + // Scan CB_MEM_TABLE areas for bios tables. + int i, count = MEM_RANGE_COUNT(cbm); + for (i=0; i<count; i++) { + struct cb_memory_range *m = &cbm->map[i]; + if (m->type == CB_MEM_TABLE) + scan_tables(m->start, m->size); + } + + find_acpi_features(); +} + + +/**************************************************************** + * ulzma + ****************************************************************/ + +// Uncompress data in flash to an area of memory. +static int +ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen) +{ + dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst); + CLzmaDecoderState state; + int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE); + if (ret != LZMA_RESULT_OK) { + dprintf(1, "LzmaDecodeProperties error - %d\n", ret); + return -1; + } + u8 scratch[15980]; + int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); + if (need > sizeof(scratch)) { + dprintf(1, "LzmaDecode need %d have %d\n", need, (unsigned int)sizeof(scratch)); + return -1; + } + state.Probs = (CProb *)scratch; + + u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE); + if (dstlen > maxlen) { + dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen); + return -1; + } + u32 inProcessed, outProcessed; + ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen + , &inProcessed, dst, dstlen, &outProcessed); + if (ret) { + dprintf(1, "LzmaDecode returned %d\n", ret); + return -1; + } + return dstlen; +} + + +/**************************************************************** + * Coreboot flash format + ****************************************************************/ + +#define CBFS_HEADER_MAGIC 0x4F524243 +#define CBFS_VERSION1 0x31313131 + +struct cbfs_header { + u32 magic; + u32 version; + u32 romsize; + u32 bootblocksize; + u32 align; + u32 offset; + u32 pad[2]; +} PACKED; + +#define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE + +struct cbfs_file { + u64 magic; + u32 len; + u32 type; + u32 checksum; + u32 offset; + char filename[0]; +} PACKED; + +struct cbfs_romfile_s { + struct romfile_s file; + struct cbfs_file *fhdr; + void *data; + u32 rawsize, flags; +}; + +// Copy a file to memory (uncompressing if necessary) +static int +cbfs_copyfile(struct romfile_s *file, void *dst, u32 maxlen) +{ + if (!CONFIG_COREBOOT_FLASH) + return -1; + + struct cbfs_romfile_s *cfile; + cfile = container_of(file, struct cbfs_romfile_s, file); + u32 size = cfile->rawsize; + void *src = cfile->data; + if (cfile->flags) { + // Compressed - copy to temp ram and uncompress it. + void *temp = malloc_tmphigh(size); + if (!temp) { + warn_noalloc(); + return -1; + } + iomemcpy(temp, src, size); + int ret = ulzma(dst, maxlen, temp, size); + yield(); + free(temp); + return ret; + } + + // Not compressed. + dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst); + if (size > maxlen) { + warn_noalloc(); + return -1; + } + iomemcpy(dst, src, size); + return size; +} + +// Process CBFS links file. The links file is a newline separated +// file where each line has a "link name" and a "destination name" +// separated by a space character. +static void +process_links_file(void) +{ + char *links = romfile_loadfile("links", NULL), *next = links; + while (next) { + // Parse out linkname and destname + char *linkname = next; + next = strchr(linkname, '\n'); + if (next) + *next++ = '\0'; + char *comment = strchr(linkname, '#'); + if (comment) + *comment = '\0'; + linkname = nullTrailingSpace(linkname); + char *destname = strchr(linkname, ' '); + if (!destname) + continue; + *destname++ = '\0'; + destname = nullTrailingSpace(destname); + // Lookup destname and create new romfile entry for linkname + struct romfile_s *ufile = romfile_find(destname); + if (!ufile) + continue; + struct cbfs_romfile_s *cufile + = container_of(ufile, struct cbfs_romfile_s, file); + struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); + if (!cfile) { + warn_noalloc(); + break; + } + memcpy(cfile, cufile, sizeof(*cfile)); + strtcpy(cfile->file.name, linkname, sizeof(cfile->file.name)); + romfile_add(&cfile->file); + } + free(links); +} + +void +coreboot_cbfs_init(void) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + + struct cbfs_header *hdr = *(void **)(CONFIG_CBFS_LOCATION - 4); + if (hdr->magic != cpu_to_be32(CBFS_HEADER_MAGIC)) { + dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n" + , hdr, hdr->magic, cpu_to_be32(CBFS_HEADER_MAGIC)); + return; + } + dprintf(1, "Found CBFS header at %p\n", hdr); + + u32 romsize = be32_to_cpu(hdr->romsize); + u32 romstart = CONFIG_CBFS_LOCATION - romsize; + struct cbfs_file *fhdr = (void*)romstart + be32_to_cpu(hdr->offset); + for (;;) { + if ((u32)fhdr - romstart > romsize) + break; + u64 magic = fhdr->magic; + if (magic != CBFS_FILE_MAGIC) + break; + struct cbfs_romfile_s *cfile = malloc_tmp(sizeof(*cfile)); + if (!cfile) { + warn_noalloc(); + break; + } + memset(cfile, 0, sizeof(*cfile)); + strtcpy(cfile->file.name, fhdr->filename, sizeof(cfile->file.name)); + cfile->file.size = cfile->rawsize = be32_to_cpu(fhdr->len); + cfile->fhdr = fhdr; + cfile->file.copy = cbfs_copyfile; + cfile->data = (void*)fhdr + be32_to_cpu(fhdr->offset); + int len = strlen(cfile->file.name); + if (len > 5 && strcmp(&cfile->file.name[len-5], ".lzma") == 0) { + // Using compression. + cfile->flags = 1; + cfile->file.name[len-5] = '\0'; + cfile->file.size = *(u32*)(cfile->data + LZMA_PROPERTIES_SIZE); + } + romfile_add(&cfile->file); + + fhdr = (void*)ALIGN((u32)cfile->data + cfile->rawsize + , be32_to_cpu(hdr->align)); + } + + process_links_file(); +} + +struct cbfs_payload_segment { + u32 type; + u32 compression; + u32 offset; + u64 load_addr; + u32 len; + u32 mem_len; +} PACKED; + +#define PAYLOAD_SEGMENT_BSS 0x20535342 +#define PAYLOAD_SEGMENT_ENTRY 0x52544E45 + +#define CBFS_COMPRESS_NONE 0 +#define CBFS_COMPRESS_LZMA 1 + +struct cbfs_payload { + struct cbfs_payload_segment segments[1]; +}; + +void +cbfs_run_payload(struct cbfs_file *fhdr) +{ + if (!CONFIG_COREBOOT_FLASH || !fhdr) + return; + dprintf(1, "Run %s\n", fhdr->filename); + struct cbfs_payload *pay = (void*)fhdr + be32_to_cpu(fhdr->offset); + struct cbfs_payload_segment *seg = pay->segments; + for (;;) { + void *src = (void*)pay + be32_to_cpu(seg->offset); + void *dest = (void*)(u32)be64_to_cpu(seg->load_addr); + u32 src_len = be32_to_cpu(seg->len); + u32 dest_len = be32_to_cpu(seg->mem_len); + switch (seg->type) { + case PAYLOAD_SEGMENT_BSS: + dprintf(3, "BSS segment %d@%p\n", dest_len, dest); + memset(dest, 0, dest_len); + break; + case PAYLOAD_SEGMENT_ENTRY: { + dprintf(1, "Calling addr %p\n", dest); + void (*func)() = dest; + func(); + return; + } + default: + dprintf(3, "Segment %x %d@%p -> %d@%p\n" + , seg->type, src_len, src, dest_len, dest); + if (seg->compression == cpu_to_be32(CBFS_COMPRESS_NONE)) { + if (src_len > dest_len) + src_len = dest_len; + memcpy(dest, src, src_len); + } else if (CONFIG_LZMA + && seg->compression == cpu_to_be32(CBFS_COMPRESS_LZMA)) { + int ret = ulzma(dest, dest_len, src, src_len); + if (ret < 0) + return; + src_len = ret; + } else { + dprintf(1, "No support for compression type %x\n" + , seg->compression); + return; + } + if (dest_len > src_len) + memset(dest + src_len, 0, dest_len - src_len); + break; + } + seg++; + } +} + +// Register payloads in "img/" directory with boot system. +void +cbfs_payload_setup(void) +{ + if (!CONFIG_COREBOOT_FLASH) + return; + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("img/", file); + if (!file) + break; + struct cbfs_romfile_s *cfile; + cfile = container_of(file, struct cbfs_romfile_s, file); + const char *filename = file->name; + char *desc = znprintf(MAXDESCSIZE, "Payload [%s]", &filename[4]); + boot_add_cbfs(cfile->fhdr, desc, bootprio_find_named_rom(filename, 0)); + } +} diff --git a/qemu/roms/seabios/src/fw/csm.c b/qemu/roms/seabios/src/fw/csm.c new file mode 100644 index 000000000..7cdb398f2 --- /dev/null +++ b/qemu/roms/seabios/src/fw/csm.c @@ -0,0 +1,344 @@ +// Compatibility Support Module (CSM) for UEFI / EDK-II +// +// Copyright © 2013 Intel Corporation +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" +#include "config.h" // CONFIG_* +#include "farptr.h" // MAKE_FLATPTR +#include "hw/pci.h" +#include "hw/pic.h" +#include "malloc.h" // csm_malloc_preinit +#include "memmap.h" +#include "output.h" // dprintf +#include "stacks.h" // wait_threads +#include "std/acpi.h" // RSDP_SIGNATURE +#include "std/bda.h" // struct bios_data_area_s +#include "std/optionrom.h" // struct rom_header +#include "util.h" // copy_smbios +#include "paravirt.h" // qemu_preinit + +#define UINT8 u8 +#define UINT16 u16 +#define UINT32 u32 +#include "std/LegacyBios.h" + +struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16); + +EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = { + .Signature = 0x24454649, + .TableChecksum = 0 /* Filled in by checkrom.py */, + .TableLength = sizeof(csm_compat_table), + .Compatibility16CallSegment = SEG_BIOS, + .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */, + .OemIdStringPointer = (u32)"SeaBIOS", + .AcpiRsdPtrPointer = (u32)&csm_rsdp, +}; + +EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table; +EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table; + +static u16 PICMask = PIC_IRQMASK_DEFAULT; + +extern void __csm_return(struct bregs *regs) __noreturn; + +static void +csm_return(struct bregs *regs) +{ + u32 rommax = rom_get_max(); + extern u8 final_readonly_start[]; + + dprintf(3, "handle_csm returning AX=%04x\n", regs->ax); + + csm_compat_table.UmaAddress = rommax; + csm_compat_table.UmaSize = (u32)final_readonly_start - rommax; + + PICMask = pic_irqmask_read(); + __csm_return(regs); +} + +static void +csm_maininit(struct bregs *regs) +{ + interface_init(); + pci_probe_devices(); + + csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS; + csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset(); + + regs->ax = 0; + + csm_return(regs); +} + +/* Legacy16InitializeYourself */ +static void +handle_csm_0000(struct bregs *regs) +{ + qemu_preinit(); + + dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es, + regs->bx); + + csm_init_table = MAKE_FLATPTR(regs->es, regs->bx); + + dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB); + dprintf(3, "HiPmmMemory %08x\n", csm_init_table->HiPmmMemory); + dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes); + dprintf(3, "ReverseThunk %04x:%04x\n", csm_init_table->ReverseThunkCallSegment, + csm_init_table->ReverseThunkCallOffset); + dprintf(3, "NumE820Entries %08x\n", csm_init_table->NumberE820Entries); + dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb); + dprintf(3, "ThunkStart %08x\n", csm_init_table->ThunkStart); + dprintf(3, "ThunkSize %08x\n", csm_init_table->ThunkSizeInBytes); + dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory); + dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes); + + csm_malloc_preinit(csm_init_table->LowPmmMemory, + csm_init_table->LowPmmMemorySizeInBytes, + csm_init_table->HiPmmMemory, + csm_init_table->HiPmmMemorySizeInBytes); + reloc_preinit(csm_maininit, regs); +} + +/* Legacy16UpdateBbs */ +static void +handle_csm_0001(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx); + + csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx); + dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion); + dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion); + dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable); + dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable); + dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength); +// dprintf(3, "SioData %08x\n", csm_boot_table->SioData); + dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType); + dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask); + dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries); +// dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo); + dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries); + dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable); + dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable); + dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb); + dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable); + + regs->ax = 0; +} + +/* PrepareToBoot */ +static void +handle_csm_0002(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx); + + struct e820entry *p = (void *)csm_compat_table.E820Pointer; + int i; + for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++) + add_e820(p[i].start, p[i].size, p[i].type); + + if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) { + u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes; + add_e820(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED); + } + + // For PCIBIOS 1ab10e + if (csm_compat_table.IrqRoutingTablePointer && + csm_compat_table.IrqRoutingTableLength) { + PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer; + dprintf(3, "CSM PIRQ table at %p\n", PirAddr); + } + + // For find_resume_vector()... and find_acpi_features() + if (csm_rsdp.signature == RSDP_SIGNATURE) { + RsdpAddr = &csm_rsdp; + dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr); + + find_acpi_features(); + } + + // SMBIOS table needs to be copied into the f-seg + // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it + if (csm_boot_table->SmbiosTable && !SMBiosAddr) + copy_smbios((void *)csm_boot_table->SmbiosTable); + + // MPTABLE is just there; we don't care where. + + // EFI may have reinitialised the video using its *own* driver. + enable_vga_console(); + + // EFI fills this in for us. Zero it for now... + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + bda->hdcount = 0; + + mathcp_setup(); + timer_setup(); + clock_setup(); + device_hardware_setup(); + wait_threads(); + interactive_bootmenu(); + + prepareboot(); + + regs->ax = 0; +} + +/* Boot */ +static void +handle_csm_0003(struct bregs *regs) +{ + if (!CONFIG_BOOT) { + regs->ax = 1; + return; + } + + dprintf(3, "Boot\n"); + + startBoot(); + + regs->ax = 1; +} + +/* Legacy16DispatchOprom */ +static void +handle_csm_0005(struct bregs *regs) +{ + EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx); + struct rom_header *rom; + u16 bdf; + + if (!CONFIG_OPTIONROMS) { + regs->ax = 1; + return; + } + + dprintf(3, "Legacy16DispatchOprom rom %p\n", table); + + dprintf(3, "OpromSegment %04x\n", table->OpromSegment); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + dprintf(3, "PnPInstallationCheck %04x:%04x\n", + table->PnPInstallationCheckSegment, + table->PnPInstallationCheckOffset); + dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment); + + rom = MAKE_FLATPTR(table->OpromSegment, 0); + bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction); + + rom_reserve(rom->size * 512); + + // XX PnP seg/ofs should never be other than default + callrom(rom, bdf); + + rom_confirm(rom->size * 512); + + regs->bx = 0; // FIXME + regs->ax = 0; +} + +/* Legacy16GetTableAddress */ +static void +handle_csm_0006(struct bregs *regs) +{ + u16 size = regs->cx; + u16 align = regs->dx; + u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either) + void *chunk = NULL; + + if (!region) + region = 3; + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n", + size, align, region); + + if (region & 2) + chunk = _malloc(&ZoneLow, size, align); + if (!chunk && (region & 1)) + chunk = _malloc(&ZoneFSeg, size, align); + + dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n", + size, align, region, chunk); + if (chunk) { + regs->ds = FLATPTR_TO_SEG(chunk); + regs->bx = FLATPTR_TO_OFFSET(chunk); + regs->ax = 0; + } else { + regs->ax = 1; + } +} + +void VISIBLE32INIT +handle_csm(struct bregs *regs) +{ + ASSERT32FLAT(); + + if (!CONFIG_CSM) + return; + + dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax); + + pic_irqmask_write(PICMask); + + switch(regs->ax) { + case 0000: handle_csm_0000(regs); break; + case 0001: handle_csm_0001(regs); break; + case 0002: handle_csm_0002(regs); break; + case 0003: handle_csm_0003(regs); break; +// case 0004: handle_csm_0004(regs); break; + case 0005: handle_csm_0005(regs); break; + case 0006: handle_csm_0006(regs); break; +// case 0007: handle_csm_0007(regs); break; +// case 0008: hamdle_csm_0008(regs); break; + default: regs->al = 1; + } + + csm_return(regs); +} + +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int index = 1 + (chanid * 2) + slave; + dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave, + index, bbs[index].BootPriority); + return bbs[index].BootPriority; +} + +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority); + return bbs[0].BootPriority; +} + +int csm_bootprio_pci(struct pci_device *pci) +{ + if (!csm_boot_table) + return -1; + BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable; + int i; + + for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) { + if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) { + dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus, + bbs[i].Device, bbs[i].Function, bbs[i].BootPriority); + return bbs[i].BootPriority; + } + } + return -1; +} diff --git a/qemu/roms/seabios/src/fw/dev-piix.h b/qemu/roms/seabios/src/fw/dev-piix.h new file mode 100644 index 000000000..c389f171a --- /dev/null +++ b/qemu/roms/seabios/src/fw/dev-piix.h @@ -0,0 +1,29 @@ +#ifndef __DEV_PIIX_H +#define __DEV_PIIX_H + +#define I440FX_PAM0 0x59 +#define I440FX_SMRAM 0x72 + +#define PIIX_PMBASE 0x40 +#define PIIX_PMREGMISC 0x80 +#define PIIX_SMBHSTBASE 0x90 +#define PIIX_SMBHSTCFG 0xd2 +#define PIIX_DEVACTB 0x58 +#define PIIX_DEVACTB_APMC_EN (1 << 25) + +#define PIIX_PORT_ELCR1 0x4d0 +#define PIIX_PORT_ELCR2 0x4d1 + +/* ICH9 PM I/O registers */ +#define PIIX_GPE0_BLK 0xafe0 +#define PIIX_GPE0_BLK_LEN 4 +#define PIIX_PMIO_GLBCTL 0x28 +#define PIIX_PMIO_GLBCTL_SMI_EN 1 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define PIIX_ACPI_ENABLE 0xf1 +#define PIIX_ACPI_DISABLE 0xf0 + +#define PIIX_PM_INTRRUPT 9 // irq 9 + +#endif // dev-piix.h diff --git a/qemu/roms/seabios/src/fw/dev-q35.h b/qemu/roms/seabios/src/fw/dev-q35.h new file mode 100644 index 000000000..c6f8bd9e7 --- /dev/null +++ b/qemu/roms/seabios/src/fw/dev-q35.h @@ -0,0 +1,49 @@ +#ifndef __DEV_Q35_H +#define __DEV_Q35_H + +#include "types.h" // u16 + +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 +#define Q35_HOST_BRIDGE_PAM0 0x90 +#define Q35_HOST_BRIDGE_SMRAM 0x9d +#define Q35_HOST_BRIDGE_PCIEXBAR 0x60 +#define Q35_HOST_BRIDGE_PCIEXBAR_SIZE (256 * 1024 * 1024) +#define Q35_HOST_BRIDGE_PCIEXBAR_ADDR 0xb0000000 +#define Q35_HOST_BRIDGE_PCIEXBAREN ((u64)1) +#define Q35_HOST_PCIE_PCI_SEGMENT 0 +#define Q35_HOST_PCIE_START_BUS_NUMBER 0 +#define Q35_HOST_PCIE_END_BUS_NUMBER 255 + +#define PCI_DEVICE_ID_INTEL_ICH9_LPC 0x2918 +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_PMBASE_RTE 0x1 + +#define ICH9_LPC_ACPI_CTRL 0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_GEN_PMCON_1 0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) +#define ICH9_LPC_PORT_ELCR1 0x4d0 +#define ICH9_LPC_PORT_ELCR2 0x4d1 +#define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 +#define ICH9_SMB_SMB_BASE 0x20 +#define ICH9_SMB_HOSTC 0x40 +#define ICH9_SMB_HOSTC_HST_EN 0x01 + +#define ICH9_ACPI_ENABLE 0x2 +#define ICH9_ACPI_DISABLE 0x3 + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_GPE0_STS 0x20 +#define ICH9_PMIO_GPE0_BLK_LEN 0x10 +#define ICH9_PMIO_SMI_EN 0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_EN_GLB_SMI_EN (1 << 0) + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE 0x2 +#define ICH9_APM_ACPI_DISABLE 0x3 + +#endif // dev-q35.h diff --git a/qemu/roms/seabios/src/fw/lzmadecode.c b/qemu/roms/seabios/src/fw/lzmadecode.c new file mode 100644 index 000000000..65819b53c --- /dev/null +++ b/qemu/roms/seabios/src/fw/lzmadecode.c @@ -0,0 +1,398 @@ +/* + LzmaDecode.c + LZMA Decoder (optimized for Speed version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "lzmadecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *cp = probs + res; RC_GET_BIT(cp, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + + *inSizeProcessed = 0; + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + RC_INIT(inStream, inSize); + + + while(nowPos < outSize) + { + CProb *prob; + UInt32 bound; + int posState = (int)( + (nowPos + ) + & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + matchByte = outStream[nowPos - rep0]; + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (Byte)symbol; + + outStream[nowPos++] = previousByte; + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + UpdateBit0(prob); + + if (nowPos == 0) + return LZMA_RESULT_DATA_ERROR; + + state = state < kNumLitStates ? 9 : 11; + previousByte = outStream[nowPos - rep0]; + outStream[nowPos++] = previousByte; + + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + if (rep0 > nowPos) + return LZMA_RESULT_DATA_ERROR; + + + do + { + previousByte = outStream[nowPos - rep0]; + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + RC_NORMALIZE; + + + *inSizeProcessed = (SizeT)(Buffer - inStream); + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/qemu/roms/seabios/src/fw/lzmadecode.h b/qemu/roms/seabios/src/fw/lzmadecode.h new file mode 100644 index 000000000..dedde0de6 --- /dev/null +++ b/qemu/roms/seabios/src/fw/lzmadecode.h @@ -0,0 +1,67 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +typedef unsigned char Byte; +typedef unsigned short UInt16; +typedef unsigned int UInt32; +typedef UInt32 SizeT; + +#define CProb UInt16 + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + + +} CLzmaDecoderState; + + +int LzmaDecode(CLzmaDecoderState *vs, + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/qemu/roms/seabios/src/fw/mptable.c b/qemu/roms/seabios/src/fw/mptable.c new file mode 100644 index 000000000..8e01e0043 --- /dev/null +++ b/qemu/roms/seabios/src/fw/mptable.c @@ -0,0 +1,196 @@ +// MPTable generation (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" +#include "hw/pci_regs.h" +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "std/mptable.h" // MPTABLE_SIGNATURE +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +void +mptable_setup(void) +{ + if (! CONFIG_MPTABLE) + return; + + dprintf(3, "init MPTable\n"); + + // Config structure in temp area. + struct mptable_config_s *config = malloc_tmp(32*1024); + if (!config) { + warn_noalloc(); + return; + } + memset(config, 0, sizeof(*config)); + config->signature = MPCONFIG_SIGNATURE; + config->spec = 4; + memcpy(config->oemid, BUILD_CPUNAME8, sizeof(config->oemid)); + memcpy(config->productid, "0.1 ", sizeof(config->productid)); + config->lapic = BUILD_APIC_ADDR; + + // Detect cpu info + u32 cpuid_signature, ebx, ecx, cpuid_features; + cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); + if (! cpuid_signature) { + // Use default values. + cpuid_signature = 0x600; + cpuid_features = 0x201; + } + int pkgcpus = 1; + if (cpuid_features & (1 << 28)) { + /* Only populate the MPS tables with the first logical CPU in + each package */ + pkgcpus = (ebx >> 16) & 0xff; + pkgcpus = 1 << (__fls(pkgcpus - 1) + 1); /* round up to power of 2 */ + } + u8 apic_version = readl((u8*)BUILD_APIC_ADDR + 0x30) & 0xff; + + // CPU definitions. + struct mpt_cpu *cpus = (void*)&config[1], *cpu = cpus; + int i; + for (i = 0; i < MaxCountCPUs; i+=pkgcpus) { + memset(cpu, 0, sizeof(*cpu)); + cpu->type = MPT_TYPE_CPU; + cpu->apicid = i; + cpu->apicver = apic_version; + /* cpu flags: enabled, bootstrap cpu */ + cpu->cpuflag = (apic_id_is_present(i) ? 0x01 : 0x00) | ((i==0) ? 0x02 : 0x00); + cpu->cpusignature = cpuid_signature; + cpu->featureflag = cpuid_features; + cpu++; + } + int entrycount = cpu - cpus; + + // PCI bus + struct mpt_bus *buses = (void*)cpu, *bus = buses; + if (!hlist_empty(&PCIDevices)) { + memset(bus, 0, sizeof(*bus)); + bus->type = MPT_TYPE_BUS; + bus->busid = 0; + memcpy(bus->bustype, "PCI ", sizeof(bus->bustype)); + bus++; + entrycount++; + } + + /* isa bus */ + int isabusid = bus - buses; + memset(bus, 0, sizeof(*bus)); + bus->type = MPT_TYPE_BUS; + bus->busid = isabusid; + memcpy(bus->bustype, "ISA ", sizeof(bus->bustype)); + bus++; + entrycount++; + + /* ioapic */ + u8 ioapic_id = BUILD_IOAPIC_ID; + struct mpt_ioapic *ioapic = (void*)bus; + memset(ioapic, 0, sizeof(*ioapic)); + ioapic->type = MPT_TYPE_IOAPIC; + ioapic->apicid = ioapic_id; + ioapic->apicver = 0x11; + ioapic->flags = 1; // enable + ioapic->apicaddr = BUILD_IOAPIC_ADDR; + entrycount++; + + /* irqs */ + struct mpt_intsrc *intsrcs = (void*)&ioapic[1], *intsrc = intsrcs; + int dev = -1; + unsigned short pinmask = 0; + + struct pci_device *pci; + foreachpci(pci) { + u16 bdf = pci->bdf; + if (pci_bdf_to_bus(bdf) != 0) + break; + int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); + int irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + if (pin == 0) + continue; + if (dev != pci_bdf_to_busdev(bdf)) { + dev = pci_bdf_to_busdev(bdf); + pinmask = 0; + } + if (pinmask & (1 << pin)) /* pin was seen already */ + continue; + pinmask |= (1 << pin); + memset(intsrc, 0, sizeof(*intsrc)); + intsrc->type = MPT_TYPE_INTSRC; + intsrc->irqtype = 0; /* INT */ + intsrc->irqflag = 1; /* active high */ + intsrc->srcbus = pci_bdf_to_bus(bdf); /* PCI bus */ + intsrc->srcbusirq = (pci_bdf_to_dev(bdf) << 2) | (pin - 1); + intsrc->dstapic = ioapic_id; + intsrc->dstirq = irq; + intsrc++; + } + + int irq0_override = romfile_loadint("etc/irq0-override", 0); + for (i = 0; i < 16; i++) { + memset(intsrc, 0, sizeof(*intsrc)); + if (BUILD_PCI_IRQS & (1 << i)) + continue; + intsrc->type = MPT_TYPE_INTSRC; + intsrc->irqtype = 0; /* INT */ + intsrc->irqflag = 0; /* conform to bus spec */ + intsrc->srcbus = isabusid; /* ISA bus */ + intsrc->srcbusirq = i; + intsrc->dstapic = ioapic_id; + intsrc->dstirq = i; + if (irq0_override) { + /* Destination 2 is covered by irq0->inti2 override (i == + 0). Source IRQ 2 is unused */ + if (i == 0) + intsrc->dstirq = 2; + else if (i == 2) + intsrc--; + } + intsrc++; + } + + /* Local interrupt assignment */ + intsrc->type = MPT_TYPE_LOCAL_INT; + intsrc->irqtype = 3; /* ExtINT */ + intsrc->irqflag = 0; /* PO, EL default */ + intsrc->srcbus = isabusid; /* ISA */ + intsrc->srcbusirq = 0; + intsrc->dstapic = 0; /* BSP == APIC #0 */ + intsrc->dstirq = 0; /* LINTIN0 */ + intsrc++; + + intsrc->type = MPT_TYPE_LOCAL_INT; + intsrc->irqtype = 1; /* NMI */ + intsrc->irqflag = 0; /* PO, EL default */ + intsrc->srcbus = isabusid; /* ISA */ + intsrc->srcbusirq = 0; + intsrc->dstapic = 0xff; /* to all local APICs */ + intsrc->dstirq = 1; /* LINTIN1 */ + intsrc++; + entrycount += intsrc - intsrcs; + + // Finalize config structure. + int length = (void*)intsrc - (void*)config; + config->entrycount = entrycount; + config->length = length; + config->checksum -= checksum(config, length); + + // floating pointer structure + struct mptable_floating_s floating; + memset(&floating, 0, sizeof(floating)); + floating.signature = MPTABLE_SIGNATURE; + floating.physaddr = (u32)config; + floating.length = 1; + floating.spec_rev = 4; + floating.checksum -= checksum(&floating, sizeof(floating)); + copy_mptable(&floating); + free(config); +} diff --git a/qemu/roms/seabios/src/fw/mtrr.c b/qemu/roms/seabios/src/fw/mtrr.c new file mode 100644 index 000000000..913580e7d --- /dev/null +++ b/qemu/roms/seabios/src/fw/mtrr.c @@ -0,0 +1,106 @@ +// Initialize MTRRs - mostly useful on KVM. +// +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pcimem_start +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "util.h" // mtrr_setup +#include "x86.h" // cpuid + +#define MSR_MTRRcap 0x000000fe +#define MSR_MTRRfix64K_00000 0x00000250 +#define MSR_MTRRfix16K_80000 0x00000258 +#define MSR_MTRRfix16K_A0000 0x00000259 +#define MSR_MTRRfix4K_C0000 0x00000268 +#define MSR_MTRRfix4K_C8000 0x00000269 +#define MSR_MTRRfix4K_D0000 0x0000026a +#define MSR_MTRRfix4K_D8000 0x0000026b +#define MSR_MTRRfix4K_E0000 0x0000026c +#define MSR_MTRRfix4K_E8000 0x0000026d +#define MSR_MTRRfix4K_F0000 0x0000026e +#define MSR_MTRRfix4K_F8000 0x0000026f +#define MSR_MTRRdefType 0x000002ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define MTRR_MEMTYPE_UC 0 +#define MTRR_MEMTYPE_WC 1 +#define MTRR_MEMTYPE_WT 4 +#define MTRR_MEMTYPE_WP 5 +#define MTRR_MEMTYPE_WB 6 + +void mtrr_setup(void) +{ + if (!CONFIG_MTRR_INIT) + return; + + u32 eax, ebx, ecx, edx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + if (!(cpuid_features & CPUID_MTRR)) + return; + if (!(cpuid_features & CPUID_MSR)) + return; + + dprintf(3, "init mtrr\n"); + + u32 mtrr_cap = rdmsr(MSR_MTRRcap); + int vcnt = mtrr_cap & 0xff; + int fix = mtrr_cap & 0x100; + if (!vcnt || !fix) + return; + + // Disable MTRRs + wrmsr_smp(MSR_MTRRdefType, 0); + + // Set fixed MTRRs + union u64b { + u8 valb[8]; + u64 val; + } u; + u.val = 0; + int i; + for (i = 0; i < 8; i++) + if (RamSize >= 65536 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WB; + wrmsr_smp(MSR_MTRRfix64K_00000, u.val); + u.val = 0; + for (i = 0; i < 8; i++) + if (RamSize >= 0x80000 + 16384 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WB; + wrmsr_smp(MSR_MTRRfix16K_80000, u.val); + wrmsr_smp(MSR_MTRRfix16K_A0000, 0); // 0xA0000-0xC0000 is uncached + int j; + for (j = 0; j < 8; j++) { + u.val = 0; + for (i = 0; i < 8; i++) + if (RamSize >= 0xC0000 + j * 0x8000 + 4096 * (i + 1)) + u.valb[i] = MTRR_MEMTYPE_WP; + wrmsr_smp(MSR_MTRRfix4K_C0000 + j, u.val); + } + + // Set variable MTRRs + int phys_bits = 36; + cpuid(0x80000000u, &eax, &ebx, &ecx, &edx); + if (eax >= 0x80000008) { + /* Get physical bits from leaf 0x80000008 (if available) */ + cpuid(0x80000008u, &eax, &ebx, &ecx, &edx); + phys_bits = eax & 0xff; + } + u64 phys_mask = ((1ull << phys_bits) - 1); + for (i=0; i<vcnt; i++) { + wrmsr_smp(MTRRphysBase_MSR(i), 0); + wrmsr_smp(MTRRphysMask_MSR(i), 0); + } + /* Mark 3.5-4GB as UC, anything not specified defaults to WB */ + wrmsr_smp(MTRRphysBase_MSR(0), pcimem_start | MTRR_MEMTYPE_UC); + wrmsr_smp(MTRRphysMask_MSR(0) + , (-((1ull<<32)-pcimem_start) & phys_mask) | 0x800); + + // Enable fixed and variable MTRRs; set default type. + wrmsr_smp(MSR_MTRRdefType, 0xc00 | MTRR_MEMTYPE_WB); +} diff --git a/qemu/roms/seabios/src/fw/paravirt.c b/qemu/roms/seabios/src/fw/paravirt.c new file mode 100644 index 000000000..db22ae8fc --- /dev/null +++ b/qemu/roms/seabios/src/fw/paravirt.c @@ -0,0 +1,448 @@ +// Paravirtualization support. +// +// Copyright (C) 2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2009 Red Hat Inc. +// +// Authors: +// Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // be32_to_cpu +#include "config.h" // CONFIG_QEMU +#include "hw/pci.h" // create_pirtable +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "hw/rtc.h" // CMOS_* +#include "malloc.h" // malloc_tmp +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // qemu_cfg_preinit +#include "romfile.h" // romfile_loadint +#include "romfile_loader.h" // romfile_loader_execute +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // cpuid +#include "xen.h" // xen_biostable_setup + +// Amount of continuous ram under 4Gig +u32 RamSize; +// Amount of continuous ram >4Gig +u64 RamSizeOver4G; +// Type of emulator platform. +int PlatformRunningOn VARFSEG; + +/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It + * should be used to determine that a VM is running under KVM. + */ +#define KVM_CPUID_SIGNATURE 0x40000000 + +static void kvm_detect(void) +{ + unsigned int eax, ebx, ecx, edx; + char signature[13]; + + cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx); + memcpy(signature + 0, &ebx, 4); + memcpy(signature + 4, &ecx, 4); + memcpy(signature + 8, &edx, 4); + signature[12] = 0; + + if (strcmp(signature, "KVMKVMKVM") == 0) { + dprintf(1, "Running on KVM\n"); + PlatformRunningOn |= PF_KVM; + } +} + +static void qemu_detect(void) +{ + if (!CONFIG_QEMU_HARDWARE) + return; + + // check northbridge @ 00:00.0 + u16 v = pci_config_readw(0, PCI_VENDOR_ID); + if (v == 0x0000 || v == 0xffff) + return; + u16 d = pci_config_readw(0, PCI_DEVICE_ID); + u16 sv = pci_config_readw(0, PCI_SUBSYSTEM_VENDOR_ID); + u16 sd = pci_config_readw(0, PCI_SUBSYSTEM_ID); + + if (sv != 0x1af4 || /* Red Hat, Inc */ + sd != 0x1100) /* Qemu virtual machine */ + return; + + PlatformRunningOn |= PF_QEMU; + switch (d) { + case 0x1237: + dprintf(1, "Running on QEMU (i440fx)\n"); + break; + case 0x29c0: + dprintf(1, "Running on QEMU (q35)\n"); + break; + default: + dprintf(1, "Running on QEMU (unknown nb: %04x:%04x)\n", v, d); + break; + } + kvm_detect(); +} + +void +qemu_preinit(void) +{ + qemu_detect(); + + if (!CONFIG_QEMU) + return; + + if (runningOnXen()) { + xen_ramsize_preinit(); + return; + } + + if (!runningOnQEMU()) { + dprintf(1, "Warning: No QEMU Northbridge found (isapc?)\n"); + PlatformRunningOn |= PF_QEMU; + kvm_detect(); + } + + // On emulators, get memory size from nvram. + u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16) + | (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24)); + if (rs) + rs += 16 * 1024 * 1024; + else + rs = (((rtc_read(CMOS_MEM_EXTMEM_LOW) << 10) + | (rtc_read(CMOS_MEM_EXTMEM_HIGH) << 18)) + + 1 * 1024 * 1024); + RamSize = rs; + add_e820(0, rs, E820_RAM); + + /* reserve 256KB BIOS area at the end of 4 GB */ + add_e820(0xfffc0000, 256*1024, E820_RESERVED); + + dprintf(1, "RamSize: 0x%08x [cmos]\n", RamSize); +} + +void +qemu_platform_setup(void) +{ + if (!CONFIG_QEMU) + return; + + if (runningOnXen()) { + pci_probe_devices(); + xen_hypercall_setup(); + xen_biostable_setup(); + return; + } + + // Initialize pci + pci_setup(); + smm_device_setup(); + smm_setup(); + + // Initialize mtrr and smp + mtrr_setup(); + smp_setup(); + + // Create bios tables + pirtable_setup(); + mptable_setup(); + smbios_setup(); + + if (CONFIG_FW_ROMFILE_LOAD) { + int loader_err; + + dprintf(3, "load ACPI tables\n"); + + loader_err = romfile_loader_execute("etc/table-loader"); + + RsdpAddr = find_acpi_rsdp(); + + if (RsdpAddr) + return; + + /* If present, loader should have installed an RSDP. + * Not installed? We might still be able to continue + * using the builtin RSDP. + */ + if (!loader_err) + warn_internalerror(); + } + + acpi_setup(); +} + + +/**************************************************************** + * QEMU firmware config (fw_cfg) interface + ****************************************************************/ + +// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content +// should be passed via the fw_cfg "file" interface.) +#define QEMU_CFG_SIGNATURE 0x00 +#define QEMU_CFG_ID 0x01 +#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_NUMA 0x0d +#define QEMU_CFG_BOOT_MENU 0x0e +#define QEMU_CFG_MAX_CPUS 0x0f +#define QEMU_CFG_FILE_DIR 0x19 +#define QEMU_CFG_ARCH_LOCAL 0x8000 +#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) +#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) + +static void +qemu_cfg_select(u16 f) +{ + outw(f, PORT_QEMU_CFG_CTL); +} + +static void +qemu_cfg_read(void *buf, int len) +{ + insb(PORT_QEMU_CFG_DATA, buf, len); +} + +static void +qemu_cfg_skip(int len) +{ + while (len--) + inb(PORT_QEMU_CFG_DATA); +} + +static void +qemu_cfg_read_entry(void *buf, int e, int len) +{ + qemu_cfg_select(e); + qemu_cfg_read(buf, len); +} + +struct qemu_romfile_s { + struct romfile_s file; + int select, skip; +}; + +static int +qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) +{ + if (file->size > maxlen) + return -1; + struct qemu_romfile_s *qfile; + qfile = container_of(file, struct qemu_romfile_s, file); + qemu_cfg_select(qfile->select); + qemu_cfg_skip(qfile->skip); + qemu_cfg_read(dst, file->size); + return file->size; +} + +static void +qemu_romfile_add(char *name, int select, int skip, int size) +{ + struct qemu_romfile_s *qfile = malloc_tmp(sizeof(*qfile)); + if (!qfile) { + warn_noalloc(); + return; + } + memset(qfile, 0, sizeof(*qfile)); + strtcpy(qfile->file.name, name, sizeof(qfile->file.name)); + qfile->file.size = size; + qfile->select = select; + qfile->skip = skip; + qfile->file.copy = qemu_cfg_read_file; + romfile_add(&qfile->file); +} + +struct e820_reservation { + u64 address; + u64 length; + u32 type; +}; + +#define SMBIOS_FIELD_ENTRY 0 +#define SMBIOS_TABLE_ENTRY 1 + +struct qemu_smbios_header { + u16 length; + u8 headertype; + u8 tabletype; + u16 fieldoffset; +} PACKED; + +static void +qemu_cfg_e820(void) +{ + struct e820_reservation *table; + int i, size; + + if (!CONFIG_QEMU) + return; + + // "etc/e820" has both ram and reservations + table = romfile_loadfile("etc/e820", &size); + if (table) { + for (i = 0; i < size / sizeof(struct e820_reservation); i++) { + switch (table[i].type) { + case E820_RAM: + dprintf(1, "RamBlock: addr 0x%016llx len 0x%016llx [e820]\n", + table[i].address, table[i].length); + if (table[i].address < RamSize) + // ignore, preinit got it from cmos already and + // adding this again would ruin any reservations + // done so far + continue; + if (table[i].address < 0x100000000LL) { + // below 4g -- adjust RamSize to mark highest lowram addr + if (RamSize < table[i].address + table[i].length) + RamSize = table[i].address + table[i].length; + } else { + // above 4g -- adjust RamSizeOver4G to mark highest ram addr + if (0x100000000LL + RamSizeOver4G < table[i].address + table[i].length) + RamSizeOver4G = table[i].address + table[i].length - 0x100000000LL; + } + /* fall through */ + case E820_RESERVED: + add_e820(table[i].address, table[i].length, table[i].type); + break; + default: + /* + * Qemu 1.7 uses RAM + RESERVED only. Ignore + * everything else, so we have the option to + * extend this in the future without breakage. + */ + break; + } + } + return; + } + + // QEMU_CFG_E820_TABLE has reservations only + u32 count32; + qemu_cfg_read_entry(&count32, QEMU_CFG_E820_TABLE, sizeof(count32)); + if (count32) { + struct e820_reservation entry; + int i; + for (i = 0; i < count32; i++) { + qemu_cfg_read(&entry, sizeof(entry)); + add_e820(entry.address, entry.length, entry.type); + } + } else if (runningOnKVM()) { + // Backwards compatibility - provide hard coded range. + // 4 pages before the bios, 3 pages for vmx tss pages, the + // other page for EPT real mode pagetable + add_e820(0xfffbc000, 4*4096, E820_RESERVED); + } + + // Check for memory over 4Gig in cmos + u64 high = ((rtc_read(CMOS_MEM_HIGHMEM_LOW) << 16) + | ((u32)rtc_read(CMOS_MEM_HIGHMEM_MID) << 24) + | ((u64)rtc_read(CMOS_MEM_HIGHMEM_HIGH) << 32)); + RamSizeOver4G = high; + add_e820(0x100000000ull, high, E820_RAM); + dprintf(1, "RamSizeOver4G: 0x%016llx [cmos]\n", RamSizeOver4G); +} + +// Populate romfile entries for legacy fw_cfg ports (that predate the +// "file" interface). +static void +qemu_cfg_legacy(void) +{ + if (!CONFIG_QEMU) + return; + + // Misc config items. + qemu_romfile_add("etc/show-boot-menu", QEMU_CFG_BOOT_MENU, 0, 2); + qemu_romfile_add("etc/irq0-override", QEMU_CFG_IRQ0_OVERRIDE, 0, 1); + qemu_romfile_add("etc/max-cpus", QEMU_CFG_MAX_CPUS, 0, 2); + + // NUMA data + u64 numacount; + qemu_cfg_read_entry(&numacount, QEMU_CFG_NUMA, sizeof(numacount)); + int max_cpu = romfile_loadint("etc/max-cpus", 0); + qemu_romfile_add("etc/numa-cpu-map", QEMU_CFG_NUMA, sizeof(numacount) + , max_cpu*sizeof(u64)); + qemu_romfile_add("etc/numa-nodes", QEMU_CFG_NUMA + , sizeof(numacount) + max_cpu*sizeof(u64) + , numacount*sizeof(u64)); + + // ACPI tables + char name[128]; + u16 cnt; + qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt)); + int i, offset = sizeof(cnt); + for (i = 0; i < cnt; i++) { + u16 len; + qemu_cfg_read(&len, sizeof(len)); + offset += sizeof(len); + snprintf(name, sizeof(name), "acpi/table%d", i); + qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); + qemu_cfg_skip(len); + offset += len; + } + + // SMBIOS info + qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt)); + offset = sizeof(cnt); + for (i = 0; i < cnt; i++) { + struct qemu_smbios_header header; + qemu_cfg_read(&header, sizeof(header)); + if (header.headertype == SMBIOS_FIELD_ENTRY) { + snprintf(name, sizeof(name), "smbios/field%d-%d" + , header.tabletype, header.fieldoffset); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + sizeof(header) + , header.length - sizeof(header)); + } else { + snprintf(name, sizeof(name), "smbios/table%d-%d" + , header.tabletype, i); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + 3, header.length - 3); + } + qemu_cfg_skip(header.length - sizeof(header)); + offset += header.length; + } +} + +struct QemuCfgFile { + u32 size; /* file size */ + u16 select; /* write this to 0x510 to read it */ + u16 reserved; + char name[56]; +}; + +void qemu_cfg_init(void) +{ + if (!runningOnQEMU()) + return; + + // Detect fw_cfg interface. + qemu_cfg_select(QEMU_CFG_SIGNATURE); + char *sig = "QEMU"; + int i; + for (i = 0; i < 4; i++) + if (inb(PORT_QEMU_CFG_DATA) != sig[i]) + return; + dprintf(1, "Found QEMU fw_cfg\n"); + + // Populate romfiles for legacy fw_cfg entries + qemu_cfg_legacy(); + + // Load files found in the fw_cfg file directory + u32 count; + qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count)); + count = be32_to_cpu(count); + u32 e; + for (e = 0; e < count; e++) { + struct QemuCfgFile qfile; + qemu_cfg_read(&qfile, sizeof(qfile)); + qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) + , 0, be32_to_cpu(qfile.size)); + } + + qemu_cfg_e820(); + + if (romfile_find("etc/table-loader")) { + acpi_pm_base = 0x0600; + dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base); + } +} diff --git a/qemu/roms/seabios/src/fw/paravirt.h b/qemu/roms/seabios/src/fw/paravirt.h new file mode 100644 index 000000000..95ffb92ad --- /dev/null +++ b/qemu/roms/seabios/src/fw/paravirt.h @@ -0,0 +1,37 @@ +#ifndef __PV_H +#define __PV_H + +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL + +// Types of paravirtualized platforms. +#define PF_QEMU (1<<0) +#define PF_XEN (1<<1) +#define PF_KVM (1<<2) + +extern u32 RamSize; +extern u64 RamSizeOver4G; +extern int PlatformRunningOn; + +static inline int runningOnQEMU(void) { + return CONFIG_QEMU || ( + CONFIG_QEMU_HARDWARE && GET_GLOBAL(PlatformRunningOn) & PF_QEMU); +} +static inline int runningOnXen(void) { + return CONFIG_XEN && GET_GLOBAL(PlatformRunningOn) & PF_XEN; +} +static inline int runningOnKVM(void) { + return CONFIG_QEMU && GET_GLOBAL(PlatformRunningOn) & PF_KVM; +} + +// Common paravirt ports. +#define PORT_SMI_CMD 0x00b2 +#define PORT_SMI_STATUS 0x00b3 +#define PORT_QEMU_CFG_CTL 0x0510 +#define PORT_QEMU_CFG_DATA 0x0511 + +void qemu_preinit(void); +void qemu_platform_setup(void); +void qemu_cfg_init(void); + +#endif diff --git a/qemu/roms/seabios/src/fw/pciinit.c b/qemu/roms/seabios/src/fw/pciinit.c new file mode 100644 index 000000000..46ae7090e --- /dev/null +++ b/qemu/roms/seabios/src/fw/pciinit.c @@ -0,0 +1,954 @@ +// Initialize PCI devices (on emulators) +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "byteorder.h" // le64_to_cpu +#include "config.h" // CONFIG_* +#include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR +#include "dev-piix.h" // PIIX_* +#include "hw/ata.h" // PORT_ATA1_CMD_BASE +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_COMMAND +#include "list.h" // struct hlist_node +#include "malloc.h" // free +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_loadint +#include "string.h" // memset +#include "util.h" // pci_setup +#include "x86.h" // outb + +#define PCI_DEVICE_MEM_MIN (1<<12) // 4k == page size +#define PCI_BRIDGE_MEM_MIN (1<<21) // 2M == hugepage size +#define PCI_BRIDGE_IO_MIN 0x1000 // mandated by pci bridge spec + +static const char *region_type_name[] = { + [ PCI_REGION_TYPE_IO ] = "io", + [ PCI_REGION_TYPE_MEM ] = "mem", + [ PCI_REGION_TYPE_PREFMEM ] = "prefmem", +}; + +u64 pcimem_start = BUILD_PCIMEM_START; +u64 pcimem_end = BUILD_PCIMEM_END; +u64 pcimem64_start = BUILD_PCIMEM64_START; +u64 pcimem64_end = BUILD_PCIMEM64_END; +u64 pci_io_low_end = 0xa000; + +struct pci_region_entry { + struct pci_device *dev; + int bar; + u64 size; + u64 align; + int is64; + enum pci_region_type type; + struct hlist_node node; +}; + +struct pci_region { + /* pci region assignments */ + u64 base; + struct hlist_head list; +}; + +struct pci_bus { + struct pci_region r[PCI_REGION_TYPE_COUNT]; + struct pci_device *bus_dev; +}; + +static u32 pci_bar(struct pci_device *pci, int region_num) +{ + if (region_num != PCI_ROM_SLOT) { + return PCI_BASE_ADDRESS_0 + region_num * 4; + } + +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 + u8 type = pci->header_type & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; +} + +static void +pci_set_io_region_addr(struct pci_device *pci, int bar, u64 addr, int is64) +{ + u32 ofs = pci_bar(pci, bar); + pci_config_writel(pci->bdf, ofs, addr); + if (is64) + pci_config_writel(pci->bdf, ofs + 4, addr >> 32); +} + + +/**************************************************************** + * Misc. device init + ****************************************************************/ + +/* host irqs corresponding to PCI irqs A-D */ +const u8 pci_irqs[4] = { + 10, 10, 11, 11 +}; + +static int dummy_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + dprintf(1, "pci_slot_get_irq called with unknown routing\n"); + + return 0xff; /* PCI defined "unknown" or "no connection" for x86 */ +} + +static int (*pci_slot_get_irq)(struct pci_device *pci, int pin) = + dummy_pci_slot_get_irq; + +// Return the global irq number corresponding to a host bus device irq pin. +static int piix_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int slot_addend = 0; + + while (pci->parent != NULL) { + slot_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + slot_addend += pci_bdf_to_dev(pci->bdf) - 1; + return pci_irqs[(pin - 1 + slot_addend) & 3]; +} + +static int mch_pci_slot_get_irq(struct pci_device *pci, int pin) +{ + int pin_addend = 0; + while (pci->parent != NULL) { + pin_addend += pci_bdf_to_dev(pci->bdf); + pci = pci->parent; + } + u8 slot = pci_bdf_to_dev(pci->bdf); + if (slot <= 24) + /* Slots 0-24 rotate slot:pin mapping similar to piix above, but + with a different starting index - see q35-acpi-dsdt.dsl */ + return pci_irqs[(pin - 1 + pin_addend + slot) & 3]; + /* Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H) */ + return pci_irqs[(pin - 1 + pin_addend) & 3]; +} + +/* PIIX3/PIIX4 PCI to ISA bridge */ +static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) +{ + int i, irq; + u8 elcr[2]; + + elcr[0] = 0x00; + elcr[1] = 0x00; + for (i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + /* activate irq remapping in PIIX */ + pci_config_writeb(pci->bdf, 0x60 + i, irq); + } + outb(elcr[0], PIIX_PORT_ELCR1); + outb(elcr[1], PIIX_PORT_ELCR2); + dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); +} + +/* ICH9 LPC PCI to ISA bridge */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +static void mch_isa_bridge_setup(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + int i, irq; + u8 elcr[2]; + + elcr[0] = 0x00; + elcr[1] = 0x00; + + for (i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + + /* activate irq remapping in LPC */ + + /* PIRQ[A-D] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, irq); + /* PIRQ[E-H] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, irq); + } + outb(elcr[0], ICH9_LPC_PORT_ELCR1); + outb(elcr[1], ICH9_LPC_PORT_ELCR2); + dprintf(1, "Q35 LPC init: elcr=%02x %02x\n", elcr[0], elcr[1]); + + /* pm io base */ + pci_config_writel(bdf, ICH9_LPC_PMBASE, + acpi_pm_base | ICH9_LPC_PMBASE_RTE); + + /* acpi enable, SCI: IRQ9 000b = irq9*/ + pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); + + acpi_pm1a_cnt = acpi_pm_base + 0x04; + pmtimer_setup(acpi_pm_base + 0x08); +} + +static void storage_ide_setup(struct pci_device *pci, void *arg) +{ + /* IDE: we map it as in ISA mode */ + pci_set_io_region_addr(pci, 0, PORT_ATA1_CMD_BASE, 0); + pci_set_io_region_addr(pci, 1, PORT_ATA1_CTRL_BASE, 0); + pci_set_io_region_addr(pci, 2, PORT_ATA2_CMD_BASE, 0); + pci_set_io_region_addr(pci, 3, PORT_ATA2_CTRL_BASE, 0); +} + +/* PIIX3/PIIX4 IDE */ +static void piix_ide_setup(struct pci_device *pci, void *arg) +{ + u16 bdf = pci->bdf; + pci_config_writew(bdf, 0x40, 0x8000); // enable IDE0 + pci_config_writew(bdf, 0x42, 0x8000); // enable IDE1 +} + +static void pic_ibm_setup(struct pci_device *pci, void *arg) +{ + /* PIC, IBM, MPIC & MPIC2 */ + pci_set_io_region_addr(pci, 0, 0x80800000 + 0x00040000, 0); +} + +static void apple_macio_setup(struct pci_device *pci, void *arg) +{ + /* macio bridge */ + pci_set_io_region_addr(pci, 0, 0x80800000, 0); +} + +static void piix4_pm_config_setup(u16 bdf) +{ + // acpi sci is hardwired to 9 + pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9); + + pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); + pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ + pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); + pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ +} + +static int PiixPmBDF = -1; + +/* PIIX4 Power Management device (for ACPI) */ +static void piix4_pm_setup(struct pci_device *pci, void *arg) +{ + PiixPmBDF = pci->bdf; + piix4_pm_config_setup(pci->bdf); + + acpi_pm1a_cnt = acpi_pm_base + 0x04; + pmtimer_setup(acpi_pm_base + 0x08); +} + +/* ICH9 SMBUS */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ +static void ich9_smbus_setup(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + /* map smbus into io space */ + pci_config_writel(bdf, ICH9_SMB_SMB_BASE, + (acpi_pm_base + 0x100) | PCI_BASE_ADDRESS_SPACE_IO); + + /* enable SMBus */ + pci_config_writeb(bdf, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN); +} + +static const struct pci_device_id pci_device_tbl[] = { + /* PIIX3/PIIX4 PCI to ISA bridge */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, + piix_isa_bridge_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, + piix_isa_bridge_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + mch_isa_bridge_setup), + + /* STORAGE IDE */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, + PCI_CLASS_STORAGE_IDE, piix_ide_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, + PCI_CLASS_STORAGE_IDE, piix_ide_setup), + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, + storage_ide_setup), + + /* PIC, IBM, MIPC & MPIC2 */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC, + pic_ibm_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC, + pic_ibm_setup), + + /* PIIX4 Power Management device (for ACPI) */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + piix4_pm_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS, + ich9_smbus_setup), + + /* 0xff00 */ + PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_setup), + PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_setup), + + PCI_DEVICE_END, +}; + +void pci_resume(void) +{ + if (!CONFIG_QEMU) { + return; + } + + if (PiixPmBDF >= 0) { + piix4_pm_config_setup(PiixPmBDF); + } +} + +static void pci_bios_init_device(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "PCI: init bdf=%02x:%02x.%x id=%04x:%04x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) + , pci->vendor, pci->device); + + /* map the interrupt */ + int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); + if (pin != 0) + pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pci_slot_get_irq(pci, pin)); + + pci_init_device(pci_device_tbl, pci, NULL); + + /* enable memory mappings */ + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR); +} + +static void pci_bios_init_devices(void) +{ + struct pci_device *pci; + foreachpci(pci) { + pci_bios_init_device(pci); + } +} + +static void pci_enable_default_vga(void) +{ + struct pci_device *pci; + + foreachpci(pci) { + if (is_pci_vga(pci)) { + dprintf(1, "PCI: Using %02x:%02x.%x for primary VGA\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + return; + } + } + + pci = pci_find_class(PCI_CLASS_DISPLAY_VGA); + if (!pci) { + dprintf(1, "PCI: No VGA devices found\n"); + return; + } + + dprintf(1, "PCI: Enabling %02x:%02x.%x for primary VGA\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + while (pci->parent) { + pci = pci->parent; + + dprintf(1, "PCI: Setting VGA enable on bridge %02x:%02x.%x\n", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf)); + + pci_config_maskw(pci->bdf, PCI_BRIDGE_CONTROL, 0, PCI_BRIDGE_CTL_VGA); + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + } +} + +/**************************************************************** + * Platform device initialization + ****************************************************************/ + +static void i440fx_mem_addr_setup(struct pci_device *dev, void *arg) +{ + if (RamSize <= 0x80000000) + pcimem_start = 0x80000000; + else if (RamSize <= 0xc0000000) + pcimem_start = 0xc0000000; + + pci_slot_get_irq = piix_pci_slot_get_irq; +} + +static void mch_mem_addr_setup(struct pci_device *dev, void *arg) +{ + u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; + u32 size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + + /* setup mmconfig */ + u16 bdf = dev->bdf; + u32 upper = addr >> 32; + u32 lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); + add_e820(addr, size, E820_RESERVED); + + /* setup pci i/o window (above mmconfig) */ + pcimem_start = addr + size; + + pci_slot_get_irq = mch_pci_slot_get_irq; + + /* setup io address space */ + if (acpi_pm_base < 0x1000) + pci_io_low_end = 0x10000; + else + pci_io_low_end = acpi_pm_base; +} + +static const struct pci_device_id pci_platform_tbl[] = { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, + i440fx_mem_addr_setup), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mem_addr_setup), + PCI_DEVICE_END +}; + +static void pci_bios_init_platform(void) +{ + struct pci_device *pci; + foreachpci(pci) { + pci_init_device(pci_platform_tbl, pci, NULL); + } +} + + +/**************************************************************** + * Bus initialization + ****************************************************************/ + +static void +pci_bios_init_bus_rec(int bus, u8 *pci_bus) +{ + int bdf; + u16 class; + + dprintf(1, "PCI: %s bus = 0x%x\n", __func__, bus); + + /* prevent accidental access to unintended devices */ + foreachbdf(bdf, bus) { + class = pci_config_readw(bdf, PCI_CLASS_DEVICE); + if (class == PCI_CLASS_BRIDGE_PCI) { + pci_config_writeb(bdf, PCI_SECONDARY_BUS, 255); + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); + } + } + + foreachbdf(bdf, bus) { + class = pci_config_readw(bdf, PCI_CLASS_DEVICE); + if (class != PCI_CLASS_BRIDGE_PCI) { + continue; + } + dprintf(1, "PCI: %s bdf = 0x%x\n", __func__, bdf); + + u8 pribus = pci_config_readb(bdf, PCI_PRIMARY_BUS); + if (pribus != bus) { + dprintf(1, "PCI: primary bus = 0x%x -> 0x%x\n", pribus, bus); + pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); + } else { + dprintf(1, "PCI: primary bus = 0x%x\n", pribus); + } + + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + (*pci_bus)++; + if (*pci_bus != secbus) { + dprintf(1, "PCI: secondary bus = 0x%x -> 0x%x\n", + secbus, *pci_bus); + secbus = *pci_bus; + pci_config_writeb(bdf, PCI_SECONDARY_BUS, secbus); + } else { + dprintf(1, "PCI: secondary bus = 0x%x\n", secbus); + } + + /* set to max for access to all subordinate buses. + later set it to accurate value */ + u8 subbus = pci_config_readb(bdf, PCI_SUBORDINATE_BUS); + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); + + pci_bios_init_bus_rec(secbus, pci_bus); + + if (subbus != *pci_bus) { + dprintf(1, "PCI: subordinate bus = 0x%x -> 0x%x\n", + subbus, *pci_bus); + subbus = *pci_bus; + } else { + dprintf(1, "PCI: subordinate bus = 0x%x\n", subbus); + } + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, subbus); + } +} + +static void +pci_bios_init_bus(void) +{ + u8 extraroots = romfile_loadint("etc/extra-pci-roots", 0); + u8 pci_bus = 0; + + pci_bios_init_bus_rec(0 /* host bus */, &pci_bus); + + if (extraroots) { + while (pci_bus < 0xff) { + pci_bus++; + pci_bios_init_bus_rec(pci_bus, &pci_bus); + } + } +} + + +/**************************************************************** + * Bus sizing + ****************************************************************/ + +static void +pci_bios_get_bar(struct pci_device *pci, int bar, + int *ptype, u64 *psize, int *pis64) +{ + u32 ofs = pci_bar(pci, bar); + u16 bdf = pci->bdf; + u32 old = pci_config_readl(bdf, ofs); + int is64 = 0, type = PCI_REGION_TYPE_MEM; + u64 mask; + + if (bar == PCI_ROM_SLOT) { + mask = PCI_ROM_ADDRESS_MASK; + pci_config_writel(bdf, ofs, mask); + } else { + if (old & PCI_BASE_ADDRESS_SPACE_IO) { + mask = PCI_BASE_ADDRESS_IO_MASK; + type = PCI_REGION_TYPE_IO; + } else { + mask = PCI_BASE_ADDRESS_MEM_MASK; + if (old & PCI_BASE_ADDRESS_MEM_PREFETCH) + type = PCI_REGION_TYPE_PREFMEM; + is64 = ((old & PCI_BASE_ADDRESS_MEM_TYPE_MASK) + == PCI_BASE_ADDRESS_MEM_TYPE_64); + } + pci_config_writel(bdf, ofs, ~0); + } + u64 val = pci_config_readl(bdf, ofs); + pci_config_writel(bdf, ofs, old); + if (is64) { + u32 hold = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, ~0); + u32 high = pci_config_readl(bdf, ofs + 4); + pci_config_writel(bdf, ofs + 4, hold); + val |= ((u64)high << 32); + mask |= ((u64)0xffffffff << 32); + *psize = (~(val & mask)) + 1; + } else { + *psize = ((~(val & mask)) + 1) & 0xffffffff; + } + *ptype = type; + *pis64 = is64; +} + +static int pci_bios_bridge_region_is64(struct pci_region *r, + struct pci_device *pci, int type) +{ + if (type != PCI_REGION_TYPE_PREFMEM) + return 0; + u32 pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); + if (!pmem) { + pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0xfff0fff0); + pmem = pci_config_readl(pci->bdf, PCI_PREF_MEMORY_BASE); + pci_config_writel(pci->bdf, PCI_PREF_MEMORY_BASE, 0x0); + } + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) != PCI_PREF_RANGE_TYPE_64) + return 0; + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + if (!entry->is64) + return 0; + } + return 1; +} + +static u64 pci_region_align(struct pci_region *r) +{ + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + // The first entry in the sorted list has the largest alignment + return entry->align; + } + return 1; +} + +static u64 pci_region_sum(struct pci_region *r) +{ + u64 sum = 0; + struct pci_region_entry *entry; + hlist_for_each_entry(entry, &r->list, node) { + sum += entry->size; + } + return sum; +} + +static void pci_region_migrate_64bit_entries(struct pci_region *from, + struct pci_region *to) +{ + struct hlist_node *n, **last = &to->list.first; + struct pci_region_entry *entry; + hlist_for_each_entry_safe(entry, n, &from->list, node) { + if (!entry->is64) + continue; + if (entry->dev->class == PCI_CLASS_SERIAL_USB) + continue; + // Move from source list to destination list. + hlist_del(&entry->node); + hlist_add(&entry->node, last); + last = &entry->node.next; + } +} + +static struct pci_region_entry * +pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, + int bar, u64 size, u64 align, int type, int is64) +{ + struct pci_region_entry *entry = malloc_tmp(sizeof(*entry)); + if (!entry) { + warn_noalloc(); + return NULL; + } + memset(entry, 0, sizeof(*entry)); + entry->dev = dev; + entry->bar = bar; + entry->size = size; + entry->align = align; + entry->is64 = is64; + entry->type = type; + // Insert into list in sorted order. + struct hlist_node **pprev; + struct pci_region_entry *pos; + hlist_for_each_entry_pprev(pos, pprev, &bus->r[type].list, node) { + if (pos->align < align || (pos->align == align && pos->size < size)) + break; + } + hlist_add(&entry->node, pprev); + return entry; +} + +static int pci_bus_hotplug_support(struct pci_bus *bus) +{ + u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP); + u8 shpc_cap; + + if (pcie_cap) { + u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf, + pcie_cap + PCI_EXP_FLAGS); + u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >> + (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1)); + u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) || + (port_type == PCI_EXP_TYPE_ROOT_PORT); + /* + * PCI Express SPEC, 7.8.2: + * Slot Implemented – When Set, this bit indicates that the Link + * HwInit associated with this Port is connected to a slot (as + * compared to being connected to a system-integrated device or + * being disabled). + * This bit is valid for Downstream Ports. This bit is undefined + * for Upstream Ports. + */ + u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT; + + return downstream_port && slot_implemented; + } + + shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC); + return !!shpc_cap; +} + +static int pci_bios_check_devices(struct pci_bus *busses) +{ + dprintf(1, "PCI: check devices\n"); + + // Calculate resources needed for regular (non-bus) devices. + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == PCI_CLASS_BRIDGE_PCI) + busses[pci->secondary_bus].bus_dev = pci; + + struct pci_bus *bus = &busses[pci_bdf_to_bus(pci->bdf)]; + if (!bus->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + bus = &busses[0]; + int i; + for (i = 0; i < PCI_NUM_REGIONS; i++) { + if ((pci->class == PCI_CLASS_BRIDGE_PCI) && + (i >= PCI_BRIDGE_NUM_REGIONS && i < PCI_ROM_SLOT)) + continue; + int type, is64; + u64 size; + pci_bios_get_bar(pci, i, &type, &size, &is64); + if (size == 0) + continue; + + if (type != PCI_REGION_TYPE_IO && size < PCI_DEVICE_MEM_MIN) + size = PCI_DEVICE_MEM_MIN; + struct pci_region_entry *entry = pci_region_create_entry( + bus, pci, i, size, size, type, is64); + if (!entry) + return -1; + + if (is64) + i++; + } + } + + // Propagate required bus resources to parent busses. + int secondary_bus; + for (secondary_bus=MaxPCIBus; secondary_bus>0; secondary_bus--) { + struct pci_bus *s = &busses[secondary_bus]; + if (!s->bus_dev) + continue; + struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; + if (!parent->bus_dev) + /* + * Resources for all root busses go in busses[0] + */ + parent = &busses[0]; + int type; + int hotplug_support = pci_bus_hotplug_support(s); + for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { + u64 align = (type == PCI_REGION_TYPE_IO) ? + PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; + if (!pci_bridge_has_region(s->bus_dev, type)) + continue; + if (pci_region_align(&s->r[type]) > align) + align = pci_region_align(&s->r[type]); + u64 sum = pci_region_sum(&s->r[type]); + if (!sum && hotplug_support) + sum = align; /* reserve min size for hot-plug */ + u64 size = ALIGN(sum, align); + int is64 = pci_bios_bridge_region_is64(&s->r[type], + s->bus_dev, type); + // entry->bar is -1 if the entry represents a bridge region + struct pci_region_entry *entry = pci_region_create_entry( + parent, s->bus_dev, -1, size, align, type, is64); + if (!entry) + return -1; + dprintf(1, "PCI: secondary bus %d size %08llx type %s\n", + entry->dev->secondary_bus, size, + region_type_name[entry->type]); + } + } + return 0; +} + + +/**************************************************************** + * BAR assignment + ****************************************************************/ + +// Setup region bases (given the regions' size and alignment) +static int pci_bios_init_root_regions_io(struct pci_bus *bus) +{ + /* + * QEMU I/O address space usage: + * 0000 - 0fff legacy isa, pci config, pci root bus, ... + * 1000 - 9fff free + * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) + * b000 - bfff power management (PORT_ACPI_PM_BASE) + * [ qemu 1.4+ implements pci config registers + * properly so guests can place the registers + * where they want, on older versions its fixed ] + * c000 - ffff free, traditionally used for pci io + */ + struct pci_region *r_io = &bus->r[PCI_REGION_TYPE_IO]; + u64 sum = pci_region_sum(r_io); + if (sum < 0x4000) { + /* traditional region is big enougth, use it */ + r_io->base = 0xc000; + } else if (sum < pci_io_low_end - 0x1000) { + /* use the larger region at 0x1000 */ + r_io->base = 0x1000; + } else { + /* not enouth io address space -> error out */ + return -1; + } + dprintf(1, "PCI: IO: %4llx - %4llx\n", r_io->base, r_io->base + sum - 1); + return 0; +} + +static int pci_bios_init_root_regions_mem(struct pci_bus *bus) +{ + struct pci_region *r_end = &bus->r[PCI_REGION_TYPE_PREFMEM]; + struct pci_region *r_start = &bus->r[PCI_REGION_TYPE_MEM]; + + if (pci_region_align(r_start) < pci_region_align(r_end)) { + // Swap regions to improve alignment. + r_end = r_start; + r_start = &bus->r[PCI_REGION_TYPE_PREFMEM]; + } + u64 sum = pci_region_sum(r_end); + u64 align = pci_region_align(r_end); + r_end->base = ALIGN_DOWN((pcimem_end - sum), align); + sum = pci_region_sum(r_start); + align = pci_region_align(r_start); + r_start->base = ALIGN_DOWN((r_end->base - sum), align); + + if ((r_start->base < pcimem_start) || + (r_start->base > pcimem_end)) + // Memory range requested is larger than available. + return -1; + return 0; +} + +#define PCI_IO_SHIFT 8 +#define PCI_MEMORY_SHIFT 16 +#define PCI_PREF_MEMORY_SHIFT 16 + +static void +pci_region_map_one_entry(struct pci_region_entry *entry, u64 addr) +{ + u16 bdf = entry->dev->bdf; + if (entry->bar >= 0) { + dprintf(1, "PCI: map device bdf=%02x:%02x.%x" + " bar %d, addr %08llx, size %08llx [%s]\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), + entry->bar, addr, entry->size, region_type_name[entry->type]); + + pci_set_io_region_addr(entry->dev, entry->bar, addr, entry->is64); + return; + } + + u64 limit = addr + entry->size - 1; + if (entry->type == PCI_REGION_TYPE_IO) { + pci_config_writeb(bdf, PCI_IO_BASE, addr >> PCI_IO_SHIFT); + pci_config_writew(bdf, PCI_IO_BASE_UPPER16, 0); + pci_config_writeb(bdf, PCI_IO_LIMIT, limit >> PCI_IO_SHIFT); + pci_config_writew(bdf, PCI_IO_LIMIT_UPPER16, 0); + } + if (entry->type == PCI_REGION_TYPE_MEM) { + pci_config_writew(bdf, PCI_MEMORY_BASE, addr >> PCI_MEMORY_SHIFT); + pci_config_writew(bdf, PCI_MEMORY_LIMIT, limit >> PCI_MEMORY_SHIFT); + } + if (entry->type == PCI_REGION_TYPE_PREFMEM) { + pci_config_writew(bdf, PCI_PREF_MEMORY_BASE, addr >> PCI_PREF_MEMORY_SHIFT); + pci_config_writew(bdf, PCI_PREF_MEMORY_LIMIT, limit >> PCI_PREF_MEMORY_SHIFT); + pci_config_writel(bdf, PCI_PREF_BASE_UPPER32, addr >> 32); + pci_config_writel(bdf, PCI_PREF_LIMIT_UPPER32, limit >> 32); + } +} + +static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) +{ + struct hlist_node *n; + struct pci_region_entry *entry; + hlist_for_each_entry_safe(entry, n, &r->list, node) { + u64 addr = r->base; + r->base += entry->size; + if (entry->bar == -1) + // Update bus base address if entry is a bridge region + busses[entry->dev->secondary_bus].r[entry->type].base = addr; + pci_region_map_one_entry(entry, addr); + hlist_del(&entry->node); + free(entry); + } +} + +static void pci_bios_map_devices(struct pci_bus *busses) +{ + if (pci_bios_init_root_regions_io(busses)) + panic("PCI: out of I/O address space\n"); + + dprintf(1, "PCI: 32: %016llx - %016llx\n", pcimem_start, pcimem_end); + if (pci_bios_init_root_regions_mem(busses)) { + struct pci_region r64_mem, r64_pref; + r64_mem.list.first = NULL; + r64_pref.list.first = NULL; + pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_MEM], + &r64_mem); + pci_region_migrate_64bit_entries(&busses[0].r[PCI_REGION_TYPE_PREFMEM], + &r64_pref); + + if (pci_bios_init_root_regions_mem(busses)) + panic("PCI: out of 32bit address space\n"); + + u64 sum_mem = pci_region_sum(&r64_mem); + u64 sum_pref = pci_region_sum(&r64_pref); + u64 align_mem = pci_region_align(&r64_mem); + u64 align_pref = pci_region_align(&r64_pref); + + r64_mem.base = le64_to_cpu(romfile_loadint("etc/reserved-memory-end", 0)); + if (r64_mem.base < 0x100000000LL + RamSizeOver4G) + r64_mem.base = 0x100000000LL + RamSizeOver4G; + r64_mem.base = ALIGN(r64_mem.base, align_mem); + r64_mem.base = ALIGN(r64_mem.base, (1LL<<30)); // 1G hugepage + r64_pref.base = r64_mem.base + sum_mem; + r64_pref.base = ALIGN(r64_pref.base, align_pref); + r64_pref.base = ALIGN(r64_pref.base, (1LL<<30)); // 1G hugepage + pcimem64_start = r64_mem.base; + pcimem64_end = r64_pref.base + sum_pref; + pcimem64_end = ALIGN(pcimem64_end, (1LL<<30)); // 1G hugepage + dprintf(1, "PCI: 64: %016llx - %016llx\n", pcimem64_start, pcimem64_end); + + pci_region_map_entries(busses, &r64_mem); + pci_region_map_entries(busses, &r64_pref); + } else { + // no bars mapped high -> drop 64bit window (see dsdt) + pcimem64_start = 0; + } + // Map regions on each device. + int bus; + for (bus = 0; bus<=MaxPCIBus; bus++) { + int type; + for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) + pci_region_map_entries(busses, &busses[bus].r[type]); + } +} + + +/**************************************************************** + * Main setup code + ****************************************************************/ + +void +pci_setup(void) +{ + if (!CONFIG_QEMU) + return; + + dprintf(3, "pci setup\n"); + + dprintf(1, "=== PCI bus & bridge init ===\n"); + if (pci_probe_host() != 0) { + return; + } + pci_bios_init_bus(); + + dprintf(1, "=== PCI device probing ===\n"); + pci_probe_devices(); + + pcimem_start = RamSize; + pci_bios_init_platform(); + + dprintf(1, "=== PCI new allocation pass #1 ===\n"); + struct pci_bus *busses = malloc_tmp(sizeof(*busses) * (MaxPCIBus + 1)); + if (!busses) { + warn_noalloc(); + return; + } + memset(busses, 0, sizeof(*busses) * (MaxPCIBus + 1)); + if (pci_bios_check_devices(busses)) + return; + + dprintf(1, "=== PCI new allocation pass #2 ===\n"); + pci_bios_map_devices(busses); + + pci_bios_init_devices(); + + free(busses); + + pci_enable_default_vga(); +} diff --git a/qemu/roms/seabios/src/fw/pirtable.c b/qemu/roms/seabios/src/fw/pirtable.c new file mode 100644 index 000000000..a49440808 --- /dev/null +++ b/qemu/roms/seabios/src/fw/pirtable.c @@ -0,0 +1,103 @@ +// PIR table generation (for emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "std/pirtable.h" // struct pir_header +#include "string.h" // checksum +#include "util.h" // PirAddr + +struct pir_table { + struct pir_header pir; + struct pir_slot slots[6]; +} PACKED; + +static struct pir_table PIR_TABLE = { + .pir = { + .version = 0x0100, + .size = sizeof(struct pir_table), + .router_devfunc = 0x08, + .compatible_devid = 0x122e8086, + }, + .slots = { + { + // first slot entry PCI-to-ISA (embedded) + .dev = 1<<3, + .links = { + {.link = 0x60, .bitmap = 0xdef8}, // INTA# + {.link = 0x61, .bitmap = 0xdef8}, // INTB# + {.link = 0x62, .bitmap = 0xdef8}, // INTC# + {.link = 0x63, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 0, // embedded + }, { + // second slot entry: 1st PCI slot + .dev = 2<<3, + .links = { + {.link = 0x61, .bitmap = 0xdef8}, // INTA# + {.link = 0x62, .bitmap = 0xdef8}, // INTB# + {.link = 0x63, .bitmap = 0xdef8}, // INTC# + {.link = 0x60, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 1, + }, { + // third slot entry: 2nd PCI slot + .dev = 3<<3, + .links = { + {.link = 0x62, .bitmap = 0xdef8}, // INTA# + {.link = 0x63, .bitmap = 0xdef8}, // INTB# + {.link = 0x60, .bitmap = 0xdef8}, // INTC# + {.link = 0x61, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 2, + }, { + // 4th slot entry: 3rd PCI slot + .dev = 4<<3, + .links = { + {.link = 0x63, .bitmap = 0xdef8}, // INTA# + {.link = 0x60, .bitmap = 0xdef8}, // INTB# + {.link = 0x61, .bitmap = 0xdef8}, // INTC# + {.link = 0x62, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 3, + }, { + // 5th slot entry: 4rd PCI slot + .dev = 5<<3, + .links = { + {.link = 0x60, .bitmap = 0xdef8}, // INTA# + {.link = 0x61, .bitmap = 0xdef8}, // INTB# + {.link = 0x62, .bitmap = 0xdef8}, // INTC# + {.link = 0x63, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 4, + }, { + // 6th slot entry: 5rd PCI slot + .dev = 6<<3, + .links = { + {.link = 0x61, .bitmap = 0xdef8}, // INTA# + {.link = 0x62, .bitmap = 0xdef8}, // INTB# + {.link = 0x63, .bitmap = 0xdef8}, // INTC# + {.link = 0x60, .bitmap = 0xdef8}, // INTD# + }, + .slot_nr = 5, + }, + } +}; + +void +pirtable_setup(void) +{ + if (! CONFIG_PIRTABLE) + return; + + dprintf(3, "init PIR table\n"); + + PIR_TABLE.pir.signature = PIR_SIGNATURE; + PIR_TABLE.pir.checksum -= checksum(&PIR_TABLE, sizeof(PIR_TABLE)); + copy_pir(&PIR_TABLE); +} diff --git a/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl b/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl new file mode 100644 index 000000000..5dec54146 --- /dev/null +++ b/qemu/roms/seabios/src/fw/q35-acpi-dsdt.dsl @@ -0,0 +1,450 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * Copyright (c) 2010 Isaku Yamahata + * yamahata at valinux co jp + * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset. + */ + +DefinitionBlock ( + "q35-acpi-dsdt.aml",// Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x2 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + Scope(\_SB) { + OperationRegion(PCST, SystemIO, 0xae00, 0x0c) + OperationRegion(PCSB, SystemIO, 0xae0c, 0x01) + Field(PCSB, AnyAcc, NoLock, WriteAsZeros) { + PCIB, 8, + } + } + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A08")) + Name(_CID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + + // _OSC: based on sample of ACPI3.0b spec + Name(SUPP, 0) // PCI _OSC Support Field value + Name(CTRL, 0) // PCI _OSC Control Field value + Method(_OSC, 4) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 0, CDW1) + + // Check for proper UUID + If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 4, CDW2) + CreateDWordField(Arg3, 8, CDW3) + + // Save Capabilities DWORD2 & 3 + Store(CDW2, SUPP) + Store(CDW3, CTRL) + + // Always allow native PME, AER (no dependencies) + // Never allow SHPC (no SHPC controller in this system) + And(CTRL, 0x1D, CTRL) + +#if 0 // For now, nothing to do + If (Not(And(CDW1, 1))) { // Query flag clear? + // Disable GPEs for features granted native control. + If (And(CTRL, 0x01)) { // Hot plug control granted? + Store(0, HPCE) // clear the hot plug SCI enable bit + Store(1, HPCS) // clear the hot plug SCI status bit + } + If (And(CTRL, 0x04)) { // PME control granted? + Store(0, PMCE) // clear the PME SCI enable bit + Store(1, PMCS) // clear the PME SCI status bit + } + If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure? + // Set status to not restore PCI Express cap structure + // upon resume from S3 + Store(1, S3CR) + } + } +#endif + If (LNotEqual(Arg1, One)) { + // Unknown revision + Or(CDW1, 0x08, CDW1) + } + If (LNotEqual(CDW3, CTRL)) { + // Capabilities bits were masked + Or(CDW1, 0x10, CDW1) + } + // Update DWORD3 in the buffer + Store(CTRL, CDW3) + } Else { + Or(CDW1, 4, CDW1) // Unrecognized UUID + } + Return (Arg3) + } + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00010000) + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + Return (0x00) + } + } + } + + +/**************************************************************** + * LPC ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + /* PCI D31:f0 LPC ISA bridge */ + Device(ISA) { + /* PCI D31:f0 */ + Name(_ADR, 0x001f0000) + + /* ICH9 PCI to ISA irq remapping */ + OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C) + + OperationRegion(LPCD, PCI_Config, 0x80, 0x2) + Field(LPCD, AnyAcc, NoLock, Preserve) { + COMA, 3, + , 1, + COMB, 3, + + Offset(0x01), + LPTD, 2, + , 2, + FDCD, 2 + } + OperationRegion(LPCE, PCI_Config, 0x82, 0x2) + Field(LPCE, AnyAcc, NoLock, Preserve) { + CAEN, 1, + CBEN, 1, + LPEN, 1, + FDEN, 1 + } + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + /* Zero => PIC mode, One => APIC Mode */ + Name(\PICF, Zero) + Method(\_PIC, 1, NotSerialized) { + Store(Arg0, \PICF) + } + + Scope(\_SB) { + Scope(PCI0) { +#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB) +#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC) + +#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH) +#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE) +#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF) +#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG) + + Name(PRTP, package() { + prt_slot_lnkE(0x0000), + prt_slot_lnkF(0x0001), + prt_slot_lnkG(0x0002), + prt_slot_lnkH(0x0003), + prt_slot_lnkE(0x0004), + prt_slot_lnkF(0x0005), + prt_slot_lnkG(0x0006), + prt_slot_lnkH(0x0007), + prt_slot_lnkE(0x0008), + prt_slot_lnkF(0x0009), + prt_slot_lnkG(0x000a), + prt_slot_lnkH(0x000b), + prt_slot_lnkE(0x000c), + prt_slot_lnkF(0x000d), + prt_slot_lnkG(0x000e), + prt_slot_lnkH(0x000f), + prt_slot_lnkE(0x0010), + prt_slot_lnkF(0x0011), + prt_slot_lnkG(0x0012), + prt_slot_lnkH(0x0013), + prt_slot_lnkE(0x0014), + prt_slot_lnkF(0x0015), + prt_slot_lnkG(0x0016), + prt_slot_lnkH(0x0017), + prt_slot_lnkE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31 + see the default value of D<N>IR */ + prt_slot_lnkA(0x0019), + prt_slot_lnkA(0x001a), + prt_slot_lnkA(0x001b), + prt_slot_lnkA(0x001c), + prt_slot_lnkA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_lnkE(0x001e), + + prt_slot_lnkA(0x001f) + }) + +#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3) \ + Package() { nr##ffff, 0, gsi0, 0 }, \ + Package() { nr##ffff, 1, gsi1, 0 }, \ + Package() { nr##ffff, 2, gsi2, 0 }, \ + Package() { nr##ffff, 3, gsi3, 0 } + +#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID) +#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA) +#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB) +#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC) + +#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH) +#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE) +#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF) +#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG) + + Name(PRTA, package() { + prt_slot_gsiE(0x0000), + prt_slot_gsiF(0x0001), + prt_slot_gsiG(0x0002), + prt_slot_gsiH(0x0003), + prt_slot_gsiE(0x0004), + prt_slot_gsiF(0x0005), + prt_slot_gsiG(0x0006), + prt_slot_gsiH(0x0007), + prt_slot_gsiE(0x0008), + prt_slot_gsiF(0x0009), + prt_slot_gsiG(0x000a), + prt_slot_gsiH(0x000b), + prt_slot_gsiE(0x000c), + prt_slot_gsiF(0x000d), + prt_slot_gsiG(0x000e), + prt_slot_gsiH(0x000f), + prt_slot_gsiE(0x0010), + prt_slot_gsiF(0x0011), + prt_slot_gsiG(0x0012), + prt_slot_gsiH(0x0013), + prt_slot_gsiE(0x0014), + prt_slot_gsiF(0x0015), + prt_slot_gsiG(0x0016), + prt_slot_gsiH(0x0017), + prt_slot_gsiE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31, but 30 + see the default value of D<N>IR */ + prt_slot_gsiA(0x0019), + prt_slot_gsiA(0x001a), + prt_slot_gsiA(0x001b), + prt_slot_gsiA(0x001c), + prt_slot_gsiA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_gsiE(0x001e), + + prt_slot_gsiA(0x001f) + }) + + Method(_PRT, 0, NotSerialized) { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + If (LEqual(\PICF, Zero)) { + Return (PRTP) + } Else { + Return (PRTA) + } + } + } + + Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { + PRQA, 8, + PRQB, 8, + PRQC, 8, + PRQD, 8, + + Offset(0x08), + PRQE, 8, + PRQF, 8, + PRQG, 8, + PRQH, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, Serialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + Store(And(Arg0, 0x0F), PRRI) + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQA) + define_link(LNKB, 1, PRQB) + define_link(LNKC, 2, PRQC) + define_link(LNKD, 3, PRQD) + define_link(LNKE, 4, PRQE) + define_link(LNKF, 5, PRQF) + define_link(LNKG, 6, PRQG) + define_link(LNKH, 7, PRQH) + +#define define_gsi_link(link, uid, gsi) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Name(_CRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Method(_SRS, 1, NotSerialized) { \ + } \ + } + + define_gsi_link(GSIA, 0, 0x10) + define_gsi_link(GSIB, 0, 0x11) + define_gsi_link(GSIC, 0, 0x12) + define_gsi_link(GSID, 0, 0x13) + define_gsi_link(GSIE, 0, 0x14) + define_gsi_link(GSIF, 0, 0x15) + define_gsi_link(GSIG, 0, 0x16) + define_gsi_link(GSIH, 0, 0x17) + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_L01) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L02) { + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/qemu/roms/seabios/src/fw/romfile_loader.c b/qemu/roms/seabios/src/fw/romfile_loader.c new file mode 100644 index 000000000..f4b17ff90 --- /dev/null +++ b/qemu/roms/seabios/src/fw/romfile_loader.c @@ -0,0 +1,177 @@ +#include "romfile_loader.h" +#include "byteorder.h" // leXX_to_cpu/cpu_to_leXX +#include "util.h" // checksum +#include "string.h" // strcmp +#include "romfile.h" // struct romfile_s +#include "malloc.h" // Zone*, _malloc +#include "output.h" // warn_* + +struct romfile_loader_file { + struct romfile_s *file; + void *data; +}; +struct romfile_loader_files { + int nfiles; + struct romfile_loader_file files[]; +}; + +static struct romfile_loader_file * +romfile_loader_find(const char *name, + struct romfile_loader_files *files) +{ + int i; + if (name[ROMFILE_LOADER_FILESZ - 1]) + return NULL; + for (i = 0; i < files->nfiles; ++i) + if (!strcmp(files->files[i].file->name, name)) + return &files->files[i]; + return NULL; +} + +static void romfile_loader_allocate(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct zone_s *zone; + struct romfile_loader_file *file = &files->files[files->nfiles]; + void *data; + int ret; + unsigned alloc_align = le32_to_cpu(entry->alloc_align); + + if (alloc_align & (alloc_align - 1)) + goto err; + + switch (entry->alloc_zone) { + case ROMFILE_LOADER_ALLOC_ZONE_HIGH: + zone = &ZoneHigh; + break; + case ROMFILE_LOADER_ALLOC_ZONE_FSEG: + zone = &ZoneFSeg; + break; + default: + goto err; + } + if (alloc_align < MALLOC_MIN_ALIGN) + alloc_align = MALLOC_MIN_ALIGN; + if (entry->alloc_file[ROMFILE_LOADER_FILESZ - 1]) + goto err; + file->file = romfile_find(entry->alloc_file); + if (!file->file || !file->file->size) + return; + data = _malloc(zone, file->file->size, alloc_align); + if (!data) { + warn_noalloc(); + return; + } + ret = file->file->copy(file->file, data, file->file->size); + if (ret != file->file->size) + goto file_err; + file->data = data; + files->nfiles++; + return; + +file_err: + free(data); +err: + warn_internalerror(); +} + +static void romfile_loader_add_pointer(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *dest_file; + struct romfile_loader_file *src_file; + unsigned offset = le32_to_cpu(entry->pointer_offset); + u64 pointer = 0; + + dest_file = romfile_loader_find(entry->pointer_dest_file, files); + src_file = romfile_loader_find(entry->pointer_src_file, files); + + if (!dest_file || !src_file || !dest_file->data || !src_file->data || + offset + entry->pointer_size < offset || + offset + entry->pointer_size > dest_file->file->size || + entry->pointer_size < 1 || entry->pointer_size > 8 || + entry->pointer_size & (entry->pointer_size - 1)) + goto err; + + memcpy(&pointer, dest_file->data + offset, entry->pointer_size); + pointer = le64_to_cpu(pointer); + pointer += (unsigned long)src_file->data; + pointer = cpu_to_le64(pointer); + memcpy(dest_file->data + offset, &pointer, entry->pointer_size); + + return; +err: + warn_internalerror(); +} + +static void romfile_loader_add_checksum(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *file; + unsigned offset = le32_to_cpu(entry->cksum_offset); + unsigned start = le32_to_cpu(entry->cksum_start); + unsigned len = le32_to_cpu(entry->cksum_length); + u8 *data; + + file = romfile_loader_find(entry->cksum_file, files); + + if (!file || !file->data || offset >= file->file->size || + start + len < start || start + len > file->file->size) + goto err; + + data = file->data + offset; + *data -= checksum(file->data + start, len); + + return; +err: + warn_internalerror(); +} + +int romfile_loader_execute(const char *name) +{ + struct romfile_loader_entry_s *entry; + int size, offset = 0, nfiles; + struct romfile_loader_files *files; + void *data = romfile_loadfile(name, &size); + if (!data) + return -1; + + if (size % sizeof(*entry)) { + warn_internalerror(); + goto err; + } + + /* (over)estimate the number of files to load. */ + nfiles = size / sizeof(*entry); + files = malloc_tmp(sizeof(*files) + nfiles * sizeof(files->files[0])); + if (!files) { + warn_noalloc(); + goto err; + } + files->nfiles = 0; + + for (offset = 0; offset < size; offset += sizeof(*entry)) { + entry = data + offset; + switch (le32_to_cpu(entry->command)) { + case ROMFILE_LOADER_COMMAND_ALLOCATE: + romfile_loader_allocate(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_POINTER: + romfile_loader_add_pointer(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM: + romfile_loader_add_checksum(entry, files); + default: + /* Skip commands that we don't recognize. */ + break; + } + } + + free(files); + free(data); + return 0; + +err: + free(data); + return -1; +} diff --git a/qemu/roms/seabios/src/fw/romfile_loader.h b/qemu/roms/seabios/src/fw/romfile_loader.h new file mode 100644 index 000000000..15eab2ac6 --- /dev/null +++ b/qemu/roms/seabios/src/fw/romfile_loader.h @@ -0,0 +1,72 @@ +#ifndef __ROMFILE_LOADER_H +#define __ROMFILE_LOADER_H + +#include "types.h" // u8 +#include "util.h" // romfile_s + +#define ROMFILE_LOADER_FILESZ 56 + +/* ROM file linker/loader interface. Linker uses little endian format */ +struct romfile_loader_entry_s { + u32 command; + union { + /* + * COMMAND_ALLOCATE - allocate a table from @alloc_file + * subject to @alloc_align alignment (must be power of 2) + * and @alloc_zone (can be HIGH or FSEG) requirements. + * + * Must appear exactly once for each file, and before + * this file is referenced by any other command. + */ + struct { + char alloc_file[ROMFILE_LOADER_FILESZ]; + u32 alloc_align; + u8 alloc_zone; + }; + + /* + * COMMAND_ADD_POINTER - patch the table (originating from + * @dest_file) at @pointer_offset, by adding a pointer to the table + * originating from @src_file. 1,2,4 or 8 byte unsigned + * addition is used depending on @pointer_size. + */ + struct { + char pointer_dest_file[ROMFILE_LOADER_FILESZ]; + char pointer_src_file[ROMFILE_LOADER_FILESZ]; + u32 pointer_offset; + u8 pointer_size; + }; + + /* + * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by + * @cksum_start and @cksum_length fields, + * and then add the value at @cksum_offset. + * Checksum simply sums -X for each byte X in the range + * using 8-bit math. + */ + struct { + char cksum_file[ROMFILE_LOADER_FILESZ]; + u32 cksum_offset; + u32 cksum_start; + u32 cksum_length; + }; + + /* padding */ + char pad[124]; + }; +}; + +enum { + ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1, + ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2, + ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, +}; + +enum { + ROMFILE_LOADER_ALLOC_ZONE_HIGH = 0x1, + ROMFILE_LOADER_ALLOC_ZONE_FSEG = 0x2, +}; + +int romfile_loader_execute(const char *name); + +#endif diff --git a/qemu/roms/seabios/src/fw/shadow.c b/qemu/roms/seabios/src/fw/shadow.c new file mode 100644 index 000000000..4f00006bf --- /dev/null +++ b/qemu/roms/seabios/src/fw/shadow.c @@ -0,0 +1,171 @@ +// Support for enabling/disabling BIOS ram shadowing. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" // PCI_VENDOR_ID_INTEL +#include "dev-piix.h" // I440FX_PAM0 +#include "hw/pci.h" // pci_config_writeb +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "malloc.h" // rom_get_last +#include "output.h" // dprintf +#include "paravirt.h" // runningOnXen +#include "string.h" // memset +#include "util.h" // make_bios_writable +#include "x86.h" // wbinvd + +// On the emulators, the bios at 0xf0000 is also at 0xffff0000 +#define BIOS_SRC_OFFSET 0xfff00000 + +// Enable shadowing and copy bios. +static void +__make_bios_writable_intel(u16 bdf, u32 pam0) +{ + // Make ram from 0xc0000-0xf0000 writable + int clear = 0; + int i; + for (i=0; i<6; i++) { + u32 pam = pam0 + 1 + i; + int reg = pci_config_readb(bdf, pam); + if (CONFIG_OPTIONROMS_DEPLOYED && (reg & 0x11) != 0x11) { + // Need to copy optionroms to work around qemu implementation + void *mem = (void*)(BUILD_ROM_START + i * 32*1024); + memcpy((void*)BUILD_BIOS_TMP_ADDR, mem, 32*1024); + pci_config_writeb(bdf, pam, 0x33); + memcpy(mem, (void*)BUILD_BIOS_TMP_ADDR, 32*1024); + clear = 1; + } else { + pci_config_writeb(bdf, pam, 0x33); + } + } + if (clear) + memset((void*)BUILD_BIOS_TMP_ADDR, 0, 32*1024); + + // Make ram from 0xf0000-0x100000 writable + int reg = pci_config_readb(bdf, pam0); + pci_config_writeb(bdf, pam0, 0x30); + if (reg & 0x10) + // Ram already present. + return; + + // Copy bios. + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); +} + +static void +make_bios_writable_intel(u16 bdf, u32 pam0) +{ + int reg = pci_config_readb(bdf, pam0); + if (!(reg & 0x10)) { + // QEMU doesn't fully implement the piix shadow capabilities - + // if ram isn't backing the bios segment when shadowing is + // disabled, the code itself wont be in memory. So, run the + // code from the high-memory flash location. + u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; + void (*func)(u16 bdf, u32 pam0) = (void*)pos; + func(bdf, pam0); + return; + } + // Ram already present - just enable writes + __make_bios_writable_intel(bdf, pam0); +} + +static void +make_bios_readonly_intel(u16 bdf, u32 pam0) +{ + // Flush any pending writes before locking memory. + wbinvd(); + + // Write protect roms from 0xc0000-0xf0000 + u32 romlast = BUILD_BIOS_ADDR, rommax = BUILD_BIOS_ADDR; + if (CONFIG_WRITABLE_UPPERMEMORY) + romlast = rom_get_last(); + if (CONFIG_MALLOC_UPPERMEMORY) + rommax = rom_get_max(); + int i; + for (i=0; i<6; i++) { + u32 mem = BUILD_ROM_START + i * 32*1024; + u32 pam = pam0 + 1 + i; + if (romlast < mem + 16*1024 || rommax < mem + 32*1024) { + if (romlast >= mem && rommax >= mem + 16*1024) + pci_config_writeb(bdf, pam, 0x31); + break; + } + pci_config_writeb(bdf, pam, 0x11); + } + + // Write protect 0xf0000-0x100000 + pci_config_writeb(bdf, pam0, 0x10); +} + +static int ShadowBDF = -1; + +// Make the 0xc0000-0x100000 area read/writable. +void +make_bios_writable(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + + dprintf(3, "enabling shadow ram\n"); + + // At this point, statically allocated variables can't be written, + // so do this search manually. + int bdf; + foreachbdf(bdf, 0) { + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + u16 vendor = vendev & 0xffff, device = vendev >> 16; + if (vendor == PCI_VENDOR_ID_INTEL + && device == PCI_DEVICE_ID_INTEL_82441) { + make_bios_writable_intel(bdf, I440FX_PAM0); + ShadowBDF = bdf; + return; + } + if (vendor == PCI_VENDOR_ID_INTEL + && device == PCI_DEVICE_ID_INTEL_Q35_MCH) { + make_bios_writable_intel(bdf, Q35_HOST_BRIDGE_PAM0); + ShadowBDF = bdf; + return; + } + } + dprintf(1, "Unable to unlock ram - bridge not found\n"); +} + +// Make the BIOS code segment area (0xf0000) read-only. +void +make_bios_readonly(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + dprintf(3, "locking shadow ram\n"); + + if (ShadowBDF < 0) { + dprintf(1, "Unable to lock ram - bridge not found\n"); + return; + } + + u16 device = pci_config_readw(ShadowBDF, PCI_DEVICE_ID); + if (device == PCI_DEVICE_ID_INTEL_82441) + make_bios_readonly_intel(ShadowBDF, I440FX_PAM0); + else + make_bios_readonly_intel(ShadowBDF, Q35_HOST_BRIDGE_PAM0); +} + +void +qemu_prep_reset(void) +{ + if (!CONFIG_QEMU || runningOnXen()) + return; + // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a + // reset, so do that manually before invoking a hard reset. + make_bios_writable(); + extern u8 code32flat_start[], code32flat_end[]; + memcpy(code32flat_start, code32flat_start + BIOS_SRC_OFFSET + , code32flat_end - code32flat_start); +} diff --git a/qemu/roms/seabios/src/fw/smbios.c b/qemu/roms/seabios/src/fw/smbios.c new file mode 100644 index 000000000..dba054133 --- /dev/null +++ b/qemu/roms/seabios/src/fw/smbios.c @@ -0,0 +1,585 @@ +// smbios table generation (on emulators) +// DO NOT ADD NEW FEATURES HERE. (See paravirt.c / biostables.c instead.) +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "paravirt.h" // RamSize +#include "romfile.h" // romfile_findprefix +#include "std/smbios.h" // struct smbios_entry_point +#include "string.h" // memset +#include "util.h" // MaxCountCPUs +#include "x86.h" // cpuid + +static void +smbios_entry_point_setup(u16 max_structure_size, + u16 structure_table_length, + void *structure_table_address, + u16 number_of_structures) +{ + void *finaltable; + if (structure_table_length <= BUILD_MAX_SMBIOS_FSEG) + // Table is small enough for f-seg - allocate there. This + // works around a bug in JunOS (at least for small SMBIOS tables). + finaltable = malloc_fseg(structure_table_length); + else + finaltable = malloc_high(structure_table_length); + if (!finaltable) { + warn_noalloc(); + return; + } + memcpy(finaltable, structure_table_address, structure_table_length); + + struct smbios_entry_point ep; + memset(&ep, 0, sizeof(ep)); + memcpy(ep.anchor_string, "_SM_", 4); + ep.length = 0x1f; + ep.smbios_major_version = 2; + ep.smbios_minor_version = 4; + ep.max_structure_size = max_structure_size; + memcpy(ep.intermediate_anchor_string, "_DMI_", 5); + + ep.structure_table_length = structure_table_length; + ep.structure_table_address = (u32)finaltable; + ep.number_of_structures = number_of_structures; + ep.smbios_bcd_revision = 0x24; + + ep.checksum -= checksum(&ep, 0x10); + + ep.intermediate_checksum -= checksum((void*)&ep + 0x10, ep.length - 0x10); + + copy_smbios(&ep); +} + +static int +get_field(int type, int offset, void *dest) +{ + char name[128]; + snprintf(name, sizeof(name), "smbios/field%d-%d", type, offset); + struct romfile_s *file = romfile_find(name); + if (!file) + return 0; + file->copy(file, dest, file->size); + return file->size; +} + +static int +get_external(int type, char **p, unsigned *nr_structs, + unsigned *max_struct_size, char *end) +{ + static u64 used_bitmap[4] = { 0 }; + char *start = *p; + + /* Check if we've already reported these tables */ + if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f))) + return 1; + + /* Don't introduce spurious end markers */ + if (type == 127) + return 0; + + char prefix[128]; + snprintf(prefix, sizeof(prefix), "smbios/table%d-", type); + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix(prefix, file); + if (!file) + break; + + if (end - *p < file->size) { + warn_noalloc(); + break; + } + + struct smbios_structure_header *header = (void*)*p; + file->copy(file, header, file->size); + *p += file->size; + + /* Entries end with a double NULL char, if there's a string at + * the end (length is greater than formatted length), the string + * terminator provides the first NULL. */ + *((u8*)*p) = 0; + (*p)++; + if (header->length >= file->size) { + *((u8*)*p) = 0; + (*p)++; + } + + (*nr_structs)++; + if (*p - (char*)header > *max_struct_size) + *max_struct_size = *p - (char*)header; + } + + if (start == *p) + return 0; + + /* Mark that we've reported on this type */ + used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f)); + return 1; +} + +#define load_str_field_with_default(type, field, def) \ + do { \ + size = get_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size == 1) { \ + /* zero-length string, skip to avoid bogus end marker */ \ + p->field = 0; \ + } else if (size > 1) { \ + end += size; \ + p->field = ++str_index; \ + } else { \ + memcpy(end, def, sizeof(def)); \ + end += sizeof(def); \ + p->field = ++str_index; \ + } \ + } while (0) + +#define load_str_field_or_skip(type, field) \ + do { \ + size = get_field(type, offsetof(struct smbios_type_##type, \ + field), end); \ + if (size > 1) { \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ + } while (0) + +#define set_field_with_default(type, field, def) \ + do { \ + if (!get_field(type, offsetof(struct smbios_type_##type, \ + field), &p->field)) { \ + p->field = def; \ + } \ + } while (0) + +/* Type 0 -- BIOS Information */ +#define RELEASE_DATE_STR "01/01/2011" +static void * +smbios_init_type_0(void *start) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + size_t size; + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + load_str_field_with_default(0, vendor_str, BUILD_APPNAME); + load_str_field_with_default(0, bios_version_str, BUILD_APPNAME); + + p->bios_starting_address_segment = 0xe800; + + load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR); + + p->bios_rom_size = 0; /* FIXME */ + + if (!get_field(0, offsetof(struct smbios_type_0, bios_characteristics), + &p->bios_characteristics)) { + memset(p->bios_characteristics, 0, 8); + /* BIOS characteristics not supported */ + p->bios_characteristics[0] = 0x08; + } + + if (!get_field(0, offsetof(struct smbios_type_0, + bios_characteristics_extension_bytes), + &p->bios_characteristics_extension_bytes)) { + p->bios_characteristics_extension_bytes[0] = 0; + /* Enable targeted content distribution. Needed for SVVP */ + p->bios_characteristics_extension_bytes[1] = 4; + } + + set_field_with_default(0, system_bios_major_release, 1); + set_field_with_default(0, system_bios_minor_release, 0); + set_field_with_default(0, embedded_controller_major_release, 0xff); + set_field_with_default(0, embedded_controller_minor_release, 0xff); + + *end = 0; + end++; + + return end; +} + +/* Type 1 -- System Information */ +static void * +smbios_init_type_1(void *start) +{ + struct smbios_type_1 *p = (struct smbios_type_1 *)start; + char *end = (char *)start + sizeof(struct smbios_type_1); + size_t size; + int str_index = 0; + + p->header.type = 1; + p->header.length = sizeof(struct smbios_type_1); + p->header.handle = 0x100; + + load_str_field_with_default(1, manufacturer_str, BUILD_APPNAME); + load_str_field_with_default(1, product_name_str, BUILD_APPNAME); + load_str_field_or_skip(1, version_str); + load_str_field_or_skip(1, serial_number_str); + + if (!get_field(1, offsetof(struct smbios_type_1, uuid), &p->uuid)) + memset(p->uuid, 0, 16); + + set_field_with_default(1, wake_up_type, 0x06); /* power switch */ + + load_str_field_or_skip(1, sku_number_str); + load_str_field_or_skip(1, family_str); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 3 -- System Enclosure */ +static void * +smbios_init_type_3(void *start) +{ + struct smbios_type_3 *p = (struct smbios_type_3 *)start; + char *end = (char *)start + sizeof(struct smbios_type_3); + size_t size; + int str_index = 0; + + p->header.type = 3; + p->header.length = sizeof(struct smbios_type_3); + p->header.handle = 0x300; + + load_str_field_with_default(3, manufacturer_str, BUILD_APPNAME); + set_field_with_default(3, type, 0x01); /* other */ + + load_str_field_or_skip(3, version_str); + load_str_field_or_skip(3, serial_number_str); + load_str_field_or_skip(3, asset_tag_number_str); + + set_field_with_default(3, boot_up_state, 0x03); /* safe */ + set_field_with_default(3, power_supply_state, 0x03); /* safe */ + set_field_with_default(3, thermal_state, 0x03); /* safe */ + set_field_with_default(3, security_status, 0x02); /* unknown */ + + set_field_with_default(3, oem_defined, 0); + set_field_with_default(3, height, 0); + set_field_with_default(3, number_of_power_cords, 0); + set_field_with_default(3, contained_element_count, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 4 -- Processor Information */ +static void * +smbios_init_type_4(void *start, unsigned int cpu_number) +{ + struct smbios_type_4 *p = (struct smbios_type_4 *)start; + char *end = (char *)start + sizeof(struct smbios_type_4); + size_t size; + int str_index = 0; + char name[1024]; + + p->header.type = 4; + p->header.length = sizeof(struct smbios_type_4); + p->header.handle = 0x400 + cpu_number; + + size = get_field(4, offsetof(struct smbios_type_4, socket_designation_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%2x", cpu_number); + else + snprintf(name, sizeof(name), "CPU%2x", cpu_number); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->socket_designation_str = ++str_index; + + set_field_with_default(4, processor_type, 0x03); /* CPU */ + set_field_with_default(4, processor_family, 0x01); /* other */ + + load_str_field_with_default(4, processor_manufacturer_str, BUILD_APPNAME); + + if (!get_field(4, offsetof(struct smbios_type_4, processor_id) + , p->processor_id)) { + u32 cpuid_signature, ebx, ecx, cpuid_features; + cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features); + p->processor_id[0] = cpuid_signature; + p->processor_id[1] = cpuid_features; + } + + load_str_field_or_skip(4, processor_version_str); + set_field_with_default(4, voltage, 0); + set_field_with_default(4, external_clock, 0); + + set_field_with_default(4, max_speed, 2000); + set_field_with_default(4, current_speed, 2000); + + set_field_with_default(4, status, 0x41); /* socket populated, CPU enabled */ + set_field_with_default(4, processor_upgrade, 0x01); /* other */ + + /* cache information structure not provided */ + p->l1_cache_handle = 0xffff; + p->l2_cache_handle = 0xffff; + p->l3_cache_handle = 0xffff; + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 16 -- Physical Memory Array */ +static void * +smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs) +{ + struct smbios_type_16 *p = (struct smbios_type_16*)start; + + p->header.type = 16; + p->header.length = sizeof(struct smbios_type_16); + p->header.handle = 0x1000; + + set_field_with_default(16, location, 0x01); /* other */ + set_field_with_default(16, use, 0x03); /* system memory */ + /* Multi-bit ECC to make Microsoft happy */ + set_field_with_default(16, error_correction, 0x06); + /* 0x80000000 = unknown, accept sizes < 2TB - TODO multiple arrays */ + p->maximum_capacity = memory_size_mb < 2 << 20 ? + memory_size_mb << 10 : 0x80000000; + p->memory_error_information_handle = 0xfffe; /* none provided */ + p->number_of_memory_devices = nr_mem_devs; + + start += sizeof(struct smbios_type_16); + *((u16 *)start) = 0; + + return start + 2; +} + +/* Type 17 -- Memory Device */ +static void * +smbios_init_type_17(void *start, u32 size_mb, int instance) +{ + struct smbios_type_17 *p = (struct smbios_type_17 *)start; + char *end = (char *)start + sizeof(struct smbios_type_17); + size_t size; + int str_index = 0; + char name[1024]; + + p->header.type = 17; + p->header.length = sizeof(struct smbios_type_17); + p->header.handle = 0x1100 + instance; + + p->physical_memory_array_handle = 0x1000; + set_field_with_default(17, total_width, 64); + set_field_with_default(17, data_width, 64); +/* TODO: should assert in case something is wrong ASSERT((memory_size_mb & ~0x7fff) == 0); */ + p->size = size_mb; + set_field_with_default(17, form_factor, 0x09); /* DIMM */ + p->device_set = 0; + + size = get_field(17, offsetof(struct smbios_type_17, device_locator_str), + name); + if (size) + snprintf(name + size - 1, sizeof(name) - size, "%d", instance); + else + snprintf(name, sizeof(name), "DIMM %d", instance); + + memcpy(end, name, strlen(name) + 1); + end += strlen(name) + 1; + p->device_locator_str = ++str_index; + + load_str_field_or_skip(17, bank_locator_str); + set_field_with_default(17, memory_type, 0x07); /* RAM */ + set_field_with_default(17, type_detail, 0); + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } + + return end; +} + +/* Type 19 -- Memory Array Mapped Address */ +static void * +smbios_init_type_19(void *start, u32 start_mb, u32 size_mb, int instance) +{ + struct smbios_type_19 *p = (struct smbios_type_19 *)start; + + p->header.type = 19; + p->header.length = sizeof(struct smbios_type_19); + p->header.handle = 0x1300 + instance; + + p->starting_address = start_mb << 10; + p->ending_address = p->starting_address + (size_mb << 10) - 1; + p->memory_array_handle = 0x1000; + p->partition_width = 1; + + start += sizeof(struct smbios_type_19); + *((u16 *)start) = 0; + + return start + 2; +} + +/* Type 20 -- Memory Device Mapped Address */ +static void * +smbios_init_type_20(void *start, u32 start_mb, u32 size_mb, int instance, + int dev_handle, int array_handle) +{ + struct smbios_type_20 *p = (struct smbios_type_20 *)start; + + p->header.type = 20; + p->header.length = sizeof(struct smbios_type_20); + p->header.handle = 0x1400 + instance; + + p->starting_address = start_mb << 10; + p->ending_address = p->starting_address + (size_mb << 10) - 1; + p->memory_device_handle = 0x1100 + dev_handle; + p->memory_array_mapped_address_handle = 0x1300 + array_handle; + p->partition_row_position = 1; + p->interleave_position = 0; + p->interleaved_data_depth = 0; + + start += sizeof(struct smbios_type_20); + + *((u16 *)start) = 0; + return start+2; +} + +/* Type 32 -- System Boot Information */ +static void * +smbios_init_type_32(void *start) +{ + struct smbios_type_32 *p = (struct smbios_type_32 *)start; + + p->header.type = 32; + p->header.length = sizeof(struct smbios_type_32); + p->header.handle = 0x2000; + memset(p->reserved, 0, 6); + set_field_with_default(32, boot_status, 0); /* no errors detected */ + + start += sizeof(struct smbios_type_32); + *((u16 *)start) = 0; + + return start+2; +} + +/* Type 127 -- End of Table */ +static void * +smbios_init_type_127(void *start) +{ + struct smbios_type_127 *p = (struct smbios_type_127 *)start; + + p->header.type = 127; + p->header.length = sizeof(struct smbios_type_127); + p->header.handle = 0x7f00; + + start += sizeof(struct smbios_type_127); + *((u16 *)start) = 0; + + return start + 2; +} + +#define TEMPSMBIOSSIZE (32 * 1024) + +void +smbios_legacy_setup(void) +{ + if (! CONFIG_SMBIOS) + return; + + dprintf(3, "init SMBIOS tables\n"); + + char *start = malloc_tmphigh(TEMPSMBIOSSIZE); + if (! start) { + warn_noalloc(); + return; + } + memset(start, 0, TEMPSMBIOSSIZE); + + u32 nr_structs = 0, max_struct_size = 0; + char *q, *p = start; + char *end = start + TEMPSMBIOSSIZE - sizeof(struct smbios_type_127); + +#define add_struct(type, args...) \ + do { \ + if (!get_external(type, &p, &nr_structs, &max_struct_size, end)) { \ + q = smbios_init_type_##type(args); \ + nr_structs++; \ + if ((q - p) > max_struct_size) \ + max_struct_size = q - p; \ + p = q; \ + } \ + } while (0) + + add_struct(0, p); + add_struct(1, p); + add_struct(3, p); + + int cpu_num; + for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++) + add_struct(4, p, cpu_num); + + int ram_mb = (RamSize + RamSizeOver4G) >> 20; + int nr_mem_devs = (ram_mb + 0x3fff) >> 14; + add_struct(16, p, ram_mb, nr_mem_devs); + + int i, j; + for (i = 0; i < nr_mem_devs; i++) { + u32 dev_mb = ((i == (nr_mem_devs - 1)) + ? (((ram_mb - 1) & 0x3fff) + 1) + : 16384); + add_struct(17, p, dev_mb, i); + } + + add_struct(19, p, 0, RamSize >> 20, 0); + if (RamSizeOver4G) + add_struct(19, p, 4096, RamSizeOver4G >> 20, 1); + + add_struct(20, p, 0, RamSize >> 20, 0, 0, 0); + if (RamSizeOver4G) { + u32 start_mb = 4096; + for (j = 1, i = 0; i < nr_mem_devs; i++, j++) { + u32 dev_mb = ((i == (nr_mem_devs - 1)) + ? (((ram_mb - 1) & 0x3fff) + 1) + : 16384); + if (i == 0) + dev_mb -= RamSize >> 20; + + add_struct(20, p, start_mb, dev_mb, j, i, 1); + start_mb += dev_mb; + } + } + + add_struct(32, p); + /* Add any remaining provided entries before the end marker */ + for (i = 0; i < 256; i++) + get_external(i, &p, &nr_structs, &max_struct_size, end); + add_struct(127, p); + +#undef add_struct + + smbios_entry_point_setup(max_struct_size, p - start, start, nr_structs); + free(start); +} diff --git a/qemu/roms/seabios/src/fw/smm.c b/qemu/roms/seabios/src/fw/smm.c new file mode 100644 index 000000000..6cb484e7e --- /dev/null +++ b/qemu/roms/seabios/src/fw/smm.c @@ -0,0 +1,259 @@ +// System Management Mode support (on emulators) +// +// Copyright (C) 2008-2014 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "dev-q35.h" +#include "dev-piix.h" +#include "hw/pci.h" // pci_config_writel +#include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL +#include "hw/pci_regs.h" // PCI_DEVICE_ID +#include "output.h" // dprintf +#include "paravirt.h" // PORT_SMI_STATUS +#include "stacks.h" // HaveSmmCall32 +#include "string.h" // memcpy +#include "util.h" // smm_setup +#include "x86.h" // wbinvd + +/* + * Check SMM state save area format (bits 0-15) and require support + * for SMBASE relocation. + */ +#define SMM_REV_MASK 0x0002ffff + +#define SMM_REV_I32 0x00020000 +#define SMM_REV_I64 0x00020064 + +struct smm_state { + union { + struct { + u8 pad_000[0xf8]; + u32 smm_base; + u32 smm_rev; + u8 pad_100[0xd0]; + u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags; + u8 pad_1f8[0x08]; + } i32; + struct { + u8 pad_000[0xfc]; + u32 smm_rev; + u32 smm_base; + u8 pad_104[0x6c]; + u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8; + u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; + } i64; + }; +}; + +struct smm_layout { + struct smm_state backup1; + struct smm_state backup2; + u8 stack[0x7c00]; + u64 codeentry; + u8 pad_8008[0x7df8]; + struct smm_state cpu; +}; + +void VISIBLE32FLAT +handle_smi(u16 cs) +{ + if (!CONFIG_USE_SMM) + return; + u8 cmd = inb(PORT_SMI_CMD); + struct smm_layout *smm = MAKE_FLATPTR(cs, 0); + dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm); + + if (smm == (void*)BUILD_SMM_INIT_ADDR) { + // relocate SMBASE to 0xa0000 + u32 rev = smm->cpu.i32.smm_rev & SMM_REV_MASK; + if (rev == SMM_REV_I32) { + smm->cpu.i32.smm_base = BUILD_SMM_ADDR; + } else if (rev == SMM_REV_I64) { + smm->cpu.i64.smm_base = BUILD_SMM_ADDR; + } else { + warn_internalerror(); + return; + } + // indicate to smm_relocate_and_restore() that the SMM code was executed + outb(0x00, PORT_SMI_STATUS); + + if (CONFIG_CALL32_SMM) { + // Backup current cpu state for SMM trampolining + struct smm_layout *newsmm = (void*)BUILD_SMM_ADDR; + memcpy(&newsmm->backup1, &smm->cpu, sizeof(newsmm->backup1)); + memcpy(&newsmm->backup2, &smm->cpu, sizeof(newsmm->backup2)); + HaveSmmCall32 = 1; + } + + return; + } + + if (CONFIG_CALL32_SMM && cmd == CALL32SMM_CMDID) { + if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + u32 regs[8]; + memcpy(regs, &smm->cpu.i32.eax, sizeof(regs)); + if (smm->cpu.i32.ecx == CALL32SMM_ENTERID) { + dprintf(9, "smm cpu call pc=%x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } else if (smm->cpu.i32.ecx == CALL32SMM_RETURNID) { + dprintf(9, "smm cpu ret %x esp=%x\n", regs[3], regs[4]); + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i32.eax, regs, sizeof(regs)); + smm->cpu.i32.eip = regs[3]; + } + } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + u64 regs[8]; + memcpy(regs, &smm->cpu.i64.rdi, sizeof(regs)); + if ((u32)smm->cpu.i64.rcx == CALL32SMM_ENTERID) { + memcpy(&smm->backup2, &smm->cpu, sizeof(smm->backup2)); + memcpy(&smm->cpu, &smm->backup1, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } else if ((u32)smm->cpu.i64.rcx == CALL32SMM_RETURNID) { + memcpy(&smm->cpu, &smm->backup2, sizeof(smm->cpu)); + memcpy(&smm->cpu.i64.rdi, regs, sizeof(regs)); + smm->cpu.i64.rip = (u32)regs[4]; + } + } + } +} + +extern void entry_smi(void); +// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR) +#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \ + | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24)) + +static void +smm_save_and_copy(void) +{ + // save original memory content + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu)); + memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry)); + + // Setup code entry point. + initsmm->codeentry = SMI_INSN; +} + +static void +smm_relocate_and_restore(void) +{ + /* init APM status port */ + outb(0x01, PORT_SMI_STATUS); + + /* raise an SMI interrupt */ + outb(0x00, PORT_SMI_CMD); + + /* wait until SMM code executed */ + while (inb(PORT_SMI_STATUS) != 0x00) + ; + + /* restore original memory content */ + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu)); + memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry)); + + // Setup code entry point. + smm->codeentry = SMI_INSN; + wbinvd(); +} + +// This code is hardcoded for PIIX4 Power Management device. +static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) +{ + /* check if SMM init is already done */ + u32 value = pci_config_readl(isabdf, PIIX_DEVACTB); + if (value & PIIX_DEVACTB_APMC_EN) + return; + + /* enable the SMM memory window */ + pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x48); + + smm_save_and_copy(); + + /* enable SMI generation when writing to the APMC register */ + pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN); + + /* enable SMI generation */ + value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); + outl(acpi_pm_base + PIIX_PMIO_GLBCTL, value | PIIX_PMIO_GLBCTL_SMI_EN); + + smm_relocate_and_restore(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(i440_bdf, I440FX_SMRAM, 0x02 | 0x08); +} + +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf) +{ + /* check if SMM init is already done */ + u32 value = inl(acpi_pm_base + ICH9_PMIO_SMI_EN); + if (value & ICH9_PMIO_SMI_EN_APMC_EN) + return; + + /* enable the SMM memory window */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48); + + smm_save_and_copy(); + + /* enable SMI generation when writing to the APMC register */ + outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN, + acpi_pm_base + ICH9_PMIO_SMI_EN); + + /* lock SMI generation */ + value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1); + pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1, + value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK); + + smm_relocate_and_restore(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08); +} + +static int SMMISADeviceBDF = -1, SMMPMDeviceBDF = -1; + +void +smm_device_setup(void) +{ + if (!CONFIG_USE_SMM) + return; + + struct pci_device *isapci, *pmpci; + isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3); + pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441); + if (isapci && pmpci) { + SMMISADeviceBDF = isapci->bdf; + SMMPMDeviceBDF = pmpci->bdf; + return; + } + isapci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC); + pmpci = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH); + if (isapci && pmpci) { + SMMISADeviceBDF = isapci->bdf; + SMMPMDeviceBDF = pmpci->bdf; + } +} + +void +smm_setup(void) +{ + if (!CONFIG_USE_SMM || SMMISADeviceBDF < 0) + return; + + dprintf(3, "init smm\n"); + u16 device = pci_config_readw(SMMISADeviceBDF, PCI_DEVICE_ID); + if (device == PCI_DEVICE_ID_INTEL_82371AB_3) + piix4_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); + else + ich9_lpc_apmc_smm_setup(SMMISADeviceBDF, SMMPMDeviceBDF); +} diff --git a/qemu/roms/seabios/src/fw/smp.c b/qemu/roms/seabios/src/fw/smp.c new file mode 100644 index 000000000..a466ea6e9 --- /dev/null +++ b/qemu/roms/seabios/src/fw/smp.c @@ -0,0 +1,153 @@ +// QEMU multi-CPU initialization code +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2006 Fabrice Bellard +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "hw/rtc.h" // CMOS_BIOS_SMP_COUNT +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // smp_setup +#include "x86.h" // wrmsr + +#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300) +#define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0) +#define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350) +#define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360) + +#define APIC_ENABLED 0x0100 + +static struct { u32 index; u64 val; } smp_mtrr[32]; +static u32 smp_mtrr_count; + +void +wrmsr_smp(u32 index, u64 val) +{ + wrmsr(index, val); + if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) { + warn_noalloc(); + return; + } + smp_mtrr[smp_mtrr_count].index = index; + smp_mtrr[smp_mtrr_count].val = val; + smp_mtrr_count++; +} + +u32 MaxCountCPUs; +static u32 CountCPUs; +// 256 bits for the found APIC IDs +static u32 FoundAPICIDs[256/32]; + +int apic_id_is_present(u8 apic_id) +{ + return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32))); +} + +void VISIBLE32FLAT +handle_smp(void) +{ + if (!CONFIG_QEMU) + return; + + // Enable CPU caching + setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + + // Detect apic_id + u32 eax, ebx, ecx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + u8 apic_id = ebx>>24; + dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id); + + // MTRR setup + int i; + for (i=0; i<smp_mtrr_count; i++) + wrmsr(smp_mtrr[i].index, smp_mtrr[i].val); + + // Set bit on FoundAPICIDs + FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); + + CountCPUs++; +} + +// Atomic lock for shared stack across processors. +u32 SMPLock __VISIBLE; +u32 SMPStack __VISIBLE; + +// find and initialize the CPUs by launching a SIPI to them +void +smp_setup(void) +{ + if (!CONFIG_QEMU) + return; + + ASSERT32FLAT(); + u32 eax, ebx, ecx, cpuid_features; + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + if (eax < 1 || !(cpuid_features & CPUID_APIC)) { + // No apic - only the main cpu is present. + dprintf(1, "No apic - only the main cpu is present.\n"); + CountCPUs= 1; + MaxCountCPUs = 1; + return; + } + + // mark the BSP initial APIC ID as found, too: + u8 apic_id = ebx>>24; + FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32)); + CountCPUs = 1; + + // Setup jump trampoline to counter code. + u64 old = *(u64*)BUILD_AP_BOOT_ADDR; + // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR) + extern void entry_smp(void); + u64 new = (0xea | ((u64)SEG_BIOS<<24) + | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8)); + *(u64*)BUILD_AP_BOOT_ADDR = new; + + // enable local APIC + u32 val = readl(APIC_SVR); + writel(APIC_SVR, val | APIC_ENABLED); + + /* Set LINT0 as Ext_INT, level triggered */ + writel(APIC_LINT0, 0x8700); + + /* Set LINT1 as NMI, level triggered */ + writel(APIC_LINT1, 0x8400); + + // Init the lock. + writel(&SMPLock, 1); + + // broadcast SIPI + barrier(); + writel(APIC_ICR_LOW, 0x000C4500); + u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12; + writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector); + + // Wait for other CPUs to process the SIPI. + u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1; + while (cmos_smp_count != CountCPUs) + asm volatile( + // Release lock and allow other processors to use the stack. + " movl %%esp, %1\n" + " movl $0, %0\n" + // Reacquire lock and take back ownership of stack. + "1:rep ; nop\n" + " lock btsl $0, %0\n" + " jc 1b\n" + : "+m" (SMPLock), "+m" (SMPStack) + : : "cc", "memory"); + yield(); + + // Restore memory. + *(u64*)BUILD_AP_BOOT_ADDR = old; + + MaxCountCPUs = romfile_loadint("etc/max-cpus", 0); + if (!MaxCountCPUs || MaxCountCPUs < CountCPUs) + MaxCountCPUs = CountCPUs; + + dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs, + MaxCountCPUs); +} diff --git a/qemu/roms/seabios/src/fw/ssdt-misc.dsl b/qemu/roms/seabios/src/fw/ssdt-misc.dsl new file mode 100644 index 000000000..acc850e84 --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-misc.dsl @@ -0,0 +1,104 @@ +ACPI_EXTRACT_ALL_CODE ssdp_misc_aml + +DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) +{ + +/**************************************************************** + * PCI memory ranges + ****************************************************************/ + + Scope(\) { + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_start + Name(P0S, 0x12345678) + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_end + Name(P0E, 0x12345678) + ACPI_EXTRACT_NAME_BYTE_CONST acpi_pci64_valid + Name(P1V, 0x12) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_start + Name(P1S, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_end + Name(P1E, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_length + Name(P1L, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + } + + +/**************************************************************** + * Suspend + ****************************************************************/ + + Scope(\) { + /* + * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: + * must match piix4 emulation. + */ + + ACPI_EXTRACT_NAME_STRING acpi_s3_name + Name(_S3, Package(0x04) { + One, /* PM1a_CNT.SLP_TYP */ + One, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + ACPI_EXTRACT_NAME_STRING acpi_s4_name + ACPI_EXTRACT_PKG_START acpi_s4_pkg + Name(_S4, Package(0x04) { + 0x2, /* PM1a_CNT.SLP_TYP */ + 0x2, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + Name(_S5, Package(0x04) { + Zero, /* PM1a_CNT.SLP_TYP */ + Zero, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + } + + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.ISA, DeviceObj) + + Scope(\_SB.PCI0.ISA) { + Device(PEVT) { + Name(_HID, "QEMU0001") + /* PEST will be patched to be Zero if no such device */ + ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest + Name(PEST, 0xFFFF) + OperationRegion(PEOR, SystemIO, PEST, 0x01) + Field(PEOR, ByteAcc, NoLock, Preserve) { + PEPT, 8, + } + + Method(_STA, 0, NotSerialized) { + Store(PEST, Local0) + If (LEqual(Local0, Zero)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + + Method(RDPT, 0, NotSerialized) { + Store(PEPT, Local0) + Return (Local0) + } + + Method(WRPT, 1, NotSerialized) { + Store(Arg0, PEPT) + } + + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO) + }) + + CreateWordField(_CRS, IO._MIN, IOMN) + CreateWordField(_CRS, IO._MAX, IOMX) + + Method(_INI, 0, NotSerialized) { + Store(PEST, IOMN) + Store(PEST, IOMX) + } + } + } +} diff --git a/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl b/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl new file mode 100644 index 000000000..cb24c11ce --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-pcihp.dsl @@ -0,0 +1,36 @@ +ACPI_EXTRACT_ALL_CODE ssdp_pcihp_aml + +DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) +{ + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + /* Objects supplied by DSDT */ + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.PCEJ, MethodObj) + + Scope(\_SB.PCI0) { + + /* Bulk generated PCI hotplug devices */ + ACPI_EXTRACT_DEVICE_START ssdt_pcihp_start + ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end + ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name + + // Method _EJ0 can be patched by BIOS to EJ0_ + // at runtime, if the slot is detected to not support hotplug. + // Extract the offset of the address dword and the + // _EJ0 name to allow this patching. + Device(SAA) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id + Name(_SUN, 0xAA) + ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr + Name(_ADR, 0xAA0000) + ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0 + Method(_EJ0, 1) { + PCEJ(_SUN) + } + } + } +} diff --git a/qemu/roms/seabios/src/fw/ssdt-proc.dsl b/qemu/roms/seabios/src/fw/ssdt-proc.dsl new file mode 100644 index 000000000..407d61ead --- /dev/null +++ b/qemu/roms/seabios/src/fw/ssdt-proc.dsl @@ -0,0 +1,48 @@ +/* This file is the basis for the ssdt table generated in src/acpi.c. + * It defines the contents of the per-cpu Processor() object. At + * runtime, a dynamically generated SSDT will contain one copy of this + * AML snippet for every possible cpu in the system. The objects will + * be placed in the \_SB_ namespace. + * + * In addition to the aml code generated from this file, the + * src/acpi.c file creates a NTFY method with an entry for each cpu: + * Method(NTFY, 2) { + * If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) } + * If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) } + * ... + * } + * and a CPON array with the list of active and inactive cpus: + * Name(CPON, Package() { One, One, ..., Zero, Zero, ... }) + */ + +ACPI_EXTRACT_ALL_CODE ssdp_proc_aml + +DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1) +{ + ACPI_EXTRACT_PROCESSOR_START ssdt_proc_start + ACPI_EXTRACT_PROCESSOR_END ssdt_proc_end + ACPI_EXTRACT_PROCESSOR_STRING ssdt_proc_name + Processor(CPAA, 0xAA, 0x0000b010, 0x06) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id + Name(ID, 0xAA) +/* + * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update + * CPAA and 0xAA with the appropriate CPU id (see + * SD_OFFSET_CPUHEX/CPUID1/CPUID2). Don't change the above without + * also updating the C code. + */ + Name(_HID, "ACPI0007") + External(CPMA, MethodObj) + External(CPST, MethodObj) + External(CPEJ, MethodObj) + Method(_MAT, 0) { + Return (CPMA(ID)) + } + Method(_STA, 0) { + Return (CPST(ID)) + } + Method(_EJ0, 1, NotSerialized) { + CPEJ(ID, Arg0) + } + } +} diff --git a/qemu/roms/seabios/src/fw/xen.c b/qemu/roms/seabios/src/fw/xen.c new file mode 100644 index 000000000..dd8e8afd4 --- /dev/null +++ b/qemu/roms/seabios/src/fw/xen.c @@ -0,0 +1,147 @@ +// Xen HVM support +// +// Copyright (C) 2011 Citrix Systems. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" +#include "hw/serialio.h" // DebugOutputPort +#include "malloc.h" // memalign_high +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "paravirt.h" // PlatformRunningOn +#include "string.h" // memcpy +#include "util.h" // copy_acpi_rsdp +#include "x86.h" // cpuid +#include "xen.h" + +#define INFO_PHYSICAL_ADDRESS 0x00001000 + +u32 xen_cpuid_base = 0; +unsigned long xen_hypercall_page = 0; + +struct xen_seabios_info { + char signature[14]; /* XenHVMSeaBIOS\0 */ + u8 length; /* Length of this struct */ + u8 checksum; /* Set such that the sum over bytes 0..length == 0 */ + /* + * Physical address of an array of tables_nr elements. + * + * Each element is a 32 bit value contianing the physical address + * of a BIOS table. + */ + u32 tables; + u32 tables_nr; + /* + * Physical address of the e820 table, contains e820_nr entries. + */ + u32 e820; + u32 e820_nr; +} PACKED; + +static void validate_info(struct xen_seabios_info *t) +{ + if ( memcmp(t->signature, "XenHVMSeaBIOS", 14) ) + panic("Bad Xen info signature\n"); + + if ( t->length < sizeof(struct xen_seabios_info) ) + panic("Bad Xen info length\n"); + + if (checksum(t, t->length) != 0) + panic("Bad Xen info checksum\n"); +} + +void xen_preinit(void) +{ + u32 base, eax, ebx, ecx, edx; + char signature[13]; + + if (!CONFIG_XEN) + return; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) { + cpuid(base, &eax, &ebx, &ecx, &edx); + memcpy(signature + 0, &ebx, 4); + memcpy(signature + 4, &ecx, 4); + memcpy(signature + 8, &edx, 4); + signature[12] = 0; + + dprintf(9, "Found hypervisor signature \"%s\" at %x\n", + signature, base); + if (strcmp(signature, "XenVMMXenVMM") == 0) { + /* Set debug_io_port first, so the following messages work. */ + DebugOutputPort = 0xe9; + debug_banner(); + dprintf(1, "\nFound Xen hypervisor signature at %x\n", base); + if ((eax - base) < 2) + panic("Insufficient Xen cpuid leaves. eax=%x at base %x\n", + eax, base); + xen_cpuid_base = base; + break; + } + } + if (!xen_cpuid_base) { + dprintf(1, "No Xen hypervisor found.\n"); + return; + } + PlatformRunningOn = PF_QEMU|PF_XEN; +} + +static int hypercall_xen_version( int cmd, void *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +/* Fill in hypercall transfer pages. */ +void xen_hypercall_setup(void) +{ + u32 eax, ebx, ecx, edx; + xen_extraversion_t extraversion; + unsigned long i; + + if (!runningOnXen()) + return; + + cpuid(xen_cpuid_base + 2, &eax, &ebx, &ecx, &edx); + + xen_hypercall_page = (unsigned long)memalign_high(PAGE_SIZE, eax*PAGE_SIZE); + if (!xen_hypercall_page) + panic("unable to allocate Xen hypercall page\n"); + + dprintf(1, "Allocated Xen hypercall page at %lx\n", xen_hypercall_page); + for ( i = 0; i < eax; i++ ) + wrmsr(ebx, xen_hypercall_page + (i << 12) + i); + + /* Print version information. */ + cpuid(xen_cpuid_base + 1, &eax, &ebx, &ecx, &edx); + hypercall_xen_version(XENVER_extraversion, extraversion); + dprintf(1, "Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion); +} + +void xen_biostable_setup(void) +{ + struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; + void **tables = (void*)info->tables; + int i; + + dprintf(1, "xen: copy BIOS tables...\n"); + for (i=0; i<info->tables_nr; i++) + copy_table(tables[i]); + + find_acpi_features(); +} + +void xen_ramsize_preinit(void) +{ + int i; + struct xen_seabios_info *info = (void *)INFO_PHYSICAL_ADDRESS; + struct e820entry *e820 = (struct e820entry *)info->e820; + validate_info(info); + + dprintf(1, "xen: copy e820...\n"); + + for (i = 0; i < info->e820_nr; i++) { + struct e820entry *e = &e820[i]; + add_e820(e->start, e->size, e->type); + } +} diff --git a/qemu/roms/seabios/src/fw/xen.h b/qemu/roms/seabios/src/fw/xen.h new file mode 100644 index 000000000..f00f8407d --- /dev/null +++ b/qemu/roms/seabios/src/fw/xen.h @@ -0,0 +1,125 @@ +#ifndef __XEN_H +#define __XEN_H + +void xen_preinit(void); +void xen_ramsize_preinit(void); +void xen_hypercall_setup(void); +void xen_biostable_setup(void); + +extern unsigned long xen_hypercall_page; + +#define _hypercall0(type, name) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res) \ + : "0" (__hentry) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1) \ + : "0" (__hentry), "1" ((long)(a1)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall4(type, name, a1, a2, a3, a4) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3, __ign4; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5) \ +({ \ + unsigned long __hentry = xen_hypercall_page+__HYPERVISOR_##name*32; \ + long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ + asm volatile ( \ + "call *%%eax" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "0" (__hentry), "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + "5" ((long)(a5)) \ + : "memory" ); \ + (type)__res; \ +}) + +/****************************************************************************** + * + * The following interface definitions are taken from Xen and have the + * following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* xen.h */ + +#define __HYPERVISOR_xen_version 17 + +/* version.h */ + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +typedef char xen_extraversion_t[16]; +#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t)) + +#endif diff --git a/qemu/roms/seabios/src/gen-defs.h b/qemu/roms/seabios/src/gen-defs.h new file mode 100644 index 000000000..dabf64cd9 --- /dev/null +++ b/qemu/roms/seabios/src/gen-defs.h @@ -0,0 +1,19 @@ +// Tool for building defintions accessible from assembler code. This +// is based on code from the Linux Kernel. +#ifndef __GEN_DEFS_H +#define __GEN_DEFS_H + + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() \ + asm volatile("\n->" : : ) + +#define OFFSET(sym, str, mem) \ + DEFINE(sym, offsetof(struct str, mem)) + +#define COMMENT(x) \ + asm volatile("\n->#" x) + +#endif // gen-defs.h diff --git a/qemu/roms/seabios/src/hw/ahci.c b/qemu/roms/seabios/src/hw/ahci.c new file mode 100644 index 000000000..3193d81a6 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ahci.c @@ -0,0 +1,639 @@ +// Low level AHCI disk access +// +// Copyright (C) 2010 Gerd Hoffmann <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // CDB_CMD_READ_10 +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBAL +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define AHCI_REQUEST_TIMEOUT 32000 // 32 seconds max for IDE ops +#define AHCI_RESET_TIMEOUT 500 // 500 miliseconds +#define AHCI_LINK_TIMEOUT 10 // 10 miliseconds + +// prepare sata command fis +static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = command; +} + +static void sata_prep_readwrite(struct sata_cmd_fis *fis, + struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + u8 command; + + memset_fl(fis, 0, sizeof(*fis)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + fis->sector_count2 = op->count >> 8; + fis->lba_low2 = lba >> 24; + fis->lba_mid2 = lba >> 32; + fis->lba_high2 = lba >> 40; + lba &= 0xffffff; + command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + fis->feature = 1; /* dma */ + fis->command = command; + fis->sector_count = op->count; + fis->lba_low = lba; + fis->lba_mid = lba >> 8; + fis->lba_high = lba >> 16; + fis->device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; +} + +static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = ATA_CMD_PACKET; + fis->feature = 1; /* dma */ + fis->lba_mid = blocksize; + fis->lba_high = blocksize >> 8; +} + +// ahci register access helpers +static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg) +{ + u32 addr = ctrl->iobase + reg; + return readl((void*)addr); +} + +static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val) +{ + u32 addr = ctrl->iobase + reg; + writel((void*)addr, val); +} + +static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg) +{ + u32 ctrl_reg = 0x100; + ctrl_reg += pnr * 0x80; + ctrl_reg += port_reg; + return ctrl_reg; +} + +static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + return ahci_ctrl_readl(ctrl, ctrl_reg); +} + +static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + ahci_ctrl_writel(ctrl, ctrl_reg, val); +} + +// submit ahci command + wait for result +static int ahci_command(struct ahci_port_s *port_gf, int iswrite, int isatapi, + void *buffer, u32 bsize) +{ + u32 val, status, success, flags, intbits, error; + struct ahci_ctrl_s *ctrl = port_gf->ctrl; + struct ahci_cmd_s *cmd = port_gf->cmd; + struct ahci_fis_s *fis = port_gf->fis; + struct ahci_list_s *list = port_gf->list; + u32 pnr = port_gf->pnr; + + cmd->fis.reg = 0x27; + cmd->fis.pmp_type = 1 << 7; /* cmd fis */ + cmd->prdt[0].base = (u32)buffer; + cmd->prdt[0].baseu = 0; + cmd->prdt[0].flags = bsize-1; + + flags = ((1 << 16) | /* one prd entry */ + (iswrite ? (1 << 6) : 0) | + (isatapi ? (1 << 5) : 0) | + (5 << 0)); /* fis length (dwords) */ + list[0].flags = flags; + list[0].bytes = 0; + list[0].base = (u32)(cmd); + list[0].baseu = 0; + + dprintf(8, "AHCI/%d: send cmd ...\n", pnr); + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1); + ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1); + + u32 end = timer_calc(AHCI_REQUEST_TIMEOUT); + do { + for (;;) { + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) { + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + if (intbits & 0x02) { + status = GET_LOWFLAT(fis->psfis[2]); + error = GET_LOWFLAT(fis->psfis[3]); + break; + } + if (intbits & 0x01) { + status = GET_LOWFLAT(fis->rfis[2]); + error = GET_LOWFLAT(fis->rfis[3]); + break; + } + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(8, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n", + pnr, intbits, status); + } while (status & ATA_CB_STAT_BSY); + + success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | + ATA_CB_STAT_ERR)) && + ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY))); + if (success) { + dprintf(8, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr, + status); + } else { + dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr, + status, error); + + // non-queued error recovery (AHCI 1.3 section 6.2.2.1) + // Clears PxCMD.ST to 0 to reset the PxCI register + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START); + + // waits for PxCMD.CR to clear to 0 + while (1) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if ((val & PORT_CMD_LIST_ON) == 0) + break; + yield(); + } + + // Clears any error bits in PxSERR to enable capturing new errors + val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val); + + // Clears status bits in PxIS as appropriate + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); + + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue + // a COMRESET to the device to put it in an idle state + val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) { + dprintf(2, "AHCI/%d: issue comreset\n", pnr); + val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL); + // set Device Detection Initialization (DET) to 1 for 1 ms for comreset + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1); + mdelay (1); + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val); + } + + // Sets PxCMD.ST to 1 to enable issuing new commands + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START); + } + return success ? 0 : -1; +} + +#define CDROM_CDB_SIZE 12 + +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_AHCI) + return 0; + + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + u8 *atapi = cdbcmd; + int i, rc; + + sata_prep_atapi(&cmd->fis, blocksize); + for (i = 0; i < CDROM_CDB_SIZE; i++) { + cmd->atapi[i] = atapi[i]; + } + rc = ahci_command(port_gf, 0, 1, op->buf_fl, + op->count * blocksize); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive, op->buf_fl must be word aligned +static int +ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite) +{ + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + int rc; + + sata_prep_readwrite(&cmd->fis, op, iswrite); + rc = ahci_command(port_gf, iswrite, 0, op->buf_fl, + op->count * DISK_SECTOR_SIZE); + dprintf(8, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n", + iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive. +static int +ahci_disk_readwrite(struct disk_op_s *op, int iswrite) +{ + // if caller's buffer is word aligned, use it directly + if (((u32) op->buf_fl & 1) == 0) + return ahci_disk_readwrite_aligned(op, iswrite); + + // Use a word aligned buffer for AHCI I/O + int rc; + struct disk_op_s localop = *op; + u8 *alignedbuf_fl = bounce_buf_fl; + u8 *position = op->buf_fl; + + localop.buf_fl = alignedbuf_fl; + localop.count = 1; + + if (iswrite) { + u16 block; + for (block = 0; block < op->count; block++) { + memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE); + rc = ahci_disk_readwrite_aligned (&localop, 1); + if (rc) + return rc; + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } else { // read + u16 block; + for (block = 0; block < op->count; block++) { + rc = ahci_disk_readwrite_aligned (&localop, 0); + if (rc) + return rc; + memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE); + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } + return DISK_RET_SUCCESS; +} + +// command demuxer +int VISIBLE32FLAT +process_ahci_op(struct disk_op_s *op) +{ + if (!CONFIG_AHCI) + return 0; + switch (op->command) { + case CMD_READ: + return ahci_disk_readwrite(op, 0); + case CMD_WRITE: + return ahci_disk_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + dprintf(1, "AHCI: unknown disk command %d\n", op->command); + return DISK_RET_EPARAM; + } +} + +static void +ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + u32 val; + + /* disable FIS + CMD */ + u32 end = timer_calc(AHCI_RESET_TIMEOUT); + for (;;) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START | + PORT_CMD_FIS_ON | PORT_CMD_LIST_ON))) + break; + val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START); + ahci_port_writel(ctrl, pnr, PORT_CMD, val); + if (timer_check(end)) { + warn_timeout(); + break; + } + yield(); + } + + /* disable + clear IRQs */ + ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, 0); + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (val) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); +} + +static struct ahci_port_s* +ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + struct ahci_port_s *port = malloc_tmp(sizeof(*port)); + + if (!port) { + warn_noalloc(); + return NULL; + } + port->pnr = pnr; + port->ctrl = ctrl; + port->list = memalign_tmp(1024, 1024); + port->fis = memalign_tmp(256, 256); + port->cmd = memalign_tmp(256, 256); + if (port->list == NULL || port->fis == NULL || port->cmd == NULL) { + warn_noalloc(); + return NULL; + } + memset(port->list, 0, 1024); + memset(port->fis, 0, 256); + memset(port->cmd, 0, 256); + + ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis); + return port; +} + +static void ahci_port_release(struct ahci_port_s *port) +{ + ahci_port_reset(port->ctrl, port->pnr); + free(port->list); + free(port->fis); + free(port->cmd); + free(port); +} + +static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port) +{ + struct ahci_port_s *tmp; + u32 cmd; + + tmp = malloc_fseg(sizeof(*port)); + if (!tmp) { + warn_noalloc(); + ahci_port_release(port); + return NULL; + } + *tmp = *port; + free(port); + port = tmp; + + ahci_port_reset(port->ctrl, port->pnr); + + free(port->list); + free(port->fis); + free(port->cmd); + port->list = memalign_high(1024, 1024); + port->fis = memalign_high(256, 256); + port->cmd = memalign_high(256, 256); + + ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis); + + cmd = ahci_port_readl(port->ctrl, port->pnr, PORT_CMD); + cmd |= (PORT_CMD_FIS_RX|PORT_CMD_START); + ahci_port_writel(port->ctrl, port->pnr, PORT_CMD, cmd); + + return port; +} + +#define MAXMODEL 40 + +/* See ahci spec chapter 10.1 "Software Initialization of HBA" */ +static int ahci_port_setup(struct ahci_port_s *port) +{ + struct ahci_ctrl_s *ctrl = port->ctrl; + u32 pnr = port->pnr; + char model[MAXMODEL+1]; + u16 buffer[256]; + u32 cmd, stat, err, tf; + int rc; + + /* enable FIS recv */ + cmd = ahci_port_readl(ctrl, pnr, PORT_CMD); + cmd |= PORT_CMD_FIS_RX; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + /* spin up */ + cmd |= PORT_CMD_SPIN_UP; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + u32 end = timer_calc(AHCI_LINK_TIMEOUT); + for (;;) { + stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT); + if ((stat & 0x07) == 0x03) { + dprintf(2, "AHCI/%d: link up\n", port->pnr); + break; + } + if (timer_check(end)) { + dprintf(2, "AHCI/%d: link down\n", port->pnr); + return -1; + } + yield(); + } + + /* clear error status */ + err = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + if (err) + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err); + + /* wait for device becoming ready */ + end = timer_calc(AHCI_REQUEST_TIMEOUT); + for (;;) { + tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (!(tf & (ATA_CB_STAT_BSY | + ATA_CB_STAT_DRQ))) + break; + if (timer_check(end)) { + warn_timeout(); + dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf); + return -1; + } + yield(); + } + + /* start device */ + cmd |= PORT_CMD_START; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc == 0) { + port->atapi = 1; + } else { + port->atapi = 0; + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc < 0) + return -1; + } + + port->drive.cntl_id = pnr; + port->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + + if (!port->atapi) { + // found disk (ata) + port->drive.type = DTYPE_AHCI; + port->drive.blksize = DISK_SECTOR_SIZE; + port->drive.pchs.cylinder = buffer[1]; + port->drive.pchs.head = buffer[3]; + port->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + port->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + port->desc = znprintf(MAXDESCSIZE + , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } else { + // found cdrom (atapi) + port->drive.type = DTYPE_AHCI_ATAPI; + port->drive.blksize = CDROM_SECTOR_SIZE; + port->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + if (!iscd) { + dprintf(1, "AHCI/%d: atapi device isn't a cdrom\n", port->pnr); + return -1; + } + port->desc = znprintf(MAXDESCSIZE + , "DVD/CD [AHCI/%d: %s ATAPI-%d DVD/CD]" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer)); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } + return 0; +} + +// Detect any drives attached to a given controller. +static void +ahci_port_detect(void *data) +{ + struct ahci_port_s *port = data; + int rc; + + dprintf(2, "AHCI/%d: probing\n", port->pnr); + ahci_port_reset(port->ctrl, port->pnr); + rc = ahci_port_setup(port); + if (rc < 0) + ahci_port_release(port); + else { + port = ahci_port_realloc(port); + if (port == NULL) + return; + dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc); + if (!port->atapi) { + // Register with bcv system. + boot_add_hd(&port->drive, port->desc, port->prio); + } else { + // fill cdidmap + boot_add_cd(&port->drive, port->desc, port->prio); + } + } +} + +// Initialize an ata controller and detect its drives. +static void +ahci_controller_setup(struct pci_device *pci) +{ + struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl)); + struct ahci_port_s *port; + u16 bdf = pci->bdf; + u32 val, pnr, max; + + if (!ctrl) { + warn_noalloc(); + return; + } + + if (create_bounce_buf() < 0) { + warn_noalloc(); + free(ctrl); + return; + } + + ctrl->pci_tmp = pci; + ctrl->pci_bdf = bdf; + ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5); + ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n", + bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq); + + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + val = ahci_ctrl_readl(ctrl, HOST_CTL); + ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN); + + ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP); + ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL); + dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n", + ctrl->caps, ctrl->ports); + + max = 0x1f; + for (pnr = 0; pnr <= max; pnr++) { + if (!(ctrl->ports & (1 << pnr))) + continue; + port = ahci_port_alloc(ctrl, pnr); + if (port == NULL) + continue; + run_thread(ahci_port_detect, port); + } +} + +// Locate and init ahci controllers. +static void +ahci_scan(void) +{ + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_STORAGE_SATA) + continue; + if (pci->prog_if != 1 /* AHCI rev 1 */) + continue; + ahci_controller_setup(pci); + } +} + +void +ahci_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_AHCI) + return; + + dprintf(3, "init ahci\n"); + ahci_scan(); +} diff --git a/qemu/roms/seabios/src/hw/ahci.h b/qemu/roms/seabios/src/hw/ahci.h new file mode 100644 index 000000000..c8c755a3f --- /dev/null +++ b/qemu/roms/seabios/src/hw/ahci.h @@ -0,0 +1,202 @@ +#ifndef __AHCI_H +#define __AHCI_H + +#include "block.h" // struct drive_s +#include "types.h" // u32 + +struct sata_cmd_fis { + u8 reg; + u8 pmp_type; + u8 command; + u8 feature; + + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; + u8 feature2; + + u8 sector_count; + u8 sector_count2; + u8 res_1; + u8 control; + + u8 res_2[64 - 16]; +}; + +struct ahci_ctrl_s { + struct pci_device *pci_tmp; + u16 pci_bdf; + u8 irq; + u32 iobase; + u32 caps; + u32 ports; +}; + +struct ahci_cmd_s { + struct sata_cmd_fis fis; + u8 atapi[0x20]; + u8 res[0x20]; + struct { + u32 base; + u32 baseu; + u32 res; + u32 flags; + } prdt[]; +}; + +/* command list */ +struct ahci_list_s { + u32 flags; + u32 bytes; + u32 base; + u32 baseu; + u32 res[4]; +}; + +struct ahci_fis_s { + u8 dsfis[0x1c]; /* dma setup */ + u8 res_1[0x04]; + u8 psfis[0x14]; /* pio setup */ + u8 res_2[0x0c]; + u8 rfis[0x14]; /* d2h register */ + u8 res_3[0x04]; + u8 sdbfis[0x08]; /* set device bits */ + u8 ufis[0x40]; /* unknown */ + u8 res_4[0x60]; +}; + +struct ahci_port_s { + struct drive_s drive; + struct ahci_ctrl_s *ctrl; + struct ahci_list_s *list; + struct ahci_fis_s *fis; + struct ahci_cmd_s *cmd; + u32 pnr; + u32 atapi; + char *desc; + int prio; +}; + +void ahci_setup(void); +int process_ahci_op(struct disk_op_s *op); +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); + +#define AHCI_IRQ_ON_SG (1 << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +#define HOST_CAP 0x00 /* host capabilities */ +#define HOST_CTL 0x04 /* global host control */ +#define HOST_IRQ_STAT 0x08 /* interrupt status */ +#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +#define PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define PORT_IRQ_STAT 0x10 /* interrupt status */ +#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define PORT_CMD 0x18 /* port command */ +#define PORT_TFDATA 0x20 /* taskfile data */ +#define PORT_SIG 0x24 /* device TF signature */ +#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define PORT_CMD_ISSUE 0x38 /* command issue */ +#define PORT_RESERVED 0x3c /* reserved */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ + +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */ +#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */ +#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */ +#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */ +#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */ +#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */ +#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */ +#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence + Status */ +#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */ +#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier + Status */ +#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */ +#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error + Status */ +#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */ +#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */ +#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */ +#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */ +#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */ + +#endif // ahci.h diff --git a/qemu/roms/seabios/src/hw/ata.c b/qemu/roms/seabios/src/hw/ata.c new file mode 100644 index 000000000..d805706dd --- /dev/null +++ b/qemu/roms/seabios/src/hw/ata.c @@ -0,0 +1,1043 @@ +// Low level ATA disk access +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "byteorder.h" // be16_to_cpu +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "pic.h" // enable_hwirq +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Wait for the specified ide state +static inline int +await_ide(u8 mask, u8 flags, u16 base, u16 timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(base+ATA_CB_STAT); + if ((status & mask) == flags) + return status; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Wait for the device to be not-busy. +static int +await_not_bsy(u16 base) +{ + return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT); +} + +// Wait for the device to be ready. +static int +await_rdy(u16 base) +{ + return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT); +} + +// Wait for ide state - pauses for one ata cycle first. +static inline int +pause_await_not_bsy(u16 iobase1, u16 iobase2) +{ + // Wait one PIO transfer cycle. + inb(iobase2 + ATA_CB_ASTAT); + + return await_not_bsy(iobase1); +} + +// Wait for ide state - pause for 400ns first. +static inline int +ndelay_await_not_bsy(u16 iobase1) +{ + ndelay(400); + return await_not_bsy(iobase1); +} + +// Reset a drive +static void +ata_reset(struct atadrive_s *adrive_gf) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + dprintf(6, "ata_reset drive=%p\n", &adrive_gf->drive); + // Pulse SRST + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); + udelay(5); + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); + msleep(2); + + // wait for device to become not busy. + int status = await_not_bsy(iobase1); + if (status < 0) + goto done; + if (slave) { + // Change device. + u32 end = timer_calc(IDE_TIMEOUT); + for (;;) { + outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + goto done; + if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) + break; + // Change drive request failed to take effect - retry. + if (timer_check(end)) { + warn_timeout(); + goto done; + } + } + } else { + // QEMU doesn't reset dh on reset, so set it explicitly. + outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH); + } + + // On a user-reset request, wait for RDY if it is an ATA device. + u8 type=GET_GLOBALFLAT(adrive_gf->drive.type); + if (type == DTYPE_ATA) + status = await_rdy(iobase1); + +done: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + dprintf(6, "ata_reset exit status=%x\n", status); +} + +// Check for drive RDY for 16bit interface command. +static int +isready(struct atadrive_s *adrive_gf) +{ + // Read the status from controller + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u8 status = inb(iobase1 + ATA_CB_STAT); + if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY) + return DISK_RET_SUCCESS; + return DISK_RET_ENOTREADY; +} + + +/**************************************************************** + * ATA send command + ****************************************************************/ + +struct ata_pio_command { + u8 feature; + u8 sector_count; + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + u8 command; + + u8 feature2; + u8 sector_count2; + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; +}; + +// Send an ata command to the drive. +static int +send_cmd(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + + // Select device + int status = await_not_bsy(iobase1); + if (status < 0) + return status; + u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1) + | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)); + u8 olddh = inb(iobase1 + ATA_CB_DH); + outb(newdh, iobase1 + ATA_CB_DH); + if ((olddh ^ newdh) & (1<<4)) { + // Was a device change - wait for device to become not busy. + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + } + + // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands. + if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) { + outb(cmd->feature2, iobase1 + ATA_CB_FR); + outb(cmd->sector_count2, iobase1 + ATA_CB_SC); + outb(cmd->lba_low2, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid2, iobase1 + ATA_CB_CL); + outb(cmd->lba_high2, iobase1 + ATA_CB_CH); + } + outb(cmd->feature, iobase1 + ATA_CB_FR); + outb(cmd->sector_count, iobase1 + ATA_CB_SC); + outb(cmd->lba_low, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid, iobase1 + ATA_CB_CL); + outb(cmd->lba_high, iobase1 + ATA_CB_CH); + outb(cmd->command, iobase1 + ATA_CB_CMD); + + return 0; +} + +// Wait for data after calling 'send_cmd'. +static int +ata_wait_data(u16 iobase1) +{ + int status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + + if (status & ATA_CB_STAT_ERR) { + dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n" + , status, inb(iobase1 + ATA_CB_ERR)); + return -4; + } + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status); + return -5; + } + + return 0; +} + +// Send an ata command that does not transfer any further data. +int +ata_cmd_nondata(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ndelay_await_not_bsy(iobase1); + if (ret < 0) + goto fail; + + if (ret & ATA_CB_STAT_ERR) { + dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n" + , ret, inb(iobase1 + ATA_CB_ERR)); + ret = -4; + goto fail; + } + if (ret & ATA_CB_STAT_DRQ) { + dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret); + ret = -5; + goto fail; + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + return ret; +} + + +/**************************************************************** + * ATA PIO transfers + ****************************************************************/ + +// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive +// 'op->drive_gf'. +static int +ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize) +{ + dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, iswrite, op->count, blocksize, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int count = op->count; + void *buf_fl = op->buf_fl; + int status; + for (;;) { + if (iswrite) { + // Write data to controller + dprintf(16, "Write sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + outsl_fl(iobase1, buf_fl, blocksize / 4); + else + outsw_fl(iobase1, buf_fl, blocksize / 2); + } else { + // Read data from controller + dprintf(16, "Read sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + insl_fl(iobase1, buf_fl, blocksize / 4); + else + insw_fl(iobase1, buf_fl, blocksize / 2); + } + buf_fl += blocksize; + + status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + // Error + op->count -= count; + return status; + } + + count--; + if (!count) + break; + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR); + if (status != ATA_CB_STAT_DRQ) { + dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n" + , status); + op->count -= count; + return -6; + } + } + + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR); + if (!iswrite) + status &= ~ATA_CB_STAT_DF; + if (status != 0) { + dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status); + return -7; + } + + return 0; +} + + +/**************************************************************** + * ATA DMA transfers + ****************************************************************/ + +#define BM_CMD 0 +#define BM_CMD_MEMWRITE 0x08 +#define BM_CMD_START 0x01 +#define BM_STATUS 2 +#define BM_STATUS_IRQ 0x04 +#define BM_STATUS_ERROR 0x02 +#define BM_STATUS_ACTIVE 0x01 +#define BM_TABLE 4 + +struct sff_dma_prd { + u32 buf_fl; + u32 count; +}; + +// Check if DMA available and setup transfer if so. +static int +ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize) +{ + ASSERT16(); + if (! CONFIG_ATA_DMA) + return -1; + u32 dest = (u32)op->buf_fl; + if (dest & 1) + // Need minimum alignment of 1. + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + if (! iomaster) + return -1; + u32 bytes = op->count * blocksize; + if (! bytes) + return -1; + + // Build PRD dma structure. + struct sff_dma_prd *dma = MAKE_FLATPTR(SEG_LOW, ExtraStack); + struct sff_dma_prd *origdma = dma; + while (bytes) { + if (dma >= &origdma[16]) + // Too many descriptors.. + return -1; + u32 count = bytes; + u32 max = 0x10000 - (dest & 0xffff); + if (count > max) + count = max; + + SET_LOWFLAT(dma->buf_fl, dest); + bytes -= count; + if (!bytes) + // Last descriptor. + count |= 1<<31; + dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count); + dest += count; + SET_LOWFLAT(dma->count, count); + dma++; + } + + // Program bus-master controller. + outl((u32)origdma, iomaster + BM_TABLE); + u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START); + outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD); + outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS); + + return 0; +} + +// Transfer data using DMA. +static int +ata_dma_transfer(struct disk_op_s *op) +{ + if (! CONFIG_ATA_DMA) + return -1; + dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_gf, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + + // Start bus-master controller. + u8 oldcmd = inb(iomaster + BM_CMD); + outb(oldcmd | BM_CMD_START, iomaster + BM_CMD); + + u32 end = timer_calc(IDE_TIMEOUT); + u8 status; + for (;;) { + status = inb(iomaster + BM_STATUS); + if (status & BM_STATUS_IRQ) + break; + // Transfer in progress + if (timer_check(end)) { + // Timeout. + warn_timeout(); + break; + } + yield(); + } + outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD); + + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int idestatus = pause_await_not_bsy(iobase1, iobase2); + + if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ + && idestatus >= 0x00 + && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR)) == 0x00) + // Success. + return 0; + + dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus + , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR)); + return -1; +} + + +/**************************************************************** + * ATA hard drive functions + ****************************************************************/ + +// Transfer data to harddrive using PIO protocol. +static int +ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd) +{ + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE); + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + return ret; +} + +// Transfer data to harddrive using DMA protocol. +static int +ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd) +{ + if (! CONFIG_ATA_DMA) + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + int ret = send_cmd(adrive_gf, cmd); + if (ret) + return ret; + return ata_dma_transfer(op); +} + +// Read/write count blocks from a harddrive. +static int +ata_readwrite(struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + + int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + cmd.sector_count2 = op->count >> 8; + cmd.lba_low2 = lba >> 24; + cmd.lba_mid2 = lba >> 32; + cmd.lba_high2 = lba >> 40; + lba &= 0xffffff; + + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT + : ATA_CMD_READ_SECTORS_EXT); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS + : ATA_CMD_READ_SECTORS); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + + cmd.sector_count = op->count; + cmd.lba_low = lba; + cmd.lba_mid = lba >> 8; + cmd.lba_high = lba >> 16; + cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; + + int ret; + if (usepio) + ret = ata_pio_cmd_data(op, iswrite, &cmd); + else + ret = ata_dma_cmd_data(op, &cmd); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// 16bit command demuxer for ATA harddrives. +int +process_ata_op(struct disk_op_s *op) +{ + if (!CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + switch (op->command) { + case CMD_READ: + return ata_readwrite(op, 0); + case CMD_WRITE: + return ata_readwrite(op, 1); + case CMD_RESET: + ata_reset(adrive_gf); + return DISK_RET_SUCCESS; + case CMD_ISREADY: + return isready(adrive_gf); + case CMD_FORMAT: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * ATAPI functions + ****************************************************************/ + +#define CDROM_CDB_SIZE 12 + +// Low-level atapi command transmit function. +int +atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.lba_mid = blocksize; + cmd.lba_high = blocksize >> 8; + cmd.command = ATA_CMD_PACKET; + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, &cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + + // Send command to device + outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2); + + int status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + ret = status; + goto fail; + } + + if (status & ATA_CB_STAT_ERR) { + u8 err = inb(iobase1 + ATA_CB_ERR); + // skip "Not Ready" + if (err != 0x20) + dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n" + , status, err); + ret = -2; + goto fail; + } + if (blocksize) { + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status); + ret = -3; + goto fail; + } + + ret = ata_pio_transfer(op, 0, blocksize); + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * ATA detect and init + ****************************************************************/ + +// Send an identify device or identify device packet command. +static int +send_ata_identity(struct atadrive_s *adrive, u16 *buffer, int command) +{ + memset(buffer, 0, DISK_SECTOR_SIZE); + + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = &adrive->drive; + dop.count = 1; + dop.lba = 1; + dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = command; + + return ata_pio_cmd_data(&dop, 0, &cmd); +} + +// Extract the ATA/ATAPI version info. +int +ata_extract_version(u16 *buffer) +{ + // Extract ATA/ATAPI version. + u16 ataversion = buffer[80]; + u8 version; + for (version=15; version>0; version--) + if (ataversion & (1<<version)) + break; + return version; +} + +#define MAXMODEL 40 + +// Extract the ATA/ATAPI model info. +char * +ata_extract_model(char *model, u32 size, u16 *buffer) +{ + // Read model name + int i; + for (i=0; i<size/2; i++) + *(u16*)&model[i*2] = be16_to_cpu(buffer[27+i]); + model[size] = 0x00; + nullTrailingSpace(model); + return model; +} + +// Common init code between ata and atapi +static struct atadrive_s * +init_atadrive(struct atadrive_s *dummy, u16 *buffer) +{ + struct atadrive_s *adrive = malloc_fseg(sizeof(*adrive)); + if (!adrive) { + warn_noalloc(); + return NULL; + } + memset(adrive, 0, sizeof(*adrive)); + adrive->chan_gf = dummy->chan_gf; + adrive->slave = dummy->slave; + adrive->drive.cntl_id = adrive->chan_gf->chanid * 2 + dummy->slave; + adrive->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + return adrive; +} + +// Detect if the given drive is an atapi - initialize it if so. +static struct atadrive_s * +init_drive_atapi(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE_PACKET command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATAPI. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA_ATAPI; + adrive->drive.blksize = CDROM_SECTOR_SIZE; + adrive->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (iscd ? "DVD/CD" : "Device")); + dprintf(1, "%s\n", desc); + + // fill cdidmap + if (iscd) { + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + boot_add_cd(&adrive->drive, desc, prio); + } + + return adrive; +} + +// Detect if the given drive is a regular ata drive - initialize it if so. +static struct atadrive_s * +init_drive_ata(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATA. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA; + adrive->drive.blksize = DISK_SECTOR_SIZE; + + adrive->drive.pchs.cylinder = buffer[1]; + adrive->drive.pchs.head = buffer[3]; + adrive->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + adrive->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + dprintf(1, "%s\n", desc); + + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + // Register with bcv system. + boot_add_hd(&adrive->drive, desc, prio); + + return adrive; +} + +static u32 SpinupEnd; + +// Wait for non-busy status and check for "floating bus" condition. +static int +powerup_await_non_bsy(u16 base) +{ + u8 orstatus = 0; + u8 status; + for (;;) { + status = inb(base+ATA_CB_STAT); + if (!(status & ATA_CB_STAT_BSY)) + break; + orstatus |= status; + if (orstatus == 0xff) { + dprintf(4, "powerup IDE floating\n"); + return orstatus; + } + if (timer_check(SpinupEnd)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(6, "powerup iobase=%x st=%x\n", base, status); + return status; +} + +// Detect any drives attached to a given controller. +static void +ata_detect(void *data) +{ + struct ata_channel_s *chan_gf = data; + struct atadrive_s dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.chan_gf = chan_gf; + // Device detection + int didreset = 0; + u8 slave; + for (slave=0; slave<=1; slave++) { + // Wait for not-bsy. + u16 iobase1 = chan_gf->iobase1; + int status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0; + outb(newdh, iobase1+ATA_CB_DH); + ndelay(400); + status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + + // Check if ioport registers look valid. + outb(newdh, iobase1+ATA_CB_DH); + u8 dh = inb(iobase1+ATA_CB_DH); + outb(0x55, iobase1+ATA_CB_SC); + outb(0xaa, iobase1+ATA_CB_SN); + u8 sc = inb(iobase1+ATA_CB_SC); + u8 sn = inb(iobase1+ATA_CB_SN); + dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n" + , chan_gf->chanid, slave, sc, sn, dh); + if (sc != 0x55 || sn != 0xaa || dh != newdh) + continue; + + // Prepare new drive. + dummy.slave = slave; + + // reset the channel + if (!didreset) { + ata_reset(&dummy); + didreset = 1; + } + + // check for ATAPI + u16 buffer[256]; + struct atadrive_s *adrive = init_drive_atapi(&dummy, buffer); + if (!adrive) { + // Didn't find an ATAPI drive - look for ATA drive. + u8 st = inb(iobase1+ATA_CB_STAT); + if (!st) + // Status not set - can't be a valid drive. + continue; + + // Wait for RDY. + int ret = await_rdy(iobase1); + if (ret < 0) + continue; + + // check for ATA. + adrive = init_drive_ata(&dummy, buffer); + if (!adrive) + // No ATA drive found + continue; + } + + u16 resetresult = buffer[93]; + dprintf(6, "ata_detect resetresult=%04x\n", resetresult); + if (!slave && (resetresult & 0xdf61) == 0x4041) + // resetresult looks valid and device 0 is responding to + // device 1 requests - device 1 must not be present - skip + // detection. + break; + } +} + +// Initialize an ata controller and detect its drives. +static void +init_controller(struct pci_device *pci, int irq + , u32 port1, u32 port2, u32 master) +{ + static int chanid = 0; + struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf)); + if (!chan_gf) { + warn_noalloc(); + return; + } + chan_gf->chanid = chanid++; + chan_gf->irq = irq; + chan_gf->pci_bdf = pci ? pci->bdf : -1; + chan_gf->pci_tmp = pci; + chan_gf->iobase1 = port1; + chan_gf->iobase2 = port2; + chan_gf->iomaster = master; + dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n" + , chanid, port1, port2, master, irq, chan_gf->pci_bdf); + run_thread(ata_detect, chan_gf); +} + +#define IRQ_ATA1 14 +#define IRQ_ATA2 15 + +// Handle controllers on an ATA PCI device. +static void +init_pciata(struct pci_device *pci, u8 prog_if) +{ + pci->have_driver = 1; + u16 bdf = pci->bdf; + u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + int master = 0; + if (CONFIG_ATA_DMA && prog_if & 0x80) { + // Check for bus-mastering. + u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4); + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + master = bar & PCI_BASE_ADDRESS_IO_MASK; + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + } + } + + u32 port1, port2, irq; + if (prog_if & 1) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_1) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA1_CMD_BASE; + port2 = PORT_ATA1_CTRL_BASE; + irq = IRQ_ATA1; + } + init_controller(pci, irq, port1, port2, master); + + if (prog_if & 4) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_3) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA2_CMD_BASE; + port2 = PORT_ATA2_CTRL_BASE; + irq = IRQ_ATA2; + } + init_controller(pci, irq, port1, port2, master ? master + 8 : 0); +} + +static void +found_genericata(struct pci_device *pci, void *arg) +{ + init_pciata(pci, pci->prog_if); +} + +static void +found_compatibleahci(struct pci_device *pci, void *arg) +{ + if (CONFIG_AHCI) + // Already handled directly via native ahci interface. + return; + init_pciata(pci, 0x8f); +} + +static const struct pci_device_id pci_ata_tbl[] = { + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE + , found_genericata), + PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4391, found_compatibleahci), + PCI_DEVICE_END, +}; + +// Locate and init ata controllers. +static void +ata_scan(void) +{ + if (CONFIG_QEMU && hlist_empty(&PCIDevices)) { + // No PCI devices found - probably a QEMU "-M isapc" machine. + // Try using ISA ports for ATA controllers. + init_controller(NULL, IRQ_ATA1 + , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0); + init_controller(NULL, IRQ_ATA2 + , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0); + return; + } + + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + pci_init_device(pci_ata_tbl, pci, NULL); + } +} + +void +ata_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ATA) + return; + + dprintf(3, "init hard drives\n"); + + SpinupEnd = timer_calc(IDE_TIMEOUT); + ata_scan(); + + SET_BDA(disk_control_byte, 0xc0); + + enable_hwirq(14, FUNC16(entry_76)); +} diff --git a/qemu/roms/seabios/src/hw/ata.h b/qemu/roms/seabios/src/hw/ata.h new file mode 100644 index 000000000..c73892bbe --- /dev/null +++ b/qemu/roms/seabios/src/hw/ata.h @@ -0,0 +1,158 @@ +#ifndef __ATA_H +#define __ATA_H + +#include "block.h" // struct drive_s +#include "config.h" // CONFIG_MAX_ATA_INTERFACES +#include "types.h" // u8 + +struct ata_channel_s { + u16 iobase1; + u16 iobase2; + u16 iomaster; + u8 irq; + u8 chanid; + int pci_bdf; + struct pci_device *pci_tmp; +}; + +struct atadrive_s { + struct drive_s drive; + struct ata_channel_s *chan_gf; + u8 slave; +}; + +// ata.c +char *ata_extract_model(char *model, u32 size, u16 *buffer); +int ata_extract_version(u16 *buffer); +int cdrom_read(struct disk_op_s *op); +int atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void ata_setup(void); +int process_ata_op(struct disk_op_s *op); + +#define PORT_ATA2_CMD_BASE 0x0170 +#define PORT_ATA1_CMD_BASE 0x01f0 +#define PORT_ATA2_CTRL_BASE 0x0374 +#define PORT_ATA1_CTRL_BASE 0x03f4 + +// Global defines -- ATA register and register bits. +// command block & control block regs +#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0 +#define ATA_CB_ERR 1 // error in pio_base_addr1+1 +#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1 +#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2 +#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3 +#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4 +#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5 +#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6 +#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7 +#define ATA_CB_CMD 7 // command out pio_base_addr1+7 + +#define ATA_CB_ASTAT 2 // alternate status in pio_base_addr2+2 +#define ATA_CB_DC 2 // device control out pio_base_addr2+2 +#define ATA_CB_DA 3 // device address in pio_base_addr2+3 + +#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC +#define ATA_CB_ER_BBK 0x80 // ATA bad block +#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error +#define ATA_CB_ER_MC 0x20 // ATA media change +#define ATA_CB_ER_IDNF 0x10 // ATA id not found +#define ATA_CB_ER_MCR 0x08 // ATA media change request +#define ATA_CB_ER_ABRT 0x04 // ATA command aborted +#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found +#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found + +#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) +#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request +#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort +#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media +#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication + +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) +#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask) +#define ATA_CB_SC_P_REL 0x04 // ATAPI release +#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O +#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D + +// bits 7-4 of the device/head (CB_DH) reg +#define ATA_CB_DH_DEV0 0xa0 // select device 0 +#define ATA_CB_DH_DEV1 0xb0 // select device 1 +#define ATA_CB_DH_LBA 0x40 // use LBA + +// status reg (CB_STAT and CB_ASTAT) bits +#define ATA_CB_STAT_BSY 0x80 // busy +#define ATA_CB_STAT_RDY 0x40 // ready +#define ATA_CB_STAT_DF 0x20 // device fault +#define ATA_CB_STAT_WFT 0x20 // write fault (old name) +#define ATA_CB_STAT_SKC 0x10 // seek complete +#define ATA_CB_STAT_SERV 0x10 // service +#define ATA_CB_STAT_DRQ 0x08 // data request +#define ATA_CB_STAT_CORR 0x04 // corrected +#define ATA_CB_STAT_IDX 0x02 // index +#define ATA_CB_STAT_ERR 0x01 // error (ATA) +#define ATA_CB_STAT_CHK 0x01 // check (ATAPI) + +// device control reg (CB_DC) bits +#define ATA_CB_DC_HD15 0x08 // bit should always be set to one +#define ATA_CB_DC_SRST 0x04 // soft reset +#define ATA_CB_DC_NIEN 0x02 // disable interrupts + +// Most mandtory and optional ATA commands (from ATA-3), +#define ATA_CMD_NOP 0x00 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define ATA_CMD_DEVICE_RESET 0x08 +#define ATA_CMD_RECALIBRATE 0x10 +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_READ_SECTORS_EXT 0x24 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_READ_DMA_QUEUED_EXT 0x26 +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT 0x27 +#define ATA_CMD_READ_MULTIPLE_EXT 0x29 +#define ATA_CMD_READ_LOG_EXT 0x2F +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_WRITE_SECTORS_EXT 0x34 +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_WRITE_DMA_QUEUED_EXT 0x36 +#define ATA_CMD_SET_MAX_ADDRESS_EXT 0x37 +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define ATA_CMD_WRITE_MULTIPLE_EXT 0x39 +#define ATA_CMD_WRITE_VERIFY 0x3C +#define ATA_CMD_WRITE_LOG_EXT 0x3F +#define ATA_CMD_READ_VERIFY_SECTORS 0x40 +#define ATA_CMD_READ_VERIFY_SECTORS_EXT 0x42 +#define ATA_CMD_FORMAT_TRACK 0x50 +#define ATA_CMD_SEEK 0x70 +#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define ATA_CMD_STANDBY_IMMEDIATE2 0x94 +#define ATA_CMD_IDLE_IMMEDIATE2 0x95 +#define ATA_CMD_STANDBY2 0x96 +#define ATA_CMD_IDLE2 0x97 +#define ATA_CMD_CHECK_POWER_MODE2 0x98 +#define ATA_CMD_SLEEP2 0x99 +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define ATA_CMD_CFA_ERASE_SECTORS 0xC0 +#define ATA_CMD_READ_MULTIPLE 0xC4 +#define ATA_CMD_WRITE_MULTIPLE 0xC5 +#define ATA_CMD_SET_MULTIPLE_MODE 0xC6 +#define ATA_CMD_READ_DMA_QUEUED 0xC7 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_QUEUED 0xCC +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define ATA_CMD_STANDBY_IMMEDIATE 0xE0 +#define ATA_CMD_IDLE_IMMEDIATE 0xE1 +#define ATA_CMD_STANDBY 0xE2 +#define ATA_CMD_IDLE 0xE3 +#define ATA_CMD_READ_BUFFER 0xE4 +#define ATA_CMD_CHECK_POWER_MODE 0xE5 +#define ATA_CMD_SLEEP 0xE6 +#define ATA_CMD_FLUSH_CACHE 0xE7 +#define ATA_CMD_WRITE_BUFFER 0xE8 +#define ATA_CMD_IDENTIFY_DEVICE 0xEC +#define ATA_CMD_SET_FEATURES 0xEF +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS 0xF8 +#define ATA_CMD_SET_MAX 0xF9 + +#endif // ata.h diff --git a/qemu/roms/seabios/src/hw/blockcmd.c b/qemu/roms/seabios/src/hw/blockcmd.c new file mode 100644 index 000000000..78c0e65f4 --- /dev/null +++ b/qemu/roms/seabios/src/hw/blockcmd.c @@ -0,0 +1,319 @@ +// Support for several common scsi like command data block requests +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // atapi_cmd_data +#include "ata.h" // atapi_cmd_data +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct disk_op_s +#include "blockcmd.h" // struct cdb_request_sense +#include "byteorder.h" // be32_to_cpu +#include "esp-scsi.h" // esp_scsi_cmd_data +#include "lsi-scsi.h" // lsi_scsi_cmd_data +#include "megasas.h" // megasas_cmd_data +#include "pvscsi.h" // pvscsi_cmd_data +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_EPARAM +#include "string.h" // memset +#include "usb-msc.h" // usb_cmd_data +#include "usb-uas.h" // usb_cmd_data +#include "util.h" // timer_calc +#include "virtio-scsi.h" // virtio_scsi_cmd_data + +// Route command to low-level handler. +static int +cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + u8 type = GET_GLOBALFLAT(op->drive_gf->type); + switch (type) { + case DTYPE_ATA_ATAPI: + return atapi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB: + return usb_cmd_data(op, cdbcmd, blocksize); + case DTYPE_UAS: + return uas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_VIRTIO_SCSI: + return virtio_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_LSI_SCSI: + return lsi_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_ESP_SCSI: + return esp_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_MEGASAS: + return megasas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB_32: + if (!MODESEGMENT) + return usb_cmd_data(op, cdbcmd, blocksize); + case DTYPE_UAS_32: + if (!MODESEGMENT) + return uas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_PVSCSI: + if (!MODESEGMENT) + return pvscsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_AHCI_ATAPI: + if (!MODESEGMENT) + return ahci_cmd_data(op, cdbcmd, blocksize); + default: + return DISK_RET_EPARAM; + } +} + +// Determine if the command is a request to pull data from the device +int +cdb_is_read(u8 *cdbcmd, u16 blocksize) +{ + return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10; +} + + +/**************************************************************** + * Low level command requests + ****************************************************************/ + +static int +cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_INQUIRY; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Request SENSE +static int +cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_REQUEST_SENSE; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Test unit ready +static int +cdb_test_unit_ready(struct disk_op_s *op) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_TEST_UNIT_READY; + op->count = 0; + op->buf_fl = NULL; + return cdb_cmd_data(op, &cmd, 0); +} + +// Request capacity +static int +cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) +{ + struct cdb_read_capacity cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_CAPACITY; + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Mode sense, geometry page. +static int +cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) +{ + struct cdb_mode_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_MODE_SENSE; + cmd.flags = 8; /* DBD */ + cmd.page = MODE_PAGE_HD_GEOMETRY; + cmd.count = cpu_to_be16(sizeof(*data)); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Read sectors. +static int +cdb_read(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + +// Write sectors. +static int +cdb_write(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_WRITE_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + + +/**************************************************************** + * Main SCSI commands + ****************************************************************/ + +int VISIBLE32FLAT +scsi_process_op(struct disk_op_s *op) +{ + switch (op->command) { + case CMD_READ: + return cdb_read(op); + case CMD_WRITE: + return cdb_write(op); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +int +scsi_is_ready(struct disk_op_s *op) +{ + dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_gf); + + /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is + * reported by the device. If the device reports "IN PROGRESS", + * 30 seconds is added. */ + int in_progress = 0; + u32 end = timer_calc(5000); + for (;;) { + if (timer_check(end)) { + dprintf(1, "test unit ready failed\n"); + return -1; + } + + int ret = cdb_test_unit_ready(op); + if (!ret) + // Success + break; + + struct cdbres_request_sense sense; + ret = cdb_get_sense(op, &sense); + if (ret) + // Error - retry. + continue; + + // Sense succeeded. + if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */ + dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for device to detect medium... "); + /* Allow 30 seconds more */ + end = timer_calc(30000); + in_progress = 1; + } + } + return 0; +} + +// Validate drive, find block size / sector count, and register drive. +int +scsi_drive_setup(struct drive_s *drive, const char *s, int prio) +{ + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = drive; + struct cdbres_inquiry data; + int ret = cdb_get_inquiry(&dop, &data); + if (ret) + return ret; + char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; + char rev[sizeof(data.rev)+1]; + strtcpy(vendor, data.vendor, sizeof(vendor)); + nullTrailingSpace(vendor); + strtcpy(product, data.product, sizeof(product)); + nullTrailingSpace(product); + strtcpy(rev, data.rev, sizeof(rev)); + nullTrailingSpace(rev); + int pdt = data.pdt & 0x1f; + int removable = !!(data.removable & 0x80); + dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n" + , s, vendor, product, rev, pdt, removable); + drive->removable = removable; + + if (pdt == SCSI_TYPE_CDROM) { + drive->blksize = CDROM_SECTOR_SIZE; + drive->sectors = (u64)-1; + + char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]" + , s, vendor, product, rev); + boot_add_cd(drive, desc, prio); + return 0; + } + + ret = scsi_is_ready(&dop); + if (ret) { + dprintf(1, "scsi_is_ready returned %d\n", ret); + return ret; + } + + struct cdbres_read_capacity capdata; + ret = cdb_read_capacity(&dop, &capdata); + if (ret) + return ret; + + // READ CAPACITY returns the address of the last block. + // We do not bother with READ CAPACITY(16) because BIOS does not support + // 64-bit LBA anyway. + drive->blksize = be32_to_cpu(capdata.blksize); + if (drive->blksize != DISK_SECTOR_SIZE) { + dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize); + return -1; + } + drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1; + dprintf(1, "%s blksize=%d sectors=%d\n" + , s, drive->blksize, (unsigned)drive->sectors); + + // We do not recover from USB stalls, so try to be safe and avoid + // sending the command if the (obsolete, but still provided by QEMU) + // fixed disk geometry page may not be supported. + // + // We could also send the command only to small disks (e.g. <504MiB) + // but some old USB keys only support a very small subset of SCSI which + // does not even include the MODE SENSE command! + // + if (CONFIG_QEMU_HARDWARE && memcmp(vendor, "QEMU", 5) == 0) { + struct cdbres_mode_sense_geom geomdata; + ret = cdb_mode_sense_geom(&dop, &geomdata); + if (ret == 0) { + u32 cylinders; + cylinders = geomdata.cyl[0] << 16; + cylinders |= geomdata.cyl[1] << 8; + cylinders |= geomdata.cyl[2]; + if (cylinders && geomdata.heads && + drive->sectors <= 0xFFFFFFFFULL && + ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) { + drive->pchs.cylinder = cylinders; + drive->pchs.head = geomdata.heads; + drive->pchs.sector = (u32)drive->sectors / (geomdata.heads * cylinders); + } + } + } + + char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s" + , s, vendor, product, rev); + boot_add_hd(drive, desc, prio); + return 0; +} diff --git a/qemu/roms/seabios/src/hw/blockcmd.h b/qemu/roms/seabios/src/hw/blockcmd.h new file mode 100644 index 000000000..df12a6d42 --- /dev/null +++ b/qemu/roms/seabios/src/hw/blockcmd.h @@ -0,0 +1,110 @@ +// Definitions for SCSI style command data blocks. +#ifndef __BLOCKCMD_H +#define __BLOCKCMD_H + +#include "types.h" // u8 + +#define CDB_CMD_READ_10 0x28 +#define CDB_CMD_VERIFY_10 0x2f +#define CDB_CMD_WRITE_10 0x2a + +struct cdb_rwdata_10 { + u8 command; + u8 flags; + u32 lba; + u8 resreved_06; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +#define CDB_CMD_READ_CAPACITY 0x25 + +struct cdb_read_capacity { + u8 command; + u8 flags; + u8 resreved_02[8]; + u8 pad[6]; +} PACKED; + +struct cdbres_read_capacity { + u32 sectors; + u32 blksize; +} PACKED; + +#define CDB_CMD_TEST_UNIT_READY 0x00 +#define CDB_CMD_INQUIRY 0x12 +#define CDB_CMD_REQUEST_SENSE 0x03 + +struct cdb_request_sense { + u8 command; + u8 flags; + u16 reserved_02; + u8 length; + u8 reserved_05; + u8 pad[10]; +} PACKED; + +struct cdbres_request_sense { + u8 errcode; + u8 segment; + u8 flags; + u32 info; + u8 additional; + u32 specific; + u8 asc; + u8 ascq; + u32 reserved_0e; +} PACKED; + +#define SCSI_TYPE_DISK 0x00 +#define SCSI_TYPE_CDROM 0x05 + +struct cdbres_inquiry { + u8 pdt; + u8 removable; + u8 reserved_02[2]; + u8 additional; + u8 reserved_05[3]; + char vendor[8]; + char product[16]; + char rev[4]; +} PACKED; + +#define CDB_CMD_MODE_SENSE 0x5A +#define MODE_PAGE_HD_GEOMETRY 0x04 + +struct cdb_mode_sense { + u8 command; + u8 flags; + u8 page; + u32 reserved_03; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +struct cdbres_mode_sense_geom { + u8 unused_00[3]; + u8 read_only; + u32 unused_04; + u8 page; + u8 length; + u8 cyl[3]; + u8 heads; + u8 precomp[3]; + u8 reduced[3]; + u16 step_rate; + u8 landing[3]; + u16 rpm; +} PACKED; + +// blockcmd.c +int cdb_is_read(u8 *cdbcmd, u16 blocksize); +struct disk_op_s; +int scsi_process_op(struct disk_op_s *op); +int scsi_is_ready(struct disk_op_s *op); +struct drive_s; +int scsi_drive_setup(struct drive_s *drive, const char *s, int prio); + +#endif // blockcmd.h diff --git a/qemu/roms/seabios/src/hw/dma.c b/qemu/roms/seabios/src/hw/dma.c new file mode 100644 index 000000000..20c9fbb76 --- /dev/null +++ b/qemu/roms/seabios/src/hw/dma.c @@ -0,0 +1,67 @@ +// Code to support legacy Intel 8237 DMA chip. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dma_setup +#include "x86.h" // outb + +#define PORT_DMA_ADDR_2 0x0004 +#define PORT_DMA_CNT_2 0x0005 +#define PORT_DMA1_MASK_REG 0x000a +#define PORT_DMA1_MODE_REG 0x000b +#define PORT_DMA1_CLEAR_FF_REG 0x000c +#define PORT_DMA1_MASTER_CLEAR 0x000d +#define PORT_DMA_PAGE_2 0x0081 +#define PORT_DMA2_MASK_REG 0x00d4 +#define PORT_DMA2_MODE_REG 0x00d6 +#define PORT_DMA2_MASTER_CLEAR 0x00da + +// Setup the DMA controller for a floppy transfer. +int +dma_floppy(u32 addr, int count, int isWrite) +{ + // check for 64K boundary overrun + u16 end = count - 1; + u32 last_addr = addr + end; + if ((addr >> 16) != (last_addr >> 16)) + return -1; + + u8 mode_register = 0x46; // single mode, increment, autoinit disable, + if (isWrite) + mode_register = 0x4a; + + outb(0x06, PORT_DMA1_MASK_REG); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(addr, PORT_DMA_ADDR_2); + outb(addr>>8, PORT_DMA_ADDR_2); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(end, PORT_DMA_CNT_2); + outb(end>>8, PORT_DMA_CNT_2); + + // port 0b: DMA-1 Mode Register + // transfer type=write, channel 2 + outb(mode_register, PORT_DMA1_MODE_REG); + + // port 81: DMA-1 Page Register, channel 2 + outb(addr>>16, PORT_DMA_PAGE_2); + + outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2 + + return 0; +} + +// Reset DMA controller +void +dma_setup(void) +{ + // first reset the DMA controllers + outb(0, PORT_DMA1_MASTER_CLEAR); + outb(0, PORT_DMA2_MASTER_CLEAR); + + // then initialize the DMA controllers + outb(0xc0, PORT_DMA2_MODE_REG); + outb(0x00, PORT_DMA2_MASK_REG); +} diff --git a/qemu/roms/seabios/src/hw/esp-scsi.c b/qemu/roms/seabios/src/hw/esp-scsi.c new file mode 100644 index 000000000..33cc44986 --- /dev/null +++ b/qemu/roms/seabios/src/hw/esp-scsi.c @@ -0,0 +1,238 @@ +// AMD PCscsi boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// based on lsi-scsi.c which is written by: +// Gerd Hoffman <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define ESP_TCLO 0x00 +#define ESP_TCMID 0x04 +#define ESP_FIFO 0x08 +#define ESP_CMD 0x0c +#define ESP_WBUSID 0x10 +#define ESP_TCHI 0x38 + +#define ESP_RSTAT 0x10 +#define ESP_RINTR 0x14 +#define ESP_RFLAGS 0x1c + +#define ESP_DMA_CMD 0x40 +#define ESP_DMA_STC 0x44 +#define ESP_DMA_SPA 0x48 +#define ESP_DMA_WBC 0x4c +#define ESP_DMA_WAC 0x50 +#define ESP_DMA_STAT 0x54 +#define ESP_DMA_SMDLA 0x58 +#define ESP_DMA_WMAC 0x58c + +#define ESP_CMD_DMA 0x80 +#define ESP_CMD_RESET 0x02 +#define ESP_CMD_TI 0x10 +#define ESP_CMD_ICCS 0x11 +#define ESP_CMD_SELATN 0x42 + +#define ESP_STAT_DI 0x01 +#define ESP_STAT_CD 0x02 +#define ESP_STAT_MSG 0x04 +#define ESP_STAT_TC 0x10 + +#define ESP_INTR_DC 0x20 + +struct esp_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static void +esp_scsi_dma(u32 iobase, u32 buf, u32 len, int read) +{ + outb(len & 0xff, iobase + ESP_TCLO); + outb((len >> 8) & 0xff, iobase + ESP_TCMID); + outb((len >> 16) & 0xff, iobase + ESP_TCHI); + outl(buf, iobase + ESP_DMA_SPA); + outl(len, iobase + ESP_DMA_STC); + outb(read ? 0x83 : 0x03, iobase + ESP_DMA_CMD); +} + +static int +esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, + u8 *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + int i, state; + u8 status; + + outb(target, iobase + ESP_WBUSID); + + /* + * We need to pass the LUN at the beginning of the command, and the FIFO + * is only 16 bytes, so we cannot support 16-byte CDBs. The alternative + * would be to use DMA for the 17-byte command too, which is quite + * overkill. + */ + outb(lun, iobase + ESP_FIFO); + cdbcmd[1] &= 0x1f; + cdbcmd[1] |= lun << 5; + for (i = 0; i < 12; i++) + outb(cdbcmd[i], iobase + ESP_FIFO); + outb(ESP_CMD_SELATN, iobase + ESP_CMD); + + for (state = 0;;) { + u8 stat = inb(iobase + ESP_RSTAT); + + /* Detect disconnected device. */ + if (state == 0 && (inb(iobase + ESP_RINTR) & ESP_INTR_DC)) { + return DISK_RET_ENOTREADY; + } + + /* HBA reads command, clears CD, sets TC -> do DMA if needed. */ + if (state == 0 && (stat & ESP_STAT_TC)) { + state++; + if (op->count && blocksize) { + /* Data phase. */ + u32 count = (u32)op->count * blocksize; + esp_scsi_dma(iobase, (u32)op->buf_fl, count, + cdb_is_read(cdbcmd, blocksize)); + outb(ESP_CMD_TI | ESP_CMD_DMA, iobase + ESP_CMD); + continue; + } + } + + /* At end of DMA TC is set again -> complete command. */ + if (state == 1 && (stat & ESP_STAT_TC)) { + state++; + outb(ESP_CMD_ICCS, iobase + ESP_CMD); + continue; + } + + /* Finally read data from the message in phase. */ + if (state == 2 && (stat & ESP_STAT_MSG)) { + state++; + status = inb(iobase + ESP_FIFO); + inb(iobase + ESP_FIFO); + break; + } + usleep(5); + } + + if (status == 0) { + return DISK_RET_SUCCESS; + } + + return DISK_RET_EBADTRACK; +} + +int +esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_ESP_SCSI) + return DISK_RET_EBADTRACK; + + struct esp_lun_s *llun_gf = + container_of(op->drive_gf, struct esp_lun_s, drive); + + return esp_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct esp_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_ESP_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "esp %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +esp_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + esp_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_esp_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found esp at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // reset + outb(ESP_CMD_RESET, iobase + ESP_CMD); + + int i; + for (i = 0; i <= 7; i++) + esp_scsi_scan_target(pci, iobase, i); + + return; +} + +void +esp_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ESP_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init esp\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_AMD + || pci->device != PCI_DEVICE_ID_AMD_SCSI) + continue; + init_esp_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/esp-scsi.h b/qemu/roms/seabios/src/hw/esp-scsi.h new file mode 100644 index 000000000..dc555f395 --- /dev/null +++ b/qemu/roms/seabios/src/hw/esp-scsi.h @@ -0,0 +1,8 @@ +#ifndef __ESP_SCSI_H +#define __ESP_SCSI_H + +struct disk_op_s; +int esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void esp_scsi_setup(void); + +#endif /* __ESP_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/floppy.c b/qemu/roms/seabios/src/hw/floppy.c new file mode 100644 index 000000000..d60362a34 --- /dev/null +++ b/qemu/roms/seabios/src/hw/floppy.c @@ -0,0 +1,673 @@ +// 16bit code to access floppy drives. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_FLOPPY +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // pci_to_bdf +#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA +#include "pic.h" // pic_eoi1 +#include "romfile.h" // romfile_loadint +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define PORT_FD_BASE 0x03f0 +#define PORT_FD_DOR 0x03f2 +#define PORT_FD_STATUS 0x03f4 +#define PORT_FD_DATA 0x03f5 +#define PORT_FD_DIR 0x03f7 + +#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors +#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02 +#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds +#define FLOPPY_FILLBYTE 0xf6 +#define FLOPPY_GAPLEN 0x1B +#define FLOPPY_FORMAT_GAPLEN 0x6c +#define FLOPPY_PIO_TIMEOUT 1000 + +// New diskette parameter table adding 3 parameters from IBM +// Since no provisions are made for multiple drive types, most +// values in this table are ignored. I set parameters for 1.44M +// floppy here +struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = { + .dbt = { + .specify1 = 0xAF, // step rate 12ms, head unload 240ms + .specify2 = 0x02, // head load time 4ms, DMA used + .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds + .bps_code = FLOPPY_SIZE_CODE, + .sectors = 18, + .interblock_len = FLOPPY_GAPLEN, + .data_len = FLOPPY_DATALEN, + .gap_len = FLOPPY_FORMAT_GAPLEN, + .fill_byte = FLOPPY_FILLBYTE, + .settle_time = 0x0F, // 15ms + .startup_time = 0x08, // 1 second + }, + .max_track = 79, // maximum track + .data_rate = 0, // data transfer rate + .drive_type = 4, // drive type in cmos +}; + +struct floppyinfo_s { + struct chs_s chs; + u8 floppy_size; + u8 data_rate; +}; + +#define FLOPPY_SIZE_525 0x01 +#define FLOPPY_SIZE_350 0x02 + +#define FLOPPY_RATE_500K 0x00 +#define FLOPPY_RATE_300K 0x01 +#define FLOPPY_RATE_250K 0x02 +#define FLOPPY_RATE_1M 0x03 + +struct floppyinfo_s FloppyInfo[] VARFSEG = { + // Unknown + { {0, 0, 0}, 0x00, 0x00}, + // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors + { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors + { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K}, + // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors + { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K}, + // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors + { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K}, + // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors + { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M}, + // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors + { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, + // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors + { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors + { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, +}; + +struct drive_s * +init_floppy(int floppyid, int ftype) +{ + if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) { + dprintf(1, "Bad floppy type %d\n", ftype); + return NULL; + } + + struct drive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return NULL; + } + memset(drive, 0, sizeof(*drive)); + drive->cntl_id = floppyid; + drive->type = DTYPE_FLOPPY; + drive->blksize = DISK_SECTOR_SIZE; + drive->floppy_type = ftype; + drive->sectors = (u64)-1; + + memcpy(&drive->lchs, &FloppyInfo[ftype].chs + , sizeof(FloppyInfo[ftype].chs)); + return drive; +} + +static void +addFloppy(int floppyid, int ftype) +{ + struct drive_s *drive = init_floppy(floppyid, ftype); + if (!drive) + return; + char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid); + struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */ + int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid); + boot_add_floppy(drive, desc, prio); +} + +void +floppy_setup(void) +{ + memcpy(&diskette_param_table, &diskette_param_table2 + , sizeof(diskette_param_table)); + SET_IVT(0x1E, SEGOFF(SEG_BIOS + , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR)); + + if (! CONFIG_FLOPPY) + return; + dprintf(3, "init floppy drives\n"); + + if (CONFIG_QEMU) { + u8 type = rtc_read(CMOS_FLOPPY_DRIVE_TYPE); + if (type & 0xf0) + addFloppy(0, type >> 4); + if (type & 0x0f) + addFloppy(1, type & 0x0f); + } else { + u8 type = romfile_loadint("etc/floppy0", 0); + if (type) + addFloppy(0, type); + type = romfile_loadint("etc/floppy1", 0); + if (type) + addFloppy(1, type); + } + + enable_hwirq(6, FUNC16(entry_0e)); +} + +// Find a floppy type that matches a given image size. +int +find_floppy_type(u32 size) +{ + int i; + for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) { + struct chs_s *c = &FloppyInfo[i].chs; + if (c->cylinder * c->head * c->sector * DISK_SECTOR_SIZE == size) + return i; + } + return -1; +} + + +/**************************************************************** + * Low-level floppy IO + ****************************************************************/ + +u8 FloppyDOR VARLOW; + +static inline void +floppy_dor_write(u8 val) +{ + outb(val, PORT_FD_DOR); + SET_LOW(FloppyDOR, val); +} + +static void +floppy_disable_controller(void) +{ + dprintf(2, "Floppy_disable_controller\n"); + floppy_dor_write(0x00); +} + +static int +floppy_wait_irq(void) +{ + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + for (;;) { + if (!GET_BDA(floppy_motor_counter)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + frs = GET_BDA(floppy_recalibration_status); + if (frs & FRS_IRQ) + break; + // Could use yield_toirq() here, but that causes issues on + // bochs, so use yield() instead. + yield(); + } + + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + return DISK_RET_SUCCESS; +} + +// Floppy commands +#define FCF_WAITIRQ 0x10000 +#define FC_CHECKIRQ (0x08 | (0<<8) | (2<<12)) +#define FC_SEEK (0x0f | (2<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_RECALIBRATE (0x07 | (1<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_READID (0x4a | (1<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_READ (0xe6 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_WRITE (0xc5 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_FORMAT (0x4d | (5<<8) | (7<<12) | FCF_WAITIRQ) + +// Send the specified command and it's parameters to the floppy controller. +static int +floppy_pio(int command, u8 *param) +{ + dprintf(9, "Floppy pio command %x\n", command); + // Send command and parameters to controller. + u32 end = timer_calc(FLOPPY_PIO_TIMEOUT); + int send = (command >> 8) & 0xf; + int i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + yield(); + continue; + } + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + if (i == 0) + outb(command & 0xff, PORT_FD_DATA); + else + outb(param[i-1], PORT_FD_DATA); + if (i++ >= send) + break; + } + + // Wait for command to complete. + if (command & FCF_WAITIRQ) { + int ret = floppy_wait_irq(); + if (ret) + return ret; + } + + // Read response from controller. + end = timer_calc(FLOPPY_PIO_TIMEOUT); + int receive = (command >> 12) & 0xf; + i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + yield(); + continue; + } + if (i >= receive) { + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + break; + } + if (!(sts & 0x40)) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + param[i++] = inb(PORT_FD_DATA); + } + + return DISK_RET_SUCCESS; +} + +static int +floppy_enable_controller(void) +{ + dprintf(2, "Floppy_enable_controller\n"); + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + floppy_dor_write(0x00); + floppy_dor_write(0x0c); + int ret = floppy_wait_irq(); + if (ret) + return ret; + + u8 param[2]; + return floppy_pio(FC_CHECKIRQ, param); +} + +// Activate a drive and send a command to it. +static int +floppy_drive_pio(u8 floppyid, int command, u8 *param) +{ + // Enable controller if it isn't running. + if (!(GET_LOW(FloppyDOR) & 0x04)) { + int ret = floppy_enable_controller(); + if (ret) + return ret; + } + + // reset the disk motor timeout value of INT 08 + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + + // Turn on motor of selected drive, DMA & int enabled, normal operation + floppy_dor_write((floppyid ? 0x20 : 0x10) | 0x0c | floppyid); + + // Send command. + int ret = floppy_pio(command, param); + if (ret) + return ret; + + // Check IRQ command is needed after irq commands with no results + if ((command & FCF_WAITIRQ) && ((command >> 12) & 0xf) == 0) + return floppy_pio(FC_CHECKIRQ, param); + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy media sense and seeking + ****************************************************************/ + +static int +floppy_drive_recal(u8 floppyid) +{ + dprintf(2, "Floppy_drive_recal %d\n", floppyid); + // send Recalibrate command to controller + u8 param[2]; + param[0] = floppyid; + int ret = floppy_drive_pio(floppyid, FC_RECALIBRATE, param); + if (ret) + return ret; + + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | (1<<floppyid)); + SET_BDA(floppy_track[floppyid], 0); + return DISK_RET_SUCCESS; +} + +static int +floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head) +{ + // Set data rate. + outb(data_rate, PORT_FD_DIR); + + // send Read Sector Id command + u8 param[7]; + param[0] = (head << 2) | floppyid; // HD DR1 DR2 + int ret = floppy_drive_pio(floppyid, FC_READID, param); + if (ret) + return ret; + if (param[0] & 0xc0) + return -1; + return 0; +} + +static int +floppy_media_sense(struct drive_s *drive_gf) +{ + u8 ftype = GET_GLOBALFLAT(drive_gf->floppy_type), stype = ftype; + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + + u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + int ret = floppy_drive_readid(floppyid, data_rate, 0); + if (ret) { + // Attempt media sense. + for (stype=1; ; stype++) { + if (stype >= ARRAY_SIZE(FloppyInfo)) + return DISK_RET_EMEDIA; + if (stype==ftype + || (GET_GLOBAL(FloppyInfo[stype].floppy_size) + != GET_GLOBAL(FloppyInfo[ftype].floppy_size)) + || (GET_GLOBAL(FloppyInfo[stype].chs.head) + > GET_GLOBAL(FloppyInfo[ftype].chs.head)) + || (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + > GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + || (GET_GLOBAL(FloppyInfo[stype].chs.sector) + > GET_GLOBAL(FloppyInfo[ftype].chs.sector))) + continue; + data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + ret = floppy_drive_readid(floppyid, data_rate, 0); + if (!ret) + break; + } + } + dprintf(2, "Floppy_media_sense on drive %d found rate %d\n" + , floppyid, data_rate); + + u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6; + SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6)); + u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07)); + u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media; + if (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + < GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + fms |= FMS_DOUBLE_STEPPING; + SET_BDA(floppy_media_state[floppyid], fms); + + return DISK_RET_SUCCESS; +} + +// Prepare a floppy for a data transfer. +static int +floppy_prep(struct drive_s *drive_gf, u8 cylinder) +{ + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + if (!(GET_BDA(floppy_recalibration_status) & (1<<floppyid)) || + !(GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) { + // Recalibrate drive. + int ret = floppy_drive_recal(floppyid); + if (ret) + return ret; + + // Sense media. + ret = floppy_media_sense(drive_gf); + if (ret) + return ret; + } + + // Seek to cylinder if needed. + u8 lastcyl = GET_BDA(floppy_track[floppyid]); + if (cylinder != lastcyl) { + u8 param[2]; + param[0] = floppyid; + param[1] = cylinder; + int ret = floppy_drive_pio(floppyid, FC_SEEK, param); + if (ret) + return ret; + SET_BDA(floppy_track[floppyid], cylinder); + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy DMA transfer + ****************************************************************/ + +// Perform a floppy transfer command (setup DMA and issue PIO). +static int +floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param) +{ + // Setup DMA controller + int isWrite = command != FC_READ; + int ret = dma_floppy((u32)op->buf_fl, count, isWrite); + if (ret) + return DISK_RET_EBOUNDARY; + + // Invoke floppy controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + ret = floppy_drive_pio(floppyid, command, param); + if (ret) + return ret; + + // Populate floppy_return_status in BDA + int i; + for (i=0; i<7; i++) + SET_BDA(floppy_return_status[i], param[i]); + + if (param[0] & 0xc0) { + if (param[1] & 0x02) + return DISK_RET_EWRITEPROTECT; + dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n" + , param[0], param[1], param[2], param[3] + , param[4], param[5], param[6]); + return DISK_RET_ECONTROLLER; + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy handlers + ****************************************************************/ + +static struct chs_s +lba2chs(struct disk_op_s *op) +{ + struct chs_s res = { }; + + u32 tmp = op->lba; + u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector); + res.sector = (tmp % nls) + 1; + + tmp /= nls; + u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head); + res.head = tmp % nlh; + + tmp /= nlh; + res.cylinder = tmp; + + return res; +} + +// diskette controller reset +static int +floppy_reset(struct disk_op_s *op) +{ + SET_BDA(floppy_recalibration_status, 0); + SET_BDA(floppy_media_state[0], 0); + SET_BDA(floppy_media_state[1], 0); + SET_BDA(floppy_track[0], 0); + SET_BDA(floppy_track[1], 0); + SET_BDA(floppy_last_data_rate, 0); + floppy_disable_controller(); + return floppy_enable_controller(); +} + +// Read Diskette Sectors +static int +floppy_read(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send read-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to read on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + return floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_READ, param); +} + +// Write Diskette Sectors +static int +floppy_write(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send write-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to write on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + return floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_WRITE, param); +} + +// Verify Diskette Sectors +static int +floppy_verify(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // This command isn't implemented - just return success. + return DISK_RET_SUCCESS; +} + +// format diskette track +static int +floppy_format(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int ret = floppy_prep(op->drive_gf, chs.cylinder); + if (ret) + return ret; + + // send format-track command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[7]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = FLOPPY_SIZE_CODE; + param[2] = op->count; // number of sectors per track + param[3] = FLOPPY_FORMAT_GAPLEN; + param[4] = FLOPPY_FILLBYTE; + return floppy_dma_cmd(op, op->count * 4, FC_FORMAT, param); +} + +int +process_floppy_op(struct disk_op_s *op) +{ + if (!CONFIG_FLOPPY) + return 0; + + switch (op->command) { + case CMD_RESET: + return floppy_reset(op); + case CMD_READ: + return floppy_read(op); + case CMD_WRITE: + return floppy_write(op); + case CMD_VERIFY: + return floppy_verify(op); + case CMD_FORMAT: + return floppy_format(op); + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * HW irqs + ****************************************************************/ + +// INT 0Eh Diskette Hardware ISR Entry Point +void VISIBLE16 +handle_0e(void) +{ + if (! CONFIG_FLOPPY) + return; + debug_isr(DEBUG_ISR_0e); + + // diskette interrupt has occurred + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | FRS_IRQ); + + pic_eoi1(); +} + +// Called from int08 handler. +void +floppy_tick(void) +{ + if (! CONFIG_FLOPPY) + return; + + // time to turn off drive(s)? + u8 fcount = GET_BDA(floppy_motor_counter); + if (fcount) { + fcount--; + SET_BDA(floppy_motor_counter, fcount); + if (fcount == 0) + // turn motor(s) off + floppy_dor_write(GET_LOW(FloppyDOR) & ~0xf0); + } +} diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.c b/qemu/roms/seabios/src/hw/lsi-scsi.c new file mode 100644 index 000000000..b1d6bbf4b --- /dev/null +++ b/qemu/roms/seabios/src/hw/lsi-scsi.c @@ -0,0 +1,217 @@ +// (qemu-emulated) lsi53c895a boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define LSI_REG_DSTAT 0x0c +#define LSI_REG_ISTAT0 0x14 +#define LSI_REG_DSP0 0x2c +#define LSI_REG_DSP1 0x2d +#define LSI_REG_DSP2 0x2e +#define LSI_REG_DSP3 0x2f +#define LSI_REG_SIST0 0x42 +#define LSI_REG_SIST1 0x43 + +#define LSI_ISTAT0_DIP 0x01 +#define LSI_ISTAT0_SIP 0x02 +#define LSI_ISTAT0_INTF 0x04 +#define LSI_ISTAT0_CON 0x08 +#define LSI_ISTAT0_SEM 0x10 +#define LSI_ISTAT0_SIGP 0x20 +#define LSI_ISTAT0_SRST 0x40 +#define LSI_ISTAT0_ABRT 0x80 + +struct lsi_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static int +lsi_scsi_cmd(struct lsi_lun_s *llun_gf, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + u32 dma = ((cdb_is_read(cdbcmd, blocksize) ? 0x01000000 : 0x00000000) | + (op->count * blocksize)); + u8 msgout[] = { + 0x80 | lun, // select lun + 0x08, + }; + u8 status = 0xff; + u8 msgin_tmp[2]; + u8 msgin = 0xff; + + u32 script[] = { + /* select target, send scsi command */ + 0x40000000 | target << 16, // select target + 0x00000000, + 0x06000001, // msgout + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgout), + 0x02000010, // scsi command + (u32)MAKE_FLATPTR(GET_SEG(SS), cdbcmd), + + /* handle disconnect */ + 0x87820000, // phase == msgin ? + 0x00000018, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + 0x50000000, // re-select + 0x00000000, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + + /* dma data, get status, raise irq */ + dma, // dma data + (u32)op->buf_fl, + 0x03000001, // status + (u32)MAKE_FLATPTR(GET_SEG(SS), &status), + 0x07000001, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin), + 0x98080000, // dma irq + 0x00000000, + }; + u32 dsp = (u32)MAKE_FLATPTR(GET_SEG(SS), &script); + + outb(dsp & 0xff, iobase + LSI_REG_DSP0); + outb((dsp >> 8) & 0xff, iobase + LSI_REG_DSP1); + outb((dsp >> 16) & 0xff, iobase + LSI_REG_DSP2); + outb((dsp >> 24) & 0xff, iobase + LSI_REG_DSP3); + + for (;;) { + u8 dstat = inb(iobase + LSI_REG_DSTAT); + u8 sist0 = inb(iobase + LSI_REG_SIST0); + u8 sist1 = inb(iobase + LSI_REG_SIST1); + if (sist0 || sist1) { + goto fail; + } + if (dstat & 0x04) { + break; + } + usleep(5); + } + + if (msgin == 0 && status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +int +lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_LSI_SCSI) + return DISK_RET_EBADTRACK; + + struct lsi_lun_s *llun_gf = + container_of(op->drive_gf, struct lsi_lun_s, drive); + + return lsi_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct lsi_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_LSI_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "lsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +lsi_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + lsi_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_lsi_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found lsi53c895a at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + // reset + outb(LSI_ISTAT0_SRST, iobase + LSI_REG_ISTAT0); + + int i; + for (i = 0; i < 7; i++) + lsi_scsi_scan_target(pci, iobase, i); + + return; +} + +void +lsi_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_LSI_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init lsi53c895a\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC + || pci->device != PCI_DEVICE_ID_LSI_53C895A) + continue; + init_lsi_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/lsi-scsi.h b/qemu/roms/seabios/src/hw/lsi-scsi.h new file mode 100644 index 000000000..9c5a9b212 --- /dev/null +++ b/qemu/roms/seabios/src/hw/lsi-scsi.h @@ -0,0 +1,8 @@ +#ifndef __LSI_SCSI_H +#define __LSI_SCSI_H + +struct disk_op_s; +int lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void lsi_scsi_setup(void); + +#endif /* __LSI_SCSI_H */ diff --git a/qemu/roms/seabios/src/hw/megasas.c b/qemu/roms/seabios/src/hw/megasas.c new file mode 100644 index 000000000..b2a65e48b --- /dev/null +++ b/qemu/roms/seabios/src/hw/megasas.c @@ -0,0 +1,404 @@ +// MegaRAID SAS boot support. +// +// Copyright (C) 2012 Hannes Reinecke, SUSE Linux Products GmbH +// +// Authors: +// Hannes Reinecke <hare@suse.de> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_XXX +#include "pci_regs.h" // PCI_VENDOR_ID +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define MFI_DB 0x0 // Doorbell +#define MFI_OMSG0 0x18 // Outbound message 0 +#define MFI_IDB 0x20 // Inbound doorbell +#define MFI_ODB 0x2c // Outbound doorbell +#define MFI_IQP 0x40 // Inbound queue port +#define MFI_OSP0 0xb0 // Outbound scratch pad0 +#define MFI_IQPL 0xc0 // Inbound queue port (low bytes) +#define MFI_IQPH 0xc4 // Inbound queue port (high bytes) + +#define MFI_STATE_MASK 0xf0000000 +#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 +#define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000 +#define MFI_STATE_READY 0xb0000000 +#define MFI_STATE_OPERATIONAL 0xc0000000 +#define MFI_STATE_FAULT 0xf0000000 + +/* MFI Commands */ +typedef enum { + MFI_CMD_INIT = 0x00, + MFI_CMD_LD_READ, + MFI_CMD_LD_WRITE, + MFI_CMD_LD_SCSI_IO, + MFI_CMD_PD_SCSI_IO, + MFI_CMD_DCMD, + MFI_CMD_ABORT, + MFI_CMD_SMP, + MFI_CMD_STP +} mfi_cmd_t; + +struct megasas_cmd_frame { + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 context_64; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xfer_len; /*14h */ + + union { + struct { + u32 opcode; /*18h */ + u8 mbox[12]; /*1Ch */ + u32 sgl_addr; /*28h */ + u32 sgl_len; /*32h */ + u32 pad; /*34h */ + } dcmd; + struct { + u32 sense_buf_lo; /*18h */ + u32 sense_buf_hi; /*1Ch */ + u8 cdb[16]; /*20h */ + u32 sgl_addr; /*30h */ + u32 sgl_len; /*34h */ + } pthru; + struct { + u8 pad[22]; /*18h */ + } gen; + }; +} __attribute__ ((packed)); + +struct mfi_ld_list_s { + u32 count; + u32 reserved_0; + struct { + u8 target; + u8 lun; + u16 seq; + u8 state; + u8 reserved_1[3]; + u64 size; + } lds[64]; +} __attribute__ ((packed)); + +#define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout + +struct megasas_lun_s { + struct drive_s drive; + struct megasas_cmd_frame *frame; + u32 iobase; + u16 pci_id; + u8 target; + u8 lun; +}; + +static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, + struct megasas_cmd_frame *frame) +{ + u32 frame_addr = (u32)frame; + int frame_count = 1; + u8 cmd_state; + + dprintf(2, "Frame 0x%x\n", frame_addr); + if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 || + pci_id == PCI_DEVICE_ID_LSI_SAS2008) { + outl(0, ioaddr + MFI_IQPH); + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQPL); + } else if (pci_id == PCI_DEVICE_ID_DELL_PERC5 || + pci_id == PCI_DEVICE_ID_LSI_SAS1064R || + pci_id == PCI_DEVICE_ID_LSI_VERDE_ZCR) { + outl(frame_addr >> 3 | frame_count, ioaddr + MFI_IQP); + } else { + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP); + } + + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + do { + for (;;) { + cmd_state = GET_LOWFLAT(frame->cmd_status); + if (cmd_state != 0xff) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + } while (cmd_state == 0xff); + + if (cmd_state == 0 || cmd_state == 0x2d) + return 0; + dprintf(1, "ERROR: Frame 0x%x, status 0x%x\n", frame_addr, cmd_state); + return -1; +} + +int +megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct megasas_lun_s *mlun_gf = + container_of(op->drive_gf, struct megasas_lun_s, drive); + u8 *cdb = cdbcmd; + struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame); + u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id); + int i; + + if (!CONFIG_MEGASAS) + return DISK_RET_EBADTRACK; + + memset_fl(frame, 0, sizeof(*frame)); + SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO); + SET_LOWFLAT(frame->cmd_status, 0xFF); + SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target)); + SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun)); + SET_LOWFLAT(frame->flags, 0x0001); + SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize); + SET_LOWFLAT(frame->cdb_len, 16); + + for (i = 0; i < 16; i++) { + SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]); + } + dprintf(2, "pthru cmd 0x%x count %d bs %d\n", + cdb[0], op->count, blocksize); + + if (op->count) { + SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl); + SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize); + SET_LOWFLAT(frame->sge_count, 1); + } + SET_LOWFLAT(frame->context, (u32)frame); + + if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0) + return DISK_RET_SUCCESS; + + dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]); + return DISK_RET_EBADTRACK; +} + +static int +megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun)); + char *name; + int prio, ret = 0; + + if (!mlun) { + warn_noalloc(); + return -1; + } + memset(mlun, 0, sizeof(*mlun)); + mlun->drive.type = DTYPE_MEGASAS; + mlun->drive.cntl_id = pci->bdf; + mlun->pci_id = pci->device; + mlun->target = target; + mlun->lun = lun; + mlun->iobase = iobase; + mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame)); + if (!mlun->frame) { + warn_noalloc(); + free(mlun); + return -1; + } + name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + prio = bootprio_find_scsi_device(pci, target, lun); + ret = scsi_drive_setup(&mlun->drive, name, prio); + free(name); + if (ret) { + free(mlun->frame); + free(mlun); + ret = -1; + } + + return ret; +} + +static void megasas_scan_target(struct pci_device *pci, u32 iobase) +{ + struct mfi_ld_list_s ld_list; + struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame)); + int i; + + memset(&ld_list, 0, sizeof(ld_list)); + memset_fl(frame, 0, sizeof(*frame)); + + frame->cmd = MFI_CMD_DCMD; + frame->cmd_status = 0xFF; + frame->sge_count = 1; + frame->flags = 0x0011; + frame->data_xfer_len = sizeof(ld_list); + frame->dcmd.opcode = 0x03010000; + frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list); + frame->dcmd.sgl_len = sizeof(ld_list); + frame->context = (u32)frame; + + if (megasas_fire_cmd(pci->device, iobase, frame) == 0) { + dprintf(2, "%d LD found\n", ld_list.count); + for (i = 0; i < ld_list.count; i++) { + dprintf(2, "LD %d:%d state 0x%x\n", + ld_list.lds[i].target, ld_list.lds[i].lun, + ld_list.lds[i].state); + if (ld_list.lds[i].state != 0) { + megasas_add_lun(pci, iobase, + ld_list.lds[i].target, ld_list.lds[i].lun); + } + } + } +} + +static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) +{ + u32 fw_state = 0, new_state, mfi_flags = 0; + + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + + while (fw_state != new_state) { + switch (new_state) { + case MFI_STATE_FAULT: + dprintf(1, "ERROR: fw in fault state\n"); + return -1; + break; + case MFI_STATE_WAIT_HANDSHAKE: + mfi_flags = 0x08; + /* fallthrough */ + case MFI_STATE_BOOT_MESSAGE_PENDING: + mfi_flags |= 0x10; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + } else { + outl(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_OPERATIONAL: + mfi_flags = 0x07; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + int j = 0; + u32 doorbell; + + while (j < MEGASAS_POLL_TIMEOUT) { + doorbell = inl(ioaddr + MFI_DB) & 1; + if (!doorbell) + break; + msleep(20); + j++; + } + } + } else { + outw(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_READY: + dprintf(2, "MegaRAID SAS fw ready\n"); + return 0; + } + // The current state should not last longer than poll timeout + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + for (;;) { + if (timer_check(end)) { + break; + } + yield(); + fw_state = new_state; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + if (new_state != fw_state) { + break; + } + } + } + dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK); + return -1; +} + +static void +init_megasas(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK; + + if (!iobase) + iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + // reset + if (megasas_transition_to_ready(pci, iobase) == 0) + megasas_scan_target(pci, iobase); + + return; +} + +void +megasas_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_MEGASAS) + return; + + dprintf(3, "init megasas\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC && + pci->vendor != PCI_VENDOR_ID_DELL) + continue; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_LSI_SAS1078 || + pci->device == PCI_DEVICE_ID_LSI_SAS1078DE || + pci->device == PCI_DEVICE_ID_LSI_SAS2108 || + pci->device == PCI_DEVICE_ID_LSI_SAS2108E || + pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR || + pci->device == PCI_DEVICE_ID_DELL_PERC5 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) + init_megasas(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/megasas.h b/qemu/roms/seabios/src/hw/megasas.h new file mode 100644 index 000000000..124042e1c --- /dev/null +++ b/qemu/roms/seabios/src/hw/megasas.h @@ -0,0 +1,8 @@ +#ifndef __MEGASAS_H +#define __MEGASAS_H + +struct disk_op_s; +int megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void megasas_setup(void); + +#endif /* __MEGASAS_H */ diff --git a/qemu/roms/seabios/src/hw/pci.c b/qemu/roms/seabios/src/hw/pci.c new file mode 100644 index 000000000..0379b558e --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci.c @@ -0,0 +1,277 @@ +// PCI config space access functions. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "pci.h" // pci_config_writel +#include "pci_regs.h" // PCI_VENDOR_ID +#include "romfile.h" // romfile_loadint +#include "string.h" // memset +#include "util.h" // udelay +#include "x86.h" // outl + +void pci_config_writel(u16 bdf, u32 addr, u32 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outl(val, PORT_PCI_DATA); +} + +void pci_config_writew(u16 bdf, u32 addr, u16 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outw(val, PORT_PCI_DATA + (addr & 2)); +} + +void pci_config_writeb(u16 bdf, u32 addr, u8 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outb(val, PORT_PCI_DATA + (addr & 3)); +} + +u32 pci_config_readl(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inl(PORT_PCI_DATA); +} + +u16 pci_config_readw(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inw(PORT_PCI_DATA + (addr & 2)); +} + +u8 pci_config_readb(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inb(PORT_PCI_DATA + (addr & 3)); +} + +void +pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on) +{ + u16 val = pci_config_readw(bdf, addr); + val = (val & ~off) | on; + pci_config_writew(bdf, addr, val); +} + +// Helper function for foreachbdf() macro - return next device +int +pci_next(int bdf, int bus) +{ + if (pci_bdf_to_fn(bdf) == 0 + && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0) + // Last found device wasn't a multi-function device - skip to + // the next device. + bdf += 8; + else + bdf += 1; + + for (;;) { + if (pci_bdf_to_bus(bdf) != bus) + return -1; + + u16 v = pci_config_readw(bdf, PCI_VENDOR_ID); + if (v != 0x0000 && v != 0xffff) + // Device is present. + return bdf; + + if (pci_bdf_to_fn(bdf) == 0) + bdf += 8; + else + bdf += 1; + } +} + +struct hlist_head PCIDevices VARVERIFY32INIT; +int MaxPCIBus VARFSEG; + +// Check if PCI is available at all +int +pci_probe_host(void) +{ + outl(0x80000000, PORT_PCI_CMD); + if (inl(PORT_PCI_CMD) != 0x80000000) { + dprintf(1, "Detected non-PCI system\n"); + return -1; + } + return 0; +} + +// Find all PCI devices and populate PCIDevices linked list. +void +pci_probe_devices(void) +{ + dprintf(3, "PCI probe\n"); + struct pci_device *busdevs[256]; + memset(busdevs, 0, sizeof(busdevs)); + struct hlist_node **pprev = &PCIDevices.first; + int extraroots = romfile_loadint("etc/extra-pci-roots", 0); + int bus = -1, lastbus = 0, rootbuses = 0, count=0; + while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + // Create new pci_device struct and add to list. + struct pci_device *dev = malloc_tmp(sizeof(*dev)); + if (!dev) { + warn_noalloc(); + return; + } + memset(dev, 0, sizeof(*dev)); + hlist_add(&dev->node, pprev); + pprev = &dev->node.next; + count++; + + // Find parent device. + int rootbus; + struct pci_device *parent = busdevs[bus]; + if (!parent) { + if (bus != lastbus) + rootbuses++; + lastbus = bus; + rootbus = rootbuses; + if (bus > MaxPCIBus) + MaxPCIBus = bus; + } else { + rootbus = parent->rootbus; + } + + // Populate pci_device info. + dev->bdf = bdf; + dev->parent = parent; + dev->rootbus = rootbus; + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + dev->vendor = vendev & 0xffff; + dev->device = vendev >> 16; + u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION); + dev->class = classrev >> 16; + dev->prog_if = classrev >> 8; + dev->revision = classrev & 0xff; + dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE); + u8 v = dev->header_type & 0x7f; + if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) { + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + dev->secondary_bus = secbus; + if (secbus > bus && !busdevs[secbus]) + busdevs[secbus] = dev; + if (secbus > MaxPCIBus) + MaxPCIBus = secbus; + } + dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf) + , dev->vendor, dev->device, dev->class); + } + } + dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus); +} + +// Search for a device with the specified vendor and device ids. +struct pci_device * +pci_find_device(u16 vendid, u16 devid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor == vendid && pci->device == devid) + return pci; + } + return NULL; +} + +// Search for a device with the specified class id. +struct pci_device * +pci_find_class(u16 classid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == classid) + return pci; + } + return NULL; +} + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg) +{ + while (ids->vendid || ids->class_mask) { + if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) && + (ids->devid == PCI_ANY_ID || ids->devid == pci->device) && + !((ids->class ^ pci->class) & ids->class_mask)) { + if (ids->func) + ids->func(pci, arg); + return 0; + } + ids++; + } + return -1; +} + +struct pci_device * +pci_find_init_device(const struct pci_device_id *ids, void *arg) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci_init_device(ids, pci, arg) == 0) + return pci; + } + return NULL; +} + +u8 pci_find_capability(struct pci_device *pci, u8 cap_id) +{ + int i; + u8 cap; + u16 status = pci_config_readw(pci->bdf, PCI_STATUS); + + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + cap = pci_config_readb(pci->bdf, PCI_CAPABILITY_LIST); + for (i = 0; cap && i <= 0xff; i++) { + if (pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_ID) == cap_id) + return cap; + cap = pci_config_readb(pci->bdf, cap + PCI_CAP_LIST_NEXT); + } + + return 0; +} + +/* Test whether bridge support forwarding of transactions + * of a specific type. + * Note: disables bridge's window registers as a side effect. + */ +int pci_bridge_has_region(struct pci_device *pci, + enum pci_region_type region_type) +{ + u8 base; + + switch (region_type) { + case PCI_REGION_TYPE_IO: + base = PCI_IO_BASE; + break; + case PCI_REGION_TYPE_PREFMEM: + base = PCI_PREF_MEMORY_BASE; + break; + default: + /* Regular memory support is mandatory */ + return 1; + } + + pci_config_writeb(pci->bdf, base, 0xFF); + + return pci_config_readb(pci->bdf, base) != 0; +} + +void +pci_reboot(void) +{ + u8 v = inb(PORT_PCI_REBOOT) & ~6; + outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */ + udelay(50); + outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */ + udelay(50); +} diff --git a/qemu/roms/seabios/src/hw/pci.h b/qemu/roms/seabios/src/hw/pci.h new file mode 100644 index 000000000..0aaa84c1a --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci.h @@ -0,0 +1,131 @@ +#ifndef __PCI_H +#define __PCI_H + +#include "types.h" // u32 +#include "list.h" // hlist_node + +#define PORT_PCI_CMD 0x0cf8 +#define PORT_PCI_REBOOT 0x0cf9 +#define PORT_PCI_DATA 0x0cfc + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 +#define PCI_BRIDGE_NUM_REGIONS 2 + +enum pci_region_type { + PCI_REGION_TYPE_IO, + PCI_REGION_TYPE_MEM, + PCI_REGION_TYPE_PREFMEM, + PCI_REGION_TYPE_COUNT, +}; + +static inline u8 pci_bdf_to_bus(u16 bdf) { + return bdf >> 8; +} +static inline u8 pci_bdf_to_devfn(u16 bdf) { + return bdf & 0xff; +} +static inline u16 pci_bdf_to_busdev(u16 bdf) { + return bdf & ~0x07; +} +static inline u8 pci_bdf_to_dev(u16 bdf) { + return (bdf >> 3) & 0x1f; +} +static inline u8 pci_bdf_to_fn(u16 bdf) { + return bdf & 0x07; +} +static inline u16 pci_to_bdf(int bus, int dev, int fn) { + return (bus<<8) | (dev<<3) | fn; +} +static inline u16 pci_bus_devfn_to_bdf(int bus, u16 devfn) { + return (bus << 8) | devfn; +} + +void pci_config_writel(u16 bdf, u32 addr, u32 val); +void pci_config_writew(u16 bdf, u32 addr, u16 val); +void pci_config_writeb(u16 bdf, u32 addr, u8 val); +u32 pci_config_readl(u16 bdf, u32 addr); +u16 pci_config_readw(u16 bdf, u32 addr); +u8 pci_config_readb(u16 bdf, u32 addr); +void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on); + +struct pci_device *pci_find_device(u16 vendid, u16 devid); +struct pci_device *pci_find_class(u16 classid); + +struct pci_device { + u16 bdf; + u8 rootbus; + struct hlist_node node; + struct pci_device *parent; + + // Configuration space device information + u16 vendor, device; + u16 class; + u8 prog_if, revision; + u8 header_type; + u8 secondary_bus; + + // Local information on device. + int have_driver; +}; +extern u64 pcimem_start, pcimem_end; +extern u64 pcimem64_start, pcimem64_end; +extern struct hlist_head PCIDevices; +extern int MaxPCIBus; +int pci_probe_host(void); +void pci_probe_devices(void); +static inline u32 pci_classprog(struct pci_device *pci) { + return (pci->class << 8) | pci->prog_if; +} + +#define foreachpci(PCI) \ + hlist_for_each_entry(PCI, &PCIDevices, node) + +int pci_next(int bdf, int bus); +#define foreachbdf(BDF, BUS) \ + for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \ + ; BDF >= 0 \ + ; BDF=pci_next(BDF, (BUS))) + +#define PCI_ANY_ID (~0) +struct pci_device_id { + u32 vendid; + u32 devid; + u32 class; + u32 class_mask; + void (*func)(struct pci_device *pci, void *arg); +}; + +#define PCI_DEVICE(vendor_id, device_id, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = PCI_ANY_ID, \ + .class_mask = 0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = (class_code), \ + .class_mask = ~0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_END \ + { \ + .vendid = 0, \ + } + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg); +struct pci_device *pci_find_init_device(const struct pci_device_id *ids + , void *arg); +u8 pci_find_capability(struct pci_device *pci, u8 cap_id); +int pci_bridge_has_region(struct pci_device *pci, + enum pci_region_type region_type); +void pci_reboot(void); + +#endif diff --git a/qemu/roms/seabios/src/hw/pci_ids.h b/qemu/roms/seabios/src/hw/pci_ids.h new file mode 100644 index 000000000..1cd4f7269 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci_ids.h @@ -0,0 +1,2623 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601 +#define PCI_CLASS_STORAGE_SAS 0x0107 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_3D 0x0302 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCI_CLASS_SYSTEM_SDHCI 0x0805 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_SCANNER 0x0903 +#define PCI_CLASS_INPUT_GAMEPORT 0x0904 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a80 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300 +#define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 +#define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 +#define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_BASE_CLASS_WIRELESS 0x0d +#define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCI_CLASS_WIRELESS_WHCI 0x0d1010 + +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define PCI_BASE_CLASS_SATELLITE 0x0f +#define PCI_CLASS_SATELLITE_TV 0x0f00 +#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define PCI_CLASS_SATELLITE_VOICE 0x0f03 +#define PCI_CLASS_SATELLITE_DATA 0x0f04 + +#define PCI_BASE_CLASS_CRYPT 0x10 +#define PCI_CLASS_CRYPT_NETWORK 0x1000 +#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define PCI_CLASS_CRYPT_OTHER 0x1080 + +#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define PCI_CLASS_SP_DPIO 0x1100 +#define PCI_CLASS_SP_OTHER 0x1180 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_DEVICE_ID_TTTECH_MC322 0x000a + +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 + +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1 +#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2 +#define PCI_DEVICE_ID_BERKOM_A4T 0xffa4 +#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8 + +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 +#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc +#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 +#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 +#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 +#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35 +#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 +#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 +#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 +#define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178 +#define PCI_DEVICE_ID_COMPAQ_CISSC 0x46 +#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 + +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_NCR_53C810 0x0001 +#define PCI_DEVICE_ID_NCR_53C820 0x0002 +#define PCI_DEVICE_ID_NCR_53C825 0x0003 +#define PCI_DEVICE_ID_NCR_53C815 0x0004 +#define PCI_DEVICE_ID_LSI_53C810AP 0x0005 +#define PCI_DEVICE_ID_NCR_53C860 0x0006 +#define PCI_DEVICE_ID_LSI_53C1510 0x000a +#define PCI_DEVICE_ID_NCR_53C896 0x000b +#define PCI_DEVICE_ID_NCR_53C895 0x000c +#define PCI_DEVICE_ID_NCR_53C885 0x000d +#define PCI_DEVICE_ID_NCR_53C875 0x000f +#define PCI_DEVICE_ID_NCR_53C1510 0x0010 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_53C875A 0x0013 +#define PCI_DEVICE_ID_LSI_53C1010_33 0x0020 +#define PCI_DEVICE_ID_LSI_53C1010_66 0x0021 +#define PCI_DEVICE_ID_LSI_53C1030 0x0030 +#define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032 +#define PCI_DEVICE_ID_LSI_53C1035 0x0040 +#define PCI_DEVICE_ID_NCR_53C875J 0x008f +#define PCI_DEVICE_ID_LSI_FC909 0x0621 +#define PCI_DEVICE_ID_LSI_FC929 0x0622 +#define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623 +#define PCI_DEVICE_ID_LSI_FC919 0x0624 +#define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625 +#define PCI_DEVICE_ID_LSI_FC929X 0x0626 +#define PCI_DEVICE_ID_LSI_FC939X 0x0642 +#define PCI_DEVICE_ID_LSI_FC949X 0x0640 +#define PCI_DEVICE_ID_LSI_FC949ES 0x0646 +#define PCI_DEVICE_ID_LSI_FC919X 0x0628 +#define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 +#define PCI_DEVICE_ID_LSI_61C102 0x0901 +#define PCI_DEVICE_ID_LSI_63C815 0x1000 +#define PCI_DEVICE_ID_LSI_SAS1064 0x0050 +#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 +#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 +#define PCI_DEVICE_ID_LSI_SAS1066 0x005E +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054 +#define PCI_DEVICE_ID_LSI_SAS1064A 0x005C +#define PCI_DEVICE_ID_LSI_SAS1064E 0x0056 +#define PCI_DEVICE_ID_LSI_SAS1066E 0x005A +#define PCI_DEVICE_ID_LSI_SAS1068E 0x0058 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 +#define PCI_DEVICE_ID_LSI_SAS1078DE 0x007C +#define PCI_DEVICE_ID_LSI_SAS2108E 0x0078 +#define PCI_DEVICE_ID_LSI_SAS2108 0x0079 +#define PCI_DEVICE_ID_LSI_SAS2208 0x005B +#define PCI_DEVICE_ID_LSI_SAS3108 0x005D +#define PCI_DEVICE_ID_LSI_SAS2004 0x0071 +#define PCI_DEVICE_ID_LSI_SAS2008 0x0073 + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Mach64 */ +#define PCI_DEVICE_ID_ATI_68800 0x4158 +#define PCI_DEVICE_ID_ATI_215CT222 0x4354 +#define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215ET222 0x4554 +/* Mach64 / Rage */ +#define PCI_DEVICE_ID_ATI_215GB 0x4742 +#define PCI_DEVICE_ID_ATI_215GD 0x4744 +#define PCI_DEVICE_ID_ATI_215GI 0x4749 +#define PCI_DEVICE_ID_ATI_215GP 0x4750 +#define PCI_DEVICE_ID_ATI_215GQ 0x4751 +#define PCI_DEVICE_ID_ATI_215XL 0x4752 +#define PCI_DEVICE_ID_ATI_215GT 0x4754 +#define PCI_DEVICE_ID_ATI_215GTB 0x4755 +#define PCI_DEVICE_ID_ATI_215_IV 0x4756 +#define PCI_DEVICE_ID_ATI_215_IW 0x4757 +#define PCI_DEVICE_ID_ATI_215_IZ 0x475A +#define PCI_DEVICE_ID_ATI_210888GX 0x4758 +#define PCI_DEVICE_ID_ATI_215_LB 0x4c42 +#define PCI_DEVICE_ID_ATI_215_LD 0x4c44 +#define PCI_DEVICE_ID_ATI_215_LG 0x4c47 +#define PCI_DEVICE_ID_ATI_215_LI 0x4c49 +#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D +#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E +#define PCI_DEVICE_ID_ATI_215_LR 0x4c52 +#define PCI_DEVICE_ID_ATI_215_LS 0x4c53 +#define PCI_DEVICE_ID_ATI_264_LT 0x4c54 +/* Mach64 VT */ +#define PCI_DEVICE_ID_ATI_264VT 0x5654 +#define PCI_DEVICE_ID_ATI_264VU 0x5655 +#define PCI_DEVICE_ID_ATI_264VV 0x5656 +/* Rage128 GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245 +#define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246 +#define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247 +/* Rage128 VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b +#define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c +#define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345 +#define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346 +#define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347 +#define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348 +#define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b +#define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c +#define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d +#define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e +/* Rage128 Ultra */ +#define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446 +#define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c +#define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452 +#define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453 +#define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454 +#define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455 +/* Rage128 M3 */ +#define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45 +#define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46 +/* Rage128 M4 */ +#define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46 +#define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041 +#define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042 +#define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043 +#define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044 +#define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045 +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Rage128 Pro VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047 +#define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048 +#define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049 +#define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A +#define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B +#define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C +#define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D +#define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E +#define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F +#define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050 +#define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051 +#define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052 +#define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053 +#define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054 +#define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055 +#define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056 +#define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057 +#define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058 +/* Rage128 M4 */ +/* Radeon R100 */ +#define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144 +#define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145 +#define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146 +#define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 +#define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a +/* Radeon R200 (8500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c +#define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e +#define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f +#define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c +#define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242 +/* Radeon R200 (9100) */ +#define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d +/* Radeon RV200 (7500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157 +#define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158 +/* Radeon NV-100 */ +/* Radeon RV250 (9000) */ +#define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964 +#define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965 +#define PCI_DEVICE_ID_ATI_RADEON_If 0x4966 +#define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 +/* Radeon RV280 (9200) */ +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 +#define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 +/* Radeon R300 (9500) */ +/* Radeon R300 (9700) */ +#define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44 +#define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45 +#define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46 +#define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47 +/* Radeon R350 (9800) */ +/* Radeon RV350 (9600) */ +/* Radeon M6 */ +#define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59 +#define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a +/* Radeon M7 */ +#define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57 +#define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58 +/* Radeon M9 */ +#define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64 +#define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65 +#define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66 +#define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67 +/* Radeon */ +/* RadeonIGP */ +#define PCI_DEVICE_ID_ATI_RS100 0xcab0 +#define PCI_DEVICE_ID_ATI_RS200 0xcab2 +#define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2 +#define PCI_DEVICE_ID_ATI_RS250 0xcab3 +#define PCI_DEVICE_ID_ATI_RS300_100 0x5830 +#define PCI_DEVICE_ID_ATI_RS300_133 0x5831 +#define PCI_DEVICE_ID_ATI_RS300_166 0x5832 +#define PCI_DEVICE_ID_ATI_RS300_200 0x5833 +#define PCI_DEVICE_ID_ATI_RS350_100 0x7830 +#define PCI_DEVICE_ID_ATI_RS350_133 0x7831 +#define PCI_DEVICE_ID_ATI_RS350_166 0x7832 +#define PCI_DEVICE_ID_ATI_RS350_200 0x7833 +#define PCI_DEVICE_ID_ATI_RS400_100 0x5a30 +#define PCI_DEVICE_ID_ATI_RS400_133 0x5a31 +#define PCI_DEVICE_ID_ATI_RS400_166 0x5a32 +#define PCI_DEVICE_ID_ATI_RS400_200 0x5a33 +#define PCI_DEVICE_ID_ATI_RS480 0x5950 +/* ATI IXP Chipset */ +#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 +#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 +#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 +#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e +#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 +#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 +#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 +#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a +#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 +#define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385 +#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c +#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390 +#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c + +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_DEVICE_ID_VLSI_82C592 0x0005 +#define PCI_DEVICE_ID_VLSI_82C593 0x0006 +#define PCI_DEVICE_ID_VLSI_82C594 0x0007 +#define PCI_DEVICE_ID_VLSI_82C597 0x0009 +#define PCI_DEVICE_ID_VLSI_82C541 0x000c +#define PCI_DEVICE_ID_VLSI_82C543 0x000d +#define PCI_DEVICE_ID_VLSI_82C532 0x0101 +#define PCI_DEVICE_ID_VLSI_82C534 0x0102 +#define PCI_DEVICE_ID_VLSI_82C535 0x0104 +#define PCI_DEVICE_ID_VLSI_82C147 0x0105 +#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702 + +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_DEVICE_ID_ADL_2301 0x2301 + +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_DEVICE_ID_NS_87415 0x0002 +#define PCI_DEVICE_ID_NS_87560_LIO 0x000e +#define PCI_DEVICE_ID_NS_87560_USB 0x0012 +#define PCI_DEVICE_ID_NS_83815 0x0020 +#define PCI_DEVICE_ID_NS_83820 0x0022 +#define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b +#define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d +#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e +#define PCI_DEVICE_ID_NS_CS5535_USB 0x002f +#define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030 +#define PCI_DEVICE_ID_NS_SATURN 0x0035 +#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 +#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 +#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 +#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 +#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 +#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 +#define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510 +#define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511 +#define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515 +#define PCI_DEVICE_ID_NS_87410 0xd001 + +#define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028 + +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202 +#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 +#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 +#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 +#define PCI_DEVICE_ID_TSENG_ET6000 0x3208 + +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_DEVICE_ID_WEITEK_P9000 0x9001 +#define PCI_DEVICE_ID_WEITEK_P9100 0x9100 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_BRD 0x0001 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TGA 0x0004 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_TGA2 0x000D +#define PCI_DEVICE_ID_DEC_FDDI 0x000F +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 +#define PCI_DEVICE_ID_DEC_21052 0x0021 +#define PCI_DEVICE_ID_DEC_21150 0x0022 +#define PCI_DEVICE_ID_DEC_21152 0x0024 +#define PCI_DEVICE_ID_DEC_21153 0x0025 +#define PCI_DEVICE_ID_DEC_21154 0x0026 +#define PCI_DEVICE_ID_DEC_21285 0x1065 +#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_7548 0x0038 +#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 +#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 +#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 +#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac +#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 +#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc +#define PCI_DEVICE_ID_CIRRUS_5462 0x00d0 +#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4 +#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6 +#define PCI_DEVICE_ID_CIRRUS_6729 0x1100 +#define PCI_DEVICE_ID_CIRRUS_6832 0x1110 +#define PCI_DEVICE_ID_CIRRUS_7543 0x1202 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_DEVICE_ID_IBM_TR 0x0018 +#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e +#define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc +#define PCI_DEVICE_ID_IBM_SNIPE 0x0180 +#define PCI_DEVICE_ID_IBM_CITRINE 0x028C +#define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 +#define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219 +#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 +#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 + +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C + +#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */ +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 + +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_DEVICE_ID_WD_90C 0xc24a + +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_K8_NB 0x1100 +#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 +#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 +#define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 +#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 +#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 +#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 +#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 +#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 +#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 +#define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 +#define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C +#define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E +#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401 +#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409 +#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B +#define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410 +#define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411 +#define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413 +#define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440 +#define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441 +#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 +#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 +#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 +#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 +#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 +#define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a +#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b +#define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d +#define PCI_DEVICE_ID_AMD_8151_0 0x7454 +#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 +#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 +#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 +#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 +#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 +#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 +#define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094 +#define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095 +#define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096 +#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 +#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A + +#define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 +#define PCI_DEVICE_ID_AMD_LX_AES 0x2082 + +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#define PCI_DEVICE_ID_TRIDENT_9320 0x9320 +#define PCI_DEVICE_ID_TRIDENT_9388 0x9388 +#define PCI_DEVICE_ID_TRIDENT_9397 0x9397 +#define PCI_DEVICE_ID_TRIDENT_939A 0x939A +#define PCI_DEVICE_ID_TRIDENT_9520 0x9520 +#define PCI_DEVICE_ID_TRIDENT_9525 0x9525 +#define PCI_DEVICE_ID_TRIDENT_9420 0x9420 +#define PCI_DEVICE_ID_TRIDENT_9440 0x9440 +#define PCI_DEVICE_ID_TRIDENT_9660 0x9660 +#define PCI_DEVICE_ID_TRIDENT_9750 0x9750 +#define PCI_DEVICE_ID_TRIDENT_9850 0x9850 +#define PCI_DEVICE_ID_TRIDENT_9880 0x9880 +#define PCI_DEVICE_ID_TRIDENT_8400 0x8400 +#define PCI_DEVICE_ID_TRIDENT_8420 0x8420 +#define PCI_DEVICE_ID_TRIDENT_8500 0x8500 + +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_DEVICE_ID_AI_M1435 0x1435 + +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_DEVICE_ID_DELL_RACIII 0x0008 +#define PCI_DEVICE_ID_DELL_RAC4 0x0012 +#define PCI_DEVICE_ID_DELL_PERC5 0x0015 + +#define PCI_VENDOR_ID_MATROX 0x102B +#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518 +#define PCI_DEVICE_ID_MATROX_MIL 0x0519 +#define PCI_DEVICE_ID_MATROX_MYS 0x051A +#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b +#define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e +#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f +#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10 +#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000 +#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001 +#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520 +#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521 +#define PCI_DEVICE_ID_MATROX_G400 0x0525 +#define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530 +#define PCI_DEVICE_ID_MATROX_G550 0x2527 +#define PCI_DEVICE_ID_MATROX_VIA 0x4536 + +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_DEVICE_ID_CT_69000 0x00c0 +#define PCI_DEVICE_ID_CT_65545 0x00d8 +#define PCI_DEVICE_ID_CT_65548 0x00dc +#define PCI_DEVICE_ID_CT_65550 0x00e0 +#define PCI_DEVICE_ID_CT_65554 0x00e4 +#define PCI_DEVICE_ID_CT_65555 0x00e5 + +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_DEVICE_ID_MIRO_36050 0x5601 +#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe +#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */ +#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */ +#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */ +#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */ +#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */ +#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */ +#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */ +#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b +#define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e +#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */ +#define PCI_DEVICE_ID_NEC_VRC5476 0x009b +#define PCI_DEVICE_ID_NEC_VRC4173 0x00a5 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6 +#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */ +#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */ + +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_DEVICE_ID_FD_36C70 0x0000 + +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_DEVICE_ID_SI_5591_AGP 0x0001 +#define PCI_DEVICE_ID_SI_6202 0x0002 +#define PCI_DEVICE_ID_SI_503 0x0008 +#define PCI_DEVICE_ID_SI_ACPI 0x0009 +#define PCI_DEVICE_ID_SI_SMBUS 0x0016 +#define PCI_DEVICE_ID_SI_LPC 0x0018 +#define PCI_DEVICE_ID_SI_5597_VGA 0x0200 +#define PCI_DEVICE_ID_SI_6205 0x0205 +#define PCI_DEVICE_ID_SI_501 0x0406 +#define PCI_DEVICE_ID_SI_496 0x0496 +#define PCI_DEVICE_ID_SI_300 0x0300 +#define PCI_DEVICE_ID_SI_315H 0x0310 +#define PCI_DEVICE_ID_SI_315 0x0315 +#define PCI_DEVICE_ID_SI_315PRO 0x0325 +#define PCI_DEVICE_ID_SI_530 0x0530 +#define PCI_DEVICE_ID_SI_540 0x0540 +#define PCI_DEVICE_ID_SI_550 0x0550 +#define PCI_DEVICE_ID_SI_540_VGA 0x5300 +#define PCI_DEVICE_ID_SI_550_VGA 0x5315 +#define PCI_DEVICE_ID_SI_620 0x0620 +#define PCI_DEVICE_ID_SI_630 0x0630 +#define PCI_DEVICE_ID_SI_633 0x0633 +#define PCI_DEVICE_ID_SI_635 0x0635 +#define PCI_DEVICE_ID_SI_640 0x0640 +#define PCI_DEVICE_ID_SI_645 0x0645 +#define PCI_DEVICE_ID_SI_646 0x0646 +#define PCI_DEVICE_ID_SI_648 0x0648 +#define PCI_DEVICE_ID_SI_650 0x0650 +#define PCI_DEVICE_ID_SI_651 0x0651 +#define PCI_DEVICE_ID_SI_655 0x0655 +#define PCI_DEVICE_ID_SI_661 0x0661 +#define PCI_DEVICE_ID_SI_730 0x0730 +#define PCI_DEVICE_ID_SI_733 0x0733 +#define PCI_DEVICE_ID_SI_630_VGA 0x6300 +#define PCI_DEVICE_ID_SI_735 0x0735 +#define PCI_DEVICE_ID_SI_740 0x0740 +#define PCI_DEVICE_ID_SI_741 0x0741 +#define PCI_DEVICE_ID_SI_745 0x0745 +#define PCI_DEVICE_ID_SI_746 0x0746 +#define PCI_DEVICE_ID_SI_755 0x0755 +#define PCI_DEVICE_ID_SI_760 0x0760 +#define PCI_DEVICE_ID_SI_900 0x0900 +#define PCI_DEVICE_ID_SI_961 0x0961 +#define PCI_DEVICE_ID_SI_962 0x0962 +#define PCI_DEVICE_ID_SI_963 0x0963 +#define PCI_DEVICE_ID_SI_965 0x0965 +#define PCI_DEVICE_ID_SI_966 0x0966 +#define PCI_DEVICE_ID_SI_968 0x0968 +#define PCI_DEVICE_ID_SI_1180 0x1180 +#define PCI_DEVICE_ID_SI_5511 0x5511 +#define PCI_DEVICE_ID_SI_5513 0x5513 +#define PCI_DEVICE_ID_SI_5517 0x5517 +#define PCI_DEVICE_ID_SI_5518 0x5518 +#define PCI_DEVICE_ID_SI_5571 0x5571 +#define PCI_DEVICE_ID_SI_5581 0x5581 +#define PCI_DEVICE_ID_SI_5582 0x5582 +#define PCI_DEVICE_ID_SI_5591 0x5591 +#define PCI_DEVICE_ID_SI_5596 0x5596 +#define PCI_DEVICE_ID_SI_5597 0x5597 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#define PCI_DEVICE_ID_SI_5600 0x5600 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#define PCI_DEVICE_ID_SI_7013 0x7013 +#define PCI_DEVICE_ID_SI_7016 0x7016 +#define PCI_DEVICE_ID_SI_7018 0x7018 + +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a +#define PCI_DEVICE_ID_HP_TACHYON 0x1028 +#define PCI_DEVICE_ID_HP_TACHLITE 0x1029 +#define PCI_DEVICE_ID_HP_J2585A 0x1030 +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#define PCI_DEVICE_ID_HP_J2973A 0x1040 +#define PCI_DEVICE_ID_HP_J2970A 0x1042 +#define PCI_DEVICE_ID_HP_DIVA 0x1048 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A +#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 +#define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b +#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 +#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 +#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 +#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a +#define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e +#define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c +#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 +#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 +#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 +#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a +#define PCI_DEVICE_ID_HP_CISSA 0x3220 +#define PCI_DEVICE_ID_HP_CISSC 0x3230 +#define PCI_DEVICE_ID_HP_CISSD 0x3238 +#define PCI_DEVICE_ID_HP_CISSE 0x323a +#define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031 + +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 +#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001 +#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020 + +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 + +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_DEVICE_ID_DPT 0xa400 + +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_DEVICE_ID_OPTI_82C558 0xc558 +#define PCI_DEVICE_ID_OPTI_82C621 0xc621 +#define PCI_DEVICE_ID_OPTI_82C700 0xc700 +#define PCI_DEVICE_ID_OPTI_82C825 0xd568 + +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 +#define PCI_DEVICE_ID_ELSA_QS3000 0x3000 + +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 +#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130 + +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_DEVICE_ID_TI_TVP4020 0x3d07 +#define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_TSB43AB22 0x8023 +#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 +#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 +#define PCI_DEVICE_ID_TI_X515 0x8036 +#define PCI_DEVICE_ID_TI_XX12 0x8039 +#define PCI_DEVICE_ID_TI_XX12_FM 0x803b +#define PCI_DEVICE_ID_TI_1130 0xac12 +#define PCI_DEVICE_ID_TI_1031 0xac13 +#define PCI_DEVICE_ID_TI_1131 0xac15 +#define PCI_DEVICE_ID_TI_1250 0xac16 +#define PCI_DEVICE_ID_TI_1220 0xac17 +#define PCI_DEVICE_ID_TI_1221 0xac19 +#define PCI_DEVICE_ID_TI_1210 0xac1a +#define PCI_DEVICE_ID_TI_1450 0xac1b +#define PCI_DEVICE_ID_TI_1225 0xac1c +#define PCI_DEVICE_ID_TI_1251A 0xac1d +#define PCI_DEVICE_ID_TI_1211 0xac1e +#define PCI_DEVICE_ID_TI_1251B 0xac1f +#define PCI_DEVICE_ID_TI_4410 0xac41 +#define PCI_DEVICE_ID_TI_4451 0xac42 +#define PCI_DEVICE_ID_TI_4510 0xac44 +#define PCI_DEVICE_ID_TI_4520 0xac46 +#define PCI_DEVICE_ID_TI_7510 0xac47 +#define PCI_DEVICE_ID_TI_7610 0xac48 +#define PCI_DEVICE_ID_TI_7410 0xac49 +#define PCI_DEVICE_ID_TI_1410 0xac50 +#define PCI_DEVICE_ID_TI_1420 0xac51 +#define PCI_DEVICE_ID_TI_1451A 0xac52 +#define PCI_DEVICE_ID_TI_1620 0xac54 +#define PCI_DEVICE_ID_TI_1520 0xac55 +#define PCI_DEVICE_ID_TI_1510 0xac56 +#define PCI_DEVICE_ID_TI_X620 0xac8d +#define PCI_DEVICE_ID_TI_X420 0xac8e +#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f + +#define PCI_VENDOR_ID_SONY 0x104d + +/* Winbond have two vendor IDs! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a +#define PCI_DEVICE_ID_WINBOND2_6692 0x6692 + +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 + +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 +#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802 +#define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 +#define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b +#define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 +#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 + +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_DEVICE_ID_PROMISE_20265 0x0d30 +#define PCI_DEVICE_ID_PROMISE_20267 0x4d30 +#define PCI_DEVICE_ID_PROMISE_20246 0x4d33 +#define PCI_DEVICE_ID_PROMISE_20262 0x4d38 +#define PCI_DEVICE_ID_PROMISE_20263 0x0D38 +#define PCI_DEVICE_ID_PROMISE_20268 0x4d68 +#define PCI_DEVICE_ID_PROMISE_20269 0x4d69 +#define PCI_DEVICE_ID_PROMISE_20270 0x6268 +#define PCI_DEVICE_ID_PROMISE_20271 0x6269 +#define PCI_DEVICE_ID_PROMISE_20275 0x1275 +#define PCI_DEVICE_ID_PROMISE_20276 0x5275 +#define PCI_DEVICE_ID_PROMISE_20277 0x7275 + +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_DEVICE_ID_UMC_UM8673F 0x0101 +#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a +#define PCI_DEVICE_ID_UMC_UM8886A 0x886a + +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002 + +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001 +#define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002 +#define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010 +#define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020 +#define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050 +#define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56 +#define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 +#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e +#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 +#define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 +#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b +#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b +#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c +#define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050 +#define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051 +#define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058 +#define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059 +#define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066 +#define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069 +#define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a +#define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b +#define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 + +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 + +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 +#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 +#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 +#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 +#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 +#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 +#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 +#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 +#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 +#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312 +#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322 +#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312 +#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322 +#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422 +#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432 +#define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512 +#define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522 +#define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422 +#define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432 + +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_DEVICE_ID_CYRIX_5510 0x0000 +#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001 +#define PCI_DEVICE_ID_CYRIX_5520 0x0002 +#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100 +#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102 +#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103 +#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104 + +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693 + +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001 +#define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100 +#define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101 +#define PCI_DEVICE_ID_SUN_RIO_1394 0x1102 +#define PCI_DEVICE_ID_SUN_RIO_USB 0x1103 +#define PCI_DEVICE_ID_SUN_GEM 0x2bad +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_PBM 0x8000 +#define PCI_DEVICE_ID_SUN_SCHIZO 0x8001 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 +#define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001 +#define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801 +#define PCI_DEVICE_ID_SUN_CASSINI 0xabba + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_643 0x0643 +#define PCI_DEVICE_ID_CMD_646 0x0646 +#define PCI_DEVICE_ID_CMD_648 0x0648 +#define PCI_DEVICE_ID_CMD_649 0x0649 + +#define PCI_DEVICE_ID_SII_680 0x0680 +#define PCI_DEVICE_ID_SII_3112 0x3112 +#define PCI_DEVICE_ID_SII_1210SA 0x0240 + +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BROOKTREE_878 0x0878 +#define PCI_DEVICE_ID_BROOKTREE_879 0x0879 + +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_DEVICE_ID_SGI_IOC3 0x0003 +#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 +#define PCI_DEVICE_ID_SGI_IOC4 0x100a + +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_DEVICE_ID_WINBOND_82C105 0x0105 +#define PCI_DEVICE_ID_WINBOND_83C553 0x0565 + +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_DEVICE_ID_PLX_R685 0x1030 +#define PCI_DEVICE_ID_PLX_ROMULUS 0x106a +#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076 +#define PCI_DEVICE_ID_PLX_1077 0x1077 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 +#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 +#define PCI_DEVICE_ID_PLX_R753 0x1152 +#define PCI_DEVICE_ID_PLX_OLITEC 0x1187 +#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 +#define PCI_DEVICE_ID_PLX_9030 0x9030 +#define PCI_DEVICE_ID_PLX_9050 0x9050 +#define PCI_DEVICE_ID_PLX_9080 0x9080 +#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 + +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_DEVICE_ID_MADGE_MK2 0x0002 + +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_DEVICE_ID_3COM_3C985 0x0001 +#define PCI_DEVICE_ID_3COM_3C940 0x1700 +#define PCI_DEVICE_ID_3COM_3C339 0x3390 +#define PCI_DEVICE_ID_3COM_3C359 0x3590 +#define PCI_DEVICE_ID_3COM_3C940B 0x80eb +#define PCI_DEVICE_ID_3COM_3CR990 0x9900 +#define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902 +#define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903 +#define PCI_DEVICE_ID_3COM_3CR990B 0x9904 +#define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905 +#define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908 +#define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909 +#define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a + +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_DEVICE_ID_AL_M1533 0x1533 +#define PCI_DEVICE_ID_AL_M1535 0x1535 +#define PCI_DEVICE_ID_AL_M1541 0x1541 +#define PCI_DEVICE_ID_AL_M1563 0x1563 +#define PCI_DEVICE_ID_AL_M1621 0x1621 +#define PCI_DEVICE_ID_AL_M1631 0x1631 +#define PCI_DEVICE_ID_AL_M1632 0x1632 +#define PCI_DEVICE_ID_AL_M1641 0x1641 +#define PCI_DEVICE_ID_AL_M1644 0x1644 +#define PCI_DEVICE_ID_AL_M1647 0x1647 +#define PCI_DEVICE_ID_AL_M1651 0x1651 +#define PCI_DEVICE_ID_AL_M1671 0x1671 +#define PCI_DEVICE_ID_AL_M1681 0x1681 +#define PCI_DEVICE_ID_AL_M1683 0x1683 +#define PCI_DEVICE_ID_AL_M1689 0x1689 +#define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 +#define PCI_DEVICE_ID_AL_M5229 0x5229 +#define PCI_DEVICE_ID_AL_M5451 0x5451 +#define PCI_DEVICE_ID_AL_M7101 0x7101 + +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016 + +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 + +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 +#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 +#define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029 +#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a +#define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C +#define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036 +#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037 +#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E +#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 +#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 +#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 +#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 +#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 +#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 +#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086 +#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 +#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a +#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099 +#define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 +#define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1 +#define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2 +#define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8 +#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9 +#define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc +#define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce +#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6 +#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6 +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 +#define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 +#define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B +#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0 +#define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4 +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc +#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F +#define PCI_DEVICE_ID_NVIDIA_NVENET_12 0x0268 +#define PCI_DEVICE_ID_NVIDIA_NVENET_13 0x0269 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_NVENET_14 0x0372 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 +#define PCI_DEVICE_ID_NVIDIA_NVENET_16 0x03E5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_17 0x03E6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NVENET_18 0x03EE +#define PCI_DEVICE_ID_NVIDIA_NVENET_19 0x03EF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NVENET_20 0x0450 +#define PCI_DEVICE_ID_NVIDIA_NVENET_21 0x0451 +#define PCI_DEVICE_ID_NVIDIA_NVENET_22 0x0452 +#define PCI_DEVICE_ID_NVIDIA_NVENET_23 0x0453 +#define PCI_DEVICE_ID_NVIDIA_NVENET_24 0x054C +#define PCI_DEVICE_ID_NVIDIA_NVENET_25 0x054D +#define PCI_DEVICE_ID_NVIDIA_NVENET_26 0x054E +#define PCI_DEVICE_ID_NVIDIA_NVENET_27 0x054F +#define PCI_DEVICE_ID_NVIDIA_NVENET_28 0x07DC +#define PCI_DEVICE_ID_NVIDIA_NVENET_29 0x07DD +#define PCI_DEVICE_ID_NVIDIA_NVENET_30 0x07DE +#define PCI_DEVICE_ID_NVIDIA_NVENET_31 0x07DF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 +#define PCI_DEVICE_ID_NVIDIA_NVENET_32 0x0760 +#define PCI_DEVICE_ID_NVIDIA_NVENET_33 0x0761 +#define PCI_DEVICE_ID_NVIDIA_NVENET_34 0x0762 +#define PCI_DEVICE_ID_NVIDIA_NVENET_35 0x0763 +#define PCI_DEVICE_ID_NVIDIA_NVENET_36 0x0AB0 +#define PCI_DEVICE_ID_NVIDIA_NVENET_37 0x0AB1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_38 0x0AB2 +#define PCI_DEVICE_ID_NVIDIA_NVENET_39 0x0AB3 + +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_DEVICE_ID_IMS_TT128 0x9128 +#define PCI_DEVICE_ID_IMS_TT3D 0x9135 + +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_DEVICE_ID_INTERG_1682 0x1682 +#define PCI_DEVICE_ID_INTERG_2000 0x2000 +#define PCI_DEVICE_ID_INTERG_2010 0x2010 +#define PCI_DEVICE_ID_INTERG_5000 0x5000 +#define PCI_DEVICE_ID_INTERG_5050 0x5050 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_DEVICE_ID_RME_DIGI96 0x3fc0 +#define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1 +#define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2 +#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 + +#define PCI_VENDOR_ID_INIT 0x1101 + +#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */ +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 + +#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 + +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_DEVICE_ID_TTI_HPT343 0x0003 +#define PCI_DEVICE_ID_TTI_HPT366 0x0004 +#define PCI_DEVICE_ID_TTI_HPT372 0x0005 +#define PCI_DEVICE_ID_TTI_HPT302 0x0006 +#define PCI_DEVICE_ID_TTI_HPT371 0x0007 +#define PCI_DEVICE_ID_TTI_HPT374 0x0008 +#define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_8763_0 0x0198 +#define PCI_DEVICE_ID_VIA_8380_0 0x0204 +#define PCI_DEVICE_ID_VIA_3238_0 0x0238 +#define PCI_DEVICE_ID_VIA_PT880 0x0258 +#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 +#define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 +#define PCI_DEVICE_ID_VIA_3269_0 0x0269 +#define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 +#define PCI_DEVICE_ID_VIA_3296_0 0x0296 +#define PCI_DEVICE_ID_VIA_8363_0 0x0305 +#define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 +#define PCI_DEVICE_ID_VIA_P4M890 0x0327 +#define PCI_DEVICE_ID_VIA_VT3324 0x0324 +#define PCI_DEVICE_ID_VIA_VT3336 0x0336 +#define PCI_DEVICE_ID_VIA_VT3351 0x0351 +#define PCI_DEVICE_ID_VIA_VT3364 0x0364 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_8501_0 0x0501 +#define PCI_DEVICE_ID_VIA_82C561 0x0561 +#define PCI_DEVICE_ID_VIA_82C586_1 0x0571 +#define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 +#define PCI_DEVICE_ID_VIA_82C596 0x0596 +#define PCI_DEVICE_ID_VIA_82C597_0 0x0597 +#define PCI_DEVICE_ID_VIA_82C598_0 0x0598 +#define PCI_DEVICE_ID_VIA_8601_0 0x0601 +#define PCI_DEVICE_ID_VIA_8605_0 0x0605 +#define PCI_DEVICE_ID_VIA_82C686 0x0686 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_82C576_1 0x1571 +#define PCI_DEVICE_ID_VIA_82C586_2 0x3038 +#define PCI_DEVICE_ID_VIA_82C586_3 0x3040 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#define PCI_DEVICE_ID_VIA_8633_0 0x3091 +#define PCI_DEVICE_ID_VIA_8367_0 0x3099 +#define PCI_DEVICE_ID_VIA_8653_0 0x3101 +#define PCI_DEVICE_ID_VIA_8622 0x3102 +#define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 +#define PCI_DEVICE_ID_VIA_8233C_0 0x3109 +#define PCI_DEVICE_ID_VIA_8361 0x3112 +#define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_612X 0x3119 +#define PCI_DEVICE_ID_VIA_862X_0 0x3123 +#define PCI_DEVICE_ID_VIA_8753_0 0x3128 +#define PCI_DEVICE_ID_VIA_8233A 0x3147 +#define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 +#define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 +#define PCI_DEVICE_ID_VIA_XN266 0x3156 +#define PCI_DEVICE_ID_VIA_6410 0x3164 +#define PCI_DEVICE_ID_VIA_8754C_0 0x3168 +#define PCI_DEVICE_ID_VIA_8235 0x3177 +#define PCI_DEVICE_ID_VIA_8385_0 0x3188 +#define PCI_DEVICE_ID_VIA_8377_0 0x3189 +#define PCI_DEVICE_ID_VIA_8378_0 0x3205 +#define PCI_DEVICE_ID_VIA_8783_0 0x3208 +#define PCI_DEVICE_ID_VIA_8237 0x3227 +#define PCI_DEVICE_ID_VIA_8251 0x3287 +#define PCI_DEVICE_ID_VIA_8237A 0x3337 +#define PCI_DEVICE_ID_VIA_8237S 0x3372 +#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 +#define PCI_DEVICE_ID_VIA_8231 0x8231 +#define PCI_DEVICE_ID_VIA_8231_4 0x8235 +#define PCI_DEVICE_ID_VIA_8365_1 0x8305 +#define PCI_DEVICE_ID_VIA_CX700 0x8324 +#define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581 +#define PCI_DEVICE_ID_VIA_VX800 0x8353 +#define PCI_DEVICE_ID_VIA_8371_1 0x8391 +#define PCI_DEVICE_ID_VIA_82C598_1 0x8598 +#define PCI_DEVICE_ID_VIA_838X_1 0xB188 +#define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198 + +#define PCI_VENDOR_ID_SIEMENS 0x110A +#define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 + +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 +#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 +#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002 +#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003 +#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004 +#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005 +#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006 +#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007 +#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008 +#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009 +#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a +#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b +#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c +#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100 +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101 +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102 +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103 +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104 +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105 + +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000 +#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002 +#define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003 +#define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005 + +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 + +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_DEVICE_ID_FORE_PCA200E 0x0300 + +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146 +#define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730 + +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_DEVICE_ID_EICON_DIVA20 0xe002 +#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 +#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 +#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b +#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 +#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 +#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 +#define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 + +#define PCI_VENDOR_ID_CISCO 0x1137 + +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 + + +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200 +#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300 +#define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320 +#define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400 +#define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500 + +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 +#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 +#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 +#define PCI_DEVICE_ID_NEO_2DB9 0x00C8 +#define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 +#define PCI_DEVICE_ID_NEO_2RJ45 0x00CA +#define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB +#define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4 + +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 +#define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 + +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 +#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 +#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036 +#define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103 +#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 + +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_DEVICE_ID_SBE_WANXL100 0x0301 +#define PCI_DEVICE_ID_SBE_WANXL200 0x0302 +#define PCI_DEVICE_ID_SBE_WANXL400 0x0104 + +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO 0x0102 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0103 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a +#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f +#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617 + +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 +#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 + +#define PCI_VENDOR_ID_ATTO 0x117c + +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 +#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466 +#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 +#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 +#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 +#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 +#define PCI_DEVICE_ID_RICOH_R5C832 0x0832 +#define PCI_DEVICE_ID_RICOH_R5C843 0x0843 + +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 + +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005 +#define PCI_DEVICE_ID_ARTOP_ATP860 0x0006 +#define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007 +#define PCI_DEVICE_ID_ARTOP_ATP865 0x0008 +#define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009 +#define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002 +#define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010 +#define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020 +#define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030 +#define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040 +#define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050 +#define PCI_DEVICE_ID_ARTOP_8060 0x8060 + +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_DEVICE_ID_ZEITNET_1221 0x0001 +#define PCI_DEVICE_ID_ZEITNET_1225 0x0002 + +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_DEVICE_ID_FUJITSU_FS155 0x0001 +#define PCI_DEVICE_ID_FUJITSU_FS50 0x0003 + +#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 +#define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334 + +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_DEVICE_ID_MARVELL_GT64111 0x4146 +#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 +#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 +#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 + +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_DEVICE_ID_V3_V960 0x0001 +#define PCI_DEVICE_ID_V3_V351 0x0002 + +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 + +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 +#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 + +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_DEVICE_ID_AD1889JS 0x1889 + +#define PCI_DEVICE_ID_SEGA_BBA 0x1234 + +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_DEVICE_ID_ZORAN_36057 0x6057 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 + +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 + +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_DEVICE_ID_RP32INTF 0x0001 +#define PCI_DEVICE_ID_RP8INTF 0x0002 +#define PCI_DEVICE_ID_RP16INTF 0x0003 +#define PCI_DEVICE_ID_RP4QUAD 0x0004 +#define PCI_DEVICE_ID_RP8OCTA 0x0005 +#define PCI_DEVICE_ID_RP8J 0x0006 +#define PCI_DEVICE_ID_RP4J 0x0007 +#define PCI_DEVICE_ID_RP8SNI 0x0008 +#define PCI_DEVICE_ID_RP16SNI 0x0009 +#define PCI_DEVICE_ID_RPP4 0x000A +#define PCI_DEVICE_ID_RPP8 0x000B +#define PCI_DEVICE_ID_RP4M 0x000D +#define PCI_DEVICE_ID_RP2_232 0x000E +#define PCI_DEVICE_ID_RP2_422 0x000F +#define PCI_DEVICE_ID_URP32INTF 0x0801 +#define PCI_DEVICE_ID_URP8INTF 0x0802 +#define PCI_DEVICE_ID_URP16INTF 0x0803 +#define PCI_DEVICE_ID_URP8OCTA 0x0805 +#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C +#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D +#define PCI_DEVICE_ID_CRP16INTF 0x0903 + +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100 +#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101 +#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102 +#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103 +#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104 +#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105 +#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 +#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_DEVICE_ID_PC300_RX_2 0x0300 +#define PCI_DEVICE_ID_PC300_RX_1 0x0301 +#define PCI_DEVICE_ID_PC300_TE_2 0x0310 +#define PCI_DEVICE_ID_PC300_TE_1 0x0311 +#define PCI_DEVICE_ID_PC300_TE_M_2 0x0320 +#define PCI_DEVICE_ID_PC300_TE_M_1 0x0321 + +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001 + +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_DEVICE_ID_O2_6729 0x6729 +#define PCI_DEVICE_ID_O2_6730 0x673a +#define PCI_DEVICE_ID_O2_6832 0x6832 +#define PCI_DEVICE_ID_O2_6836 0x6836 + +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 +#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002 +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 +#define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009 + +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_DEVICE_ID_AVM_B1 0x0700 +#define PCI_DEVICE_ID_AVM_C4 0x0800 +#define PCI_DEVICE_ID_AVM_A1 0x0a00 +#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 +#define PCI_DEVICE_ID_AVM_C2 0x1100 +#define PCI_DEVICE_ID_AVM_T1 0x1200 + +#define PCI_VENDOR_ID_STALLION 0x124d + +/* Allied Telesyn */ +#define PCI_VENDOR_ID_AT 0x1259 +#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 +#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 + +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b + +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 + +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_DEVICE_ID_EFFICEON 0x0060 + +#define PCI_VENDOR_ID_ROCKWELL 0x127A + +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_DEVICE_ID_ITE_8211 0x8211 +#define PCI_DEVICE_ID_ITE_8212 0x8212 +#define PCI_DEVICE_ID_ITE_8213 0x8213 +#define PCI_DEVICE_ID_ITE_8152 0x8152 +#define PCI_DEVICE_ID_ITE_8872 0x8872 +#define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 + +/* formerly Platform Tech */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 + +#define PCI_VENDOR_ID_ALTEON 0x12ae + +#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D +#define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332 + +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 + +#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041 +#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001 +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010 + +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 +#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 +#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003 + +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_DEVICE_ID_LML_33R10 0x8a02 + +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111 + +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_SUBVENDOR_ID_SIIG 0x131f +#define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000 +#define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001 +#define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012 +#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020 +#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021 +#define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030 +#define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031 +#define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036 +#define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050 +#define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051 +#define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052 +#define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000 +#define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001 +#define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002 +#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020 +#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021 +#define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030 +#define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031 +#define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012 +#define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050 +#define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051 +#define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062 +#define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080 +#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 +#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 +#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 + +#define PCI_VENDOR_ID_RADISYS 0x1331 + +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415 +#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425 +#define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155 + +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 + +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80 +#define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0 + +#define PCI_VENDOR_ID_QUATECH 0x135C +#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010 +#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020 +#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050 +#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060 +#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278 + +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_DEVICE_ID_SEALEVEL_U530 0x7101 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 +#define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 +#define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 + +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050 +#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104 +#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106 +#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107 +#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108 + +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 + +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_DEVICE_ID_CNET_GIGACARD 0x434e + +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_DEVICE_ID_LMC_HSSI 0x0003 +#define PCI_DEVICE_ID_LMC_DS3 0x0004 +#define PCI_DEVICE_ID_LMC_SSI 0x0005 +#define PCI_DEVICE_ID_LMC_T1 0x0006 + +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a + +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 +#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 +#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 + +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 +#define PCI_DEVICE_ID_MOXA_CP102 0x1020 +#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 +#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 +#define PCI_DEVICE_ID_MOXA_C104 0x1040 +#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 +#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 +#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 +#define PCI_DEVICE_ID_MOXA_CT114 0x1140 +#define PCI_DEVICE_ID_MOXA_CP114 0x1141 +#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 +#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 +#define PCI_DEVICE_ID_MOXA_CP132 0x1320 +#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 +#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 +#define PCI_DEVICE_ID_MOXA_C168 0x1680 +#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 +#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 +#define PCI_DEVICE_ID_MOXA_CP204J 0x2040 +#define PCI_DEVICE_ID_MOXA_C218 0x2180 +#define PCI_DEVICE_ID_MOXA_C320 0x3200 + +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 +#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 +#define PCI_DEVICE_ID_CCD_B000 0xb000 +#define PCI_DEVICE_ID_CCD_B006 0xb006 +#define PCI_DEVICE_ID_CCD_B007 0xb007 +#define PCI_DEVICE_ID_CCD_B008 0xb008 +#define PCI_DEVICE_ID_CCD_B009 0xb009 +#define PCI_DEVICE_ID_CCD_B00A 0xb00a +#define PCI_DEVICE_ID_CCD_B00B 0xb00b +#define PCI_DEVICE_ID_CCD_B00C 0xb00c +#define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 +#define PCI_DEVICE_ID_CCD_B700 0xb700 +#define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 + +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 +#define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 +#define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 + +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_DEVICE_ID_MICROGATE_USC 0x0010 +#define PCI_DEVICE_ID_MICROGATE_SCA 0x0030 + +#define PCI_VENDOR_ID_3WARE 0x13C1 +#define PCI_DEVICE_ID_3WARE_1000 0x1000 +#define PCI_DEVICE_ID_3WARE_7000 0x1001 +#define PCI_DEVICE_ID_3WARE_9000 0x1002 + +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231 + +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 + +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 + +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 + +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */ +#define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */ +#define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */ +#define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */ +#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000 +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */ +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */ +#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800 + +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_DEVICE_ID_TIMEDIA_1889 0x7168 + +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#define PCI_DEVICE_ID_VT1724 0x1724 + +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 +#define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000 +#define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C +#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 +#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 +#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 +#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 +#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523 + +#define PCI_VENDOR_ID_CHELSIO 0x1425 + +#define PCI_VENDOR_ID_SAMSUNG 0x144d + +#define PCI_VENDOR_ID_MYRICOM 0x14c1 + +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_DEVICE_ID_TITAN_010L 0x8001 +#define PCI_DEVICE_ID_TITAN_100L 0x8010 +#define PCI_DEVICE_ID_TITAN_110L 0x8011 +#define PCI_DEVICE_ID_TITAN_200L 0x8020 +#define PCI_DEVICE_ID_TITAN_210L 0x8021 +#define PCI_DEVICE_ID_TITAN_400L 0x8040 +#define PCI_DEVICE_ID_TITAN_800L 0x8080 +#define PCI_DEVICE_ID_TITAN_100 0xA001 +#define PCI_DEVICE_ID_TITAN_200 0xA005 +#define PCI_DEVICE_ID_TITAN_400 0xA003 +#define PCI_DEVICE_ID_TITAN_800B 0xA004 + +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400 +#define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402 + +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_DEVICE_ID_SP1011 0x0010 + +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 +#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 +#define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150 + +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_TIGON3_5752 0x1600 +#define PCI_DEVICE_ID_TIGON3_5752M 0x1601 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_TIGON3_5700 0x1644 +#define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 +#define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5704 0x1648 +#define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649 +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_DEVICE_ID_TIGON3_5705 0x1653 +#define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 +#define PCI_DEVICE_ID_TIGON3_5720 0x1658 +#define PCI_DEVICE_ID_TIGON3_5721 0x1659 +#define PCI_DEVICE_ID_TIGON3_5722 0x165a +#define PCI_DEVICE_ID_TIGON3_5723 0x165b +#define PCI_DEVICE_ID_TIGON3_5705M 0x165d +#define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e +#define PCI_DEVICE_ID_TIGON3_5714 0x1668 +#define PCI_DEVICE_ID_TIGON3_5714S 0x1669 +#define PCI_DEVICE_ID_TIGON3_5780 0x166a +#define PCI_DEVICE_ID_TIGON3_5780S 0x166b +#define PCI_DEVICE_ID_TIGON3_5705F 0x166e +#define PCI_DEVICE_ID_TIGON3_5754M 0x1672 +#define PCI_DEVICE_ID_TIGON3_5755M 0x1673 +#define PCI_DEVICE_ID_TIGON3_5756 0x1674 +#define PCI_DEVICE_ID_TIGON3_5750 0x1676 +#define PCI_DEVICE_ID_TIGON3_5751 0x1677 +#define PCI_DEVICE_ID_TIGON3_5715 0x1678 +#define PCI_DEVICE_ID_TIGON3_5715S 0x1679 +#define PCI_DEVICE_ID_TIGON3_5754 0x167a +#define PCI_DEVICE_ID_TIGON3_5755 0x167b +#define PCI_DEVICE_ID_TIGON3_5750M 0x167c +#define PCI_DEVICE_ID_TIGON3_5751M 0x167d +#define PCI_DEVICE_ID_TIGON3_5751F 0x167e +#define PCI_DEVICE_ID_TIGON3_5787F 0x167f +#define PCI_DEVICE_ID_TIGON3_5761E 0x1680 +#define PCI_DEVICE_ID_TIGON3_5761 0x1681 +#define PCI_DEVICE_ID_TIGON3_5764 0x1684 +#define PCI_DEVICE_ID_TIGON3_5787M 0x1693 +#define PCI_DEVICE_ID_TIGON3_5782 0x1696 +#define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5785 0x1699 +#define PCI_DEVICE_ID_TIGON3_5786 0x169a +#define PCI_DEVICE_ID_TIGON3_5787 0x169b +#define PCI_DEVICE_ID_TIGON3_5788 0x169c +#define PCI_DEVICE_ID_TIGON3_5789 0x169d +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 +#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_5708S 0x16ac +#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 +#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 +#define PCI_DEVICE_ID_TIGON3_5781 0x16dd +#define PCI_DEVICE_ID_TIGON3_5753 0x16f7 +#define PCI_DEVICE_ID_TIGON3_5753M 0x16fd +#define PCI_DEVICE_ID_TIGON3_5753F 0x16fe +#define PCI_DEVICE_ID_TIGON3_5901 0x170d +#define PCI_DEVICE_ID_BCM4401B1 0x170c +#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e +#define PCI_DEVICE_ID_TIGON3_5906 0x1712 +#define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_DEVICE_ID_BCM4401 0x4401 +#define PCI_DEVICE_ID_BCM4401B0 0x4402 + +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_DEVICE_ID_TOPIC_TP560 0x0000 + +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100 +#define PCI_VENDOR_ID_ENE 0x1524 +#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 +#define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551 +#define PCI_DEVICE_ID_ENE_CB714_SD 0x0750 +#define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751 +#define PCI_DEVICE_ID_ENE_1211 0x1211 +#define PCI_DEVICE_ID_ENE_1225 0x1225 +#define PCI_DEVICE_ID_ENE_1410 0x1410 +#define PCI_DEVICE_ID_ENE_710 0x1411 +#define PCI_DEVICE_ID_ENE_712 0x1412 +#define PCI_DEVICE_ID_ENE_1420 0x1420 +#define PCI_DEVICE_ID_ENE_720 0x1421 +#define PCI_DEVICE_ID_ENE_722 0x1422 + +#define PCI_SUBVENDOR_ID_PERLE 0x155f +#define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001 +#define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010 + +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 +#define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783 + +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000 + +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0 + +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 +#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 +#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 +#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 +#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c +#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 + +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 + +/* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ +#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 +#define PCI_VENDOR_ID_ADDIDATA 0x15B8 +#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 +#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 +#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E +#define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F + +#define PCI_VENDOR_ID_PDC 0x15e9 + +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_DEVICE_ID_FARSITE_T2P 0x0400 +#define PCI_DEVICE_ID_FARSITE_T4P 0x0440 +#define PCI_DEVICE_ID_FARSITE_T1U 0x0610 +#define PCI_DEVICE_ID_FARSITE_T2U 0x0620 +#define PCI_DEVICE_ID_FARSITE_T4U 0x0640 +#define PCI_DEVICE_ID_FARSITE_TE1 0x1610 +#define PCI_DEVICE_ID_FARSITE_TE1C 0x1612 + +#define PCI_VENDOR_ID_ARIMA 0x161f + +#define PCI_VENDOR_ID_BROCADE 0x1657 + +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_DEVICE_ID_BCM1250_PCI 0x0001 +#define PCI_DEVICE_ID_BCM1250_HT 0x0002 + +#define PCI_VENDOR_ID_ATHEROS 0x168c + +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_DEVICE_ID_REVOLUTION 0x0044 + +#define PCI_VENDOR_ID_CENATEK 0x16CA +#define PCI_DEVICE_ID_CENATEK_IDE 0x0001 + +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 + +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 + +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 +#define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9 +#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea +#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb + +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f + +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_DEVICE_ID_RDC_R6020 0x6020 +#define PCI_DEVICE_ID_RDC_R6030 0x6030 +#define PCI_DEVICE_ID_RDC_R6040 0x6040 +#define PCI_DEVICE_ID_RDC_R6060 0x6060 +#define PCI_DEVICE_ID_RDC_R6061 0x6061 + +#define PCI_VENDOR_ID_LENOVO 0x17aa + +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_DEVICE_ID_ARECA_1110 0x1110 +#define PCI_DEVICE_ID_ARECA_1120 0x1120 +#define PCI_DEVICE_ID_ARECA_1130 0x1130 +#define PCI_DEVICE_ID_ARECA_1160 0x1160 +#define PCI_DEVICE_ID_ARECA_1170 0x1170 +#define PCI_DEVICE_ID_ARECA_1200 0x1200 +#define PCI_DEVICE_ID_ARECA_1201 0x1201 +#define PCI_DEVICE_ID_ARECA_1202 0x1202 +#define PCI_DEVICE_ID_ARECA_1210 0x1210 +#define PCI_DEVICE_ID_ARECA_1220 0x1220 +#define PCI_DEVICE_ID_ARECA_1230 0x1230 +#define PCI_DEVICE_ID_ARECA_1260 0x1260 +#define PCI_DEVICE_ID_ARECA_1270 0x1270 +#define PCI_DEVICE_ID_ARECA_1280 0x1280 +#define PCI_DEVICE_ID_ARECA_1380 0x1380 +#define PCI_DEVICE_ID_ARECA_1381 0x1381 +#define PCI_DEVICE_ID_ARECA_1680 0x1680 +#define PCI_DEVICE_ID_ARECA_1681 0x1681 + +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_DEVICE_ID_S2IO_WIN 0x5731 +#define PCI_DEVICE_ID_S2IO_UNI 0x5831 +#define PCI_DEVICE_ID_HERC_WIN 0x5732 +#define PCI_DEVICE_ID_HERC_UNI 0x5832 + +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 + +#define PCI_VENDOR_ID_TOPSPIN 0x1867 + +#define PCI_VENDOR_ID_TDI 0x192E +#define PCI_DEVICE_ID_TDI_EHCI 0x0101 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8548E 0x0012 +#define PCI_DEVICE_ID_MPC8548 0x0013 +#define PCI_DEVICE_ID_MPC8543E 0x0014 +#define PCI_DEVICE_ID_MPC8543 0x0015 +#define PCI_DEVICE_ID_MPC8547E 0x0018 +#define PCI_DEVICE_ID_MPC8545E 0x0019 +#define PCI_DEVICE_ID_MPC8545 0x001a +#define PCI_DEVICE_ID_MPC8568E 0x0020 +#define PCI_DEVICE_ID_MPC8568 0x0021 +#define PCI_DEVICE_ID_MPC8567E 0x0022 +#define PCI_DEVICE_ID_MPC8567 0x0023 +#define PCI_DEVICE_ID_MPC8533E 0x0030 +#define PCI_DEVICE_ID_MPC8533 0x0031 +#define PCI_DEVICE_ID_MPC8544E 0x0032 +#define PCI_DEVICE_ID_MPC8544 0x0033 +#define PCI_DEVICE_ID_MPC8572E 0x0040 +#define PCI_DEVICE_ID_MPC8572 0x0041 +#define PCI_DEVICE_ID_MPC8536E 0x0050 +#define PCI_DEVICE_ID_MPC8536 0x0051 +#define PCI_DEVICE_ID_MPC8641 0x7010 +#define PCI_DEVICE_ID_MPC8641D 0x7011 +#define PCI_DEVICE_ID_MPC8610 0x7018 + +#define PCI_VENDOR_ID_PASEMI 0x1959 + +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 + +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 +#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 + +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 +#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff + +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 + +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_DEVICE_ID_TEHUTI_3009 0x3009 +#define PCI_DEVICE_ID_TEHUTI_3010 0x3010 +#define PCI_DEVICE_ID_TEHUTI_3014 0x3014 + +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 + +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 + +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 +#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 +#define PCI_DEVICE_ID_NX2031_4GCU 0x0003 +#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 +#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 +#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 +#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 +#define PCI_DEVICE_ID_NX3031 0x0100 + +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 + +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_TRIO 0x8811 +#define PCI_DEVICE_ID_S3_868 0x8880 +#define PCI_DEVICE_ID_S3_968 0x88f0 +#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 +#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 + +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_DEVICE_ID_DUNORD_I3000 0x0001 + +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 +#define PCI_DEVICE_ID_DCI_PCCOM8 0x0002 +#define PCI_DEVICE_ID_DCI_PCCOM2 0x0004 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_EESSC 0x0008 +#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320 +#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321 +#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329 +#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A +#define PCI_DEVICE_ID_INTEL_PXHV 0x032C +#define PCI_DEVICE_ID_INTEL_82375 0x0482 +#define PCI_DEVICE_ID_INTEL_82424 0x0483 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_I960 0x0960 +#define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 +#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 +#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 +#define PCI_DEVICE_ID_INTEL_7505_0 0x2550 +#define PCI_DEVICE_ID_INTEL_7205_0 0x255d +#define PCI_DEVICE_ID_INTEL_82437 0x122d +#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e +#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230 +#define PCI_DEVICE_ID_INTEL_82371MX 0x1234 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82380FB 0x124b +#define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 +#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 +#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 +#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 +#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 +#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 +#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 +#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 +#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 +#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 +#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 +#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445 +#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448 +#define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a +#define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b +#define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801E_0 0x2450 +#define PCI_DEVICE_ID_INTEL_82801E_11 0x245b +#define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480 +#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483 +#define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485 +#define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486 +#define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b +#define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c +#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 +#define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 +#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 +#define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 +#define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 +#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9 +#define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca +#define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb +#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc +#define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0 +#define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1 +#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3 +#define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 +#define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 +#define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db +#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc +#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd +#define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 +#define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 +#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4 +#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 +#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 +#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530 +#define PCI_DEVICE_ID_INTEL_82860_HB 0x2531 +#define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c +#define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560 +#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562 +#define PCI_DEVICE_ID_INTEL_82865_HB 0x2570 +#define PCI_DEVICE_ID_INTEL_82865_IG 0x2572 +#define PCI_DEVICE_ID_INTEL_82875_HB 0x2578 +#define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580 +#define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582 +#define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590 +#define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592 +#define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0 +#define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5 +#define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6 +#define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770 +#define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772 +#define PCI_DEVICE_ID_INTEL_3000_HB 0x2778 +#define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0 +#define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2 +#define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640 +#define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641 +#define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642 +#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a +#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d +#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e +#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f +#define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670 +#define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698 +#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b +#define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e +#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8 +#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9 +#define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0 +#define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd +#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da +#define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd +#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de +#define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df +#define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 +#define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 +#define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 +#define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814 +#define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815 +#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e +#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 +#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 +#define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 +#define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 +#define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 +#define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582 +#define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590 +#define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592 +#define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595 +#define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596 +#define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597 +#define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598 +#define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599 +#define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a +#define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e +#define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b +#define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c +#define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14 +#define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16 +#define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18 +#define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a +#define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 +#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MIN 0x3b00 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX 0x3b1f +#define PCI_DEVICE_ID_INTEL_PCH_SMBUS 0x3b30 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 +#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 +#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 +#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 +#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff +#define PCI_DEVICE_ID_INTEL_TOLAPAI_0 0x5031 +#define PCI_DEVICE_ID_INTEL_TOLAPAI_1 0x5032 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82437VX 0x7030 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124 +#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125 +#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 +#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 +#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 +#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 +#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196 +#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 +#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2 +#define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 +#define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 +#define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a +#define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 +#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca +#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb +#define PCI_DEVICE_ID_INTEL_84460GX 0x84ea +#define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 +#define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 +#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 + +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 + +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 +#define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 +#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e +#define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003 + +#define PCI_VENDOR_ID_KTI 0x8e2e + +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 +#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 +#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860 +#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 +#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 +#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 +#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 +#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 +#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 +#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 +#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 +#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378 +#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478 +#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895 +#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078 +#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178 +#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 +#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 +#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 +#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 +#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 +#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 +#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 + +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 +#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 +#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f +#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 +#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 +#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f +#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 +#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 +#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 +#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f +#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 +#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 +#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 +#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf +#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500 +#define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503 + +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_DEVICE_ID_HOLTEK_6565 0x6565 + +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_DEVICE_ID_NETMOS_9705 0x9705 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 +#define PCI_DEVICE_ID_NETMOS_9735 0x9735 +#define PCI_DEVICE_ID_NETMOS_9745 0x9745 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 +#define PCI_DEVICE_ID_NETMOS_9805 0x9805 +#define PCI_DEVICE_ID_NETMOS_9815 0x9815 +#define PCI_DEVICE_ID_NETMOS_9835 0x9835 +#define PCI_DEVICE_ID_NETMOS_9845 0x9845 +#define PCI_DEVICE_ID_NETMOS_9855 0x9855 + +#define PCI_VENDOR_ID_3COM_2 0xa727 + +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + +#define PCI_SUBVENDOR_ID_EXSYS 0xd84d +#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 +#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 + +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_DEVICE_ID_TIGERJET_300 0x0001 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 + +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_DEVICE_ID_RME_DIGI32 0x9896 +#define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897 +#define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 + +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 + +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 diff --git a/qemu/roms/seabios/src/hw/pci_regs.h b/qemu/roms/seabios/src/hw/pci_regs.h new file mode 100644 index 000000000..e5effd47e --- /dev/null +++ b/qemu/roms/seabios/src/hw/pci_regs.h @@ -0,0 +1,556 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares <mj@ucw.cz> + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ + +/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_ASPMS 0xc00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x7000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x38000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x40000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_RL 0x20 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x40 /* Common Clock COnfiguration */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_ARI 14 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_COR_SRC 52 +#define PCI_ERR_ROOT_SRC 54 + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +#endif /* LINUX_PCI_REGS_H */ diff --git a/qemu/roms/seabios/src/hw/pic.c b/qemu/roms/seabios/src/hw/pic.c new file mode 100644 index 000000000..6ff696765 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pic.c @@ -0,0 +1,101 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_IVT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "pic.h" // pic_* + +u16 +pic_irqmask_read(void) +{ + return inb(PORT_PIC1_DATA) | (inb(PORT_PIC2_DATA) << 8); +} + +void +pic_irqmask_write(u16 mask) +{ + outb(mask, PORT_PIC1_DATA); + outb(mask >> 8, PORT_PIC2_DATA); +} + +void +pic_irqmask_mask(u16 off, u16 on) +{ + u8 pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; + outb((inb(PORT_PIC1_DATA) & ~pic1off) | pic1on, PORT_PIC1_DATA); + outb((inb(PORT_PIC2_DATA) & ~pic2off) | pic2on, PORT_PIC2_DATA); +} + +void +pic_reset(u8 irq0, u8 irq8) +{ + // Send ICW1 (select OCW1 + will send ICW4) + outb(0x11, PORT_PIC1_CMD); + outb(0x11, PORT_PIC2_CMD); + // Send ICW2 (base irqs: 0x08-0x0f for irq0-7, 0x70-0x77 for irq8-15) + outb(irq0, PORT_PIC1_DATA); + outb(irq8, PORT_PIC2_DATA); + // Send ICW3 (cascaded pic ids) + outb(0x04, PORT_PIC1_DATA); + outb(0x02, PORT_PIC2_DATA); + // Send ICW4 (enable 8086 mode) + outb(0x01, PORT_PIC1_DATA); + outb(0x01, PORT_PIC2_DATA); + // Mask all irqs (except cascaded PIC2 irq) + pic_irqmask_write(PIC_IRQMASK_DEFAULT); +} + +void +pic_setup(void) +{ + dprintf(3, "init pic\n"); + pic_reset(BIOS_HWIRQ0_VECTOR, BIOS_HWIRQ8_VECTOR); +} + +void +enable_hwirq(int hwirq, struct segoff_s func) +{ + pic_irqmask_mask(1 << hwirq, 0); + int vector; + if (hwirq < 8) + vector = BIOS_HWIRQ0_VECTOR + hwirq; + else + vector = BIOS_HWIRQ8_VECTOR + hwirq - 8; + SET_IVT(vector, func); +} + +static u8 +pic_isr1_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC1_CMD); + return inb(PORT_PIC1_CMD); +} + +static u8 +pic_isr2_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC2_CMD); + return inb(PORT_PIC2_CMD); +} + +// Handler for otherwise unused hardware irqs. +void VISIBLE16 +handle_hwpic1(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic1, "handle_hwpic1 irq=%x\n", pic_isr1_read()); + pic_eoi1(); +} + +void VISIBLE16 +handle_hwpic2(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic2, "handle_hwpic2 irq=%x\n", pic_isr2_read()); + pic_eoi2(); +} diff --git a/qemu/roms/seabios/src/hw/pic.h b/qemu/roms/seabios/src/hw/pic.h new file mode 100644 index 000000000..6947b6e81 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pic.h @@ -0,0 +1,56 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __PIC_H +#define __PIC_H + +#include "x86.h" // outb + +#define PORT_PIC1_CMD 0x0020 +#define PORT_PIC1_DATA 0x0021 +#define PORT_PIC2_CMD 0x00a0 +#define PORT_PIC2_DATA 0x00a1 + +// PORT_PIC1 bitdefs +#define PIC1_IRQ0 (1<<0) +#define PIC1_IRQ1 (1<<1) +#define PIC1_IRQ2 (1<<2) +#define PIC1_IRQ5 (1<<5) +#define PIC1_IRQ6 (1<<6) +// PORT_PIC2 bitdefs +#define PIC2_IRQ8 (1<<8) +#define PIC2_IRQ12 (1<<12) +#define PIC2_IRQ13 (1<<13) +#define PIC2_IRQ14 (1<<14) + +#define PIC_IRQMASK_DEFAULT ((u16)~PIC1_IRQ2) + +#define BIOS_HWIRQ0_VECTOR 0x08 +#define BIOS_HWIRQ8_VECTOR 0x70 + +static inline void +pic_eoi1(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC1_CMD); +} + +static inline void +pic_eoi2(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC2_CMD); + pic_eoi1(); +} + +u16 pic_irqmask_read(void); +void pic_irqmask_write(u16 mask); +void pic_irqmask_mask(u16 off, u16 on); +void pic_reset(u8 irq0, u8 irq8); +void pic_setup(void); +void enable_hwirq(int hwirq, struct segoff_s func); + +#endif // pic.h diff --git a/qemu/roms/seabios/src/hw/ps2port.c b/qemu/roms/seabios/src/hw/ps2port.c new file mode 100644 index 000000000..04995c881 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ps2port.c @@ -0,0 +1,507 @@ +// Support for handling the PS/2 mouse/keyboard ports. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "output.h" // dprintf +#include "pic.h" // pic_eoi1 +#include "ps2port.h" // ps2_kbd_command +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // udelay +#include "x86.h" // inb + + +/**************************************************************** + * Low level i8042 commands. + ****************************************************************/ + +// Timeout value. +#define I8042_CTL_TIMEOUT 10000 + +#define I8042_BUFFER_SIZE 16 + +static int +i8042_wait_read(void) +{ + dprintf(7, "i8042_wait_read\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_wait_write(void) +{ + dprintf(7, "i8042_wait_write\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_IBF)) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_flush(void) +{ + dprintf(7, "i8042_flush\n"); + int i; + for (i=0; i<I8042_BUFFER_SIZE; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_OBF)) + return 0; + udelay(50); + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "i8042 flushed %x (status=%x)\n", data, status); + } + + warn_timeout(); + return -1; +} + +static int +__i8042_command(int command, u8 *param) +{ + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Send the command. + int ret = i8042_wait_write(); + if (ret) + return ret; + outb(command, PORT_PS2_STATUS); + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = i8042_wait_write(); + if (ret) + return ret; + outb(param[i], PORT_PS2_DATA); + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = i8042_wait_read(); + if (ret) + return ret; + param[i] = inb(PORT_PS2_DATA); + dprintf(7, "i8042 param=%x\n", param[i]); + } + + return 0; +} + +static int +i8042_command(int command, u8 *param) +{ + dprintf(7, "i8042_command cmd=%x\n", command); + int ret = __i8042_command(command, param); + if (ret) + dprintf(2, "i8042 command %x failed\n", command); + return ret; +} + +static int +i8042_kbd_write(u8 c) +{ + dprintf(7, "i8042_kbd_write c=%d\n", c); + int ret = i8042_wait_write(); + if (! ret) + outb(c, PORT_PS2_DATA); + return ret; +} + +static int +i8042_aux_write(u8 c) +{ + return i8042_command(I8042_CMD_AUX_SEND, &c); +} + +void +i8042_reboot(void) +{ + if (! CONFIG_PS2PORT) + return; + int i; + for (i=0; i<10; i++) { + i8042_wait_write(); + udelay(50); + outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */ + udelay(50); + } +} + + +/**************************************************************** + * Device commands. + ****************************************************************/ + +#define PS2_RET_ACK 0xfa +#define PS2_RET_NAK 0xfe + +static int +ps2_recvbyte(int aux, int needack, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) { + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "ps2 read %x\n", data); + + if (!!(status & I8042_STR_AUXDATA) == aux) { + if (!needack) + return data; + if (data == PS2_RET_ACK) + return data; + if (data == PS2_RET_NAK) { + dprintf(1, "Got ps2 nak (status=%x)\n", status); + return data; + } + } + + // This data not part of command - just discard it. + dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); + } + + if (timer_check(end)) { + // Don't warn on second byte of a reset + if (timeout > 100) + warn_timeout(); + return -1; + } + yield(); + } +} + +static int +ps2_sendbyte(int aux, u8 command, int timeout) +{ + dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command); + int ret; + if (aux) + ret = i8042_aux_write(command); + else + ret = i8042_kbd_write(command); + if (ret) + return ret; + + // Read ack. + ret = ps2_recvbyte(aux, 1, timeout); + if (ret < 0) + return ret; + if (ret != PS2_RET_ACK) + return -1; + + return 0; +} + +u8 Ps2ctr VARLOW; + +static int +__ps2_command(int aux, int command, u8 *param) +{ + int ret2; + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Disable interrupts and keyboard/mouse. + u8 ps2ctr = GET_LOW(Ps2ctr); + u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS) + & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT)); + dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr); + int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + return ret; + + // Flush any interrupts already pending. + yield(); + + // Enable port command is being sent to. + if (aux) + newctr &= ~I8042_CTR_AUXDIS; + else + newctr &= ~I8042_CTR_KBDDIS; + ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + goto fail; + + if (command == ATKBD_CMD_RESET_BAT) { + // Reset is special wrt timeouts and bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 1000); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 4000); + if (ret < 0) + goto fail; + param[0] = ret; + ret = ps2_recvbyte(aux, 0, 100); + if (ret < 0) + // Some devices only respond with one byte on reset. + ret = 0; + param[1] = ret; + } else if (command == ATKBD_CMD_GETID) { + // Getid is special wrt bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[0] = ret; + if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d + || ret == 0x60 || ret == 0x47) { + // These ids (keyboards) return two bytes. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[1] = ret; + } else { + param[1] = 0; + } + } else { + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = ps2_sendbyte(aux, param[i], 200); + if (ret) + goto fail; + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[i] = ret; + } + } + + ret = 0; + +fail: + // Restore interrupts and keyboard/mouse. + ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr); + if (ret2) + return ret2; + + return ret; +} + +static int +ps2_command(int aux, int command, u8 *param) +{ + dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command); + int ret = __ps2_command(aux, command, param); + if (ret) + dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux); + return ret; +} + +int +ps2_kbd_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + return ps2_command(0, command, param); +} + +int +ps2_mouse_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + + // Update ps2ctr for mouse enable/disable. + if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) { + u8 ps2ctr = GET_LOW(Ps2ctr); + if (command == PSMOUSE_CMD_ENABLE) + ps2ctr = (ps2ctr | I8042_CTR_AUXINT) & ~I8042_CTR_AUXDIS; + else + ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT; + SET_LOW(Ps2ctr, ps2ctr); + } + + return ps2_command(1, command, param); +} + + +/**************************************************************** + * IRQ handlers + ****************************************************************/ + +// INT74h : PS/2 mouse hardware interrupt +void VISIBLE16 +handle_74(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_74); + + u8 v = inb(PORT_PS2_STATUS); + if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) + != (I8042_STR_OBF|I8042_STR_AUXDATA)) { + dprintf(1, "ps2 mouse irq but no mouse data.\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_AUXINT)) + // Interrupts not enabled. + goto done; + + process_mouse(v); + +done: + pic_eoi2(); +} + +// INT09h : Keyboard Hardware Service Entry Point +void VISIBLE16 +handle_09(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_09); + + // read key from keyboard controller + u8 v = inb(PORT_PS2_STATUS); + if (v & I8042_STR_AUXDATA) { + dprintf(1, "ps2 keyboard irq but found mouse data?!\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_KBDINT)) + // Interrupts not enabled. + goto done; + + process_key(v); + + // Some old programs expect ISR to turn keyboard back on. + i8042_command(I8042_CMD_KBD_ENABLE, NULL); + +done: + pic_eoi1(); +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +ps2_keyboard_setup(void *data) +{ + /* flush incoming keys */ + int ret = i8042_flush(); + if (ret) + return; + + // Controller self-test. + u8 param[2]; + ret = i8042_command(I8042_CMD_CTL_TEST, param); + if (ret) + return; + if (param[0] != 0x55) { + dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]); + return; + } + + // Controller keyboard test. + ret = i8042_command(I8042_CMD_KBD_TEST, param); + if (ret) + return; + if (param[0] != 0x00) { + dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]); + return; + } + + // Disable keyboard and mouse events. + SET_LOW(Ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS); + + + /* ------------------- keyboard side ------------------------*/ + /* reset keyboard and self test (keyboard side) */ + int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0); + u32 end = timer_calc(spinupdelay); + for (;;) { + ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); + if (!ret) + break; + if (timer_check(end)) { + if (spinupdelay) + warn_timeout(); + return; + } + yield(); + } + if (param[0] != 0xaa) { + dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]); + return; + } + + /* Disable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL); + if (ret) + return; + + // Set scancode command (mode 2) + param[0] = 0x02; + ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param); + if (ret) + return; + + // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ + SET_LOW(Ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT); + + /* Enable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL); + if (ret) + return; + + dprintf(1, "PS2 keyboard initialized\n"); +} + +void +ps2port_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PS2PORT) + return; + dprintf(3, "init ps2port\n"); + + enable_hwirq(1, FUNC16(entry_09)); + enable_hwirq(12, FUNC16(entry_74)); + + run_thread(ps2_keyboard_setup, NULL); +} diff --git a/qemu/roms/seabios/src/hw/ps2port.h b/qemu/roms/seabios/src/hw/ps2port.h new file mode 100644 index 000000000..e5d9014b7 --- /dev/null +++ b/qemu/roms/seabios/src/hw/ps2port.h @@ -0,0 +1,66 @@ +// Basic ps2 port (keyboard/mouse) command handling. +#ifndef __PS2PORT_H +#define __PS2PORT_H + +#include "types.h" // u8 + +#define PORT_PS2_DATA 0x0060 +#define PORT_PS2_STATUS 0x0064 + +// Standard commands. +#define I8042_CMD_CTL_RCTR 0x0120 +#define I8042_CMD_CTL_WCTR 0x1060 +#define I8042_CMD_CTL_TEST 0x01aa + +#define I8042_CMD_KBD_TEST 0x01ab +#define I8042_CMD_KBD_DISABLE 0x00ad +#define I8042_CMD_KBD_ENABLE 0x00ae + +#define I8042_CMD_AUX_DISABLE 0x00a7 +#define I8042_CMD_AUX_ENABLE 0x00a8 +#define I8042_CMD_AUX_SEND 0x10d4 + +// Keyboard commands +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 +#define ATKBD_CMD_RESET_BAT 0x02ff + +// Mouse commands +#define PSMOUSE_CMD_SETSCALE11 0x00e6 +#define PSMOUSE_CMD_SETSCALE21 0x00e7 +#define PSMOUSE_CMD_SETRES 0x10e8 +#define PSMOUSE_CMD_GETINFO 0x03e9 +#define PSMOUSE_CMD_GETID 0x02f2 +#define PSMOUSE_CMD_SETRATE 0x10f3 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 +#define PSMOUSE_CMD_RESET_BAT 0x02ff + +// Status register bits. +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_MUXERR 0x04 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +// Control register bits. +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + +// ps2port.c +void i8042_reboot(void); +int ps2_kbd_command(int command, u8 *param); +int ps2_mouse_command(int command, u8 *param); +void ps2port_setup(void); + +#endif // ps2port.h diff --git a/qemu/roms/seabios/src/hw/pvscsi.c b/qemu/roms/seabios/src/hw/pvscsi.c new file mode 100644 index 000000000..601a551db --- /dev/null +++ b/qemu/roms/seabios/src/hw/pvscsi.c @@ -0,0 +1,354 @@ +// QEMU VMWARE Paravirtualized SCSI boot support. +// +// Copyright (c) 2013 Ravello Systems LTD (http://ravellosystems.com) +// +// Authors: +// Evgeny Budilovsky <evgeny.budilovsky@ravellosystems.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI +#include "pci_regs.h" // PCI_VENDOR_ID +#include "pvscsi.h" // pvscsi_setup +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-ring.h" // PAGE_SHIFT, virt_to_phys +#include "x86.h" // writel + +#define MASK(n) ((1 << (n)) - 1) + +#define SIMPLE_QUEUE_TAG 0x20 + +#define PVSCSI_INTR_CMPL_0 (1 << 0) +#define PVSCSI_INTR_CMPL_1 (1 << 1) +#define PVSCSI_INTR_CMPL_MASK MASK(2) + +#define PVSCSI_INTR_MSG_0 (1 << 2) +#define PVSCSI_INTR_MSG_1 (1 << 3) +#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) +#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) + +#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) +#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) +#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) +#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) +#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) + +enum PVSCSIRegOffset { + PVSCSI_REG_OFFSET_COMMAND = 0x0, + PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, + PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, + PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, + PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, + PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, + PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, + PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, + PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, + PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, + PVSCSI_REG_OFFSET_DEBUG = 0x3018, + PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, +}; + +enum PVSCSICommands { + PVSCSI_CMD_FIRST = 0, + PVSCSI_CMD_ADAPTER_RESET = 1, + PVSCSI_CMD_ISSUE_SCSI = 2, + PVSCSI_CMD_SETUP_RINGS = 3, + PVSCSI_CMD_RESET_BUS = 4, + PVSCSI_CMD_RESET_DEVICE = 5, + PVSCSI_CMD_ABORT_CMD = 6, + PVSCSI_CMD_CONFIG = 7, + PVSCSI_CMD_SETUP_MSG_RING = 8, + PVSCSI_CMD_DEVICE_UNPLUG = 9, + PVSCSI_CMD_LAST = 10 +}; + +#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 +struct PVSCSICmdDescSetupRings { + u32 reqRingNumPages; + u32 cmpRingNumPages; + u64 ringsStatePPN; + u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; + u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; +} PACKED; + +struct PVSCSIRingCmpDesc { + u64 context; + u64 dataLen; + u32 senseLen; + u16 hostStatus; + u16 scsiStatus; + u32 pad[2]; +} PACKED; + +struct PVSCSIRingsState { + u32 reqProdIdx; + u32 reqConsIdx; + u32 reqNumEntriesLog2; + + u32 cmpProdIdx; + u32 cmpConsIdx; + u32 cmpNumEntriesLog2; + + u8 pad[104]; + + u32 msgProdIdx; + u32 msgConsIdx; + u32 msgNumEntriesLog2; +} PACKED; + +struct PVSCSIRingReqDesc { + u64 context; + u64 dataAddr; + u64 dataLen; + u64 senseAddr; + u32 senseLen; + u32 flags; + u8 cdb[16]; + u8 cdbLen; + u8 lun[8]; + u8 tag; + u8 bus; + u8 target; + u8 vcpuHint; + u8 unused[59]; +} PACKED; + +struct pvscsi_ring_dsc_s { + struct PVSCSIRingsState *ring_state; + struct PVSCSIRingReqDesc *ring_reqs; + struct PVSCSIRingCmpDesc *ring_cmps; +}; + +struct pvscsi_lun_s { + struct drive_s drive; + void *iobase; + u8 target; + u8 lun; + struct pvscsi_ring_dsc_s *ring_dsc; +}; + +static void +pvscsi_write_cmd_desc(void *iobase, u32 cmd, const void *desc, size_t len) +{ + const u32 *ptr = desc; + size_t i; + + len /= sizeof(*ptr); + writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd); + for (i = 0; i < len; i++) + writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]); +} + +static void +pvscsi_kick_rw_io(void *iobase) +{ + writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0); +} + +static void +pvscsi_wait_intr_cmpl(void *iobase) +{ + while (!(readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK)) + usleep(5); + writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK); +} + +static void +pvscsi_init_rings(void *iobase, struct pvscsi_ring_dsc_s **ring_dsc) +{ + struct PVSCSICmdDescSetupRings cmd = {0,}; + + struct pvscsi_ring_dsc_s *dsc = memalign_low(sizeof(*dsc), PAGE_SIZE); + if (!dsc) { + warn_noalloc(); + return; + } + + dsc->ring_state = + (struct PVSCSIRingsState *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_reqs = + (struct PVSCSIRingReqDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_cmps = + (struct PVSCSIRingCmpDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) { + warn_noalloc(); + return; + } + memset(dsc->ring_state, 0, PAGE_SIZE); + memset(dsc->ring_reqs, 0, PAGE_SIZE); + memset(dsc->ring_cmps, 0, PAGE_SIZE); + + cmd.reqRingNumPages = 1; + cmd.cmpRingNumPages = 1; + cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT; + cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT; + cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT; + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS, + &cmd, sizeof(cmd)); + *ring_dsc = dsc; +} + +static void pvscsi_fill_req(struct PVSCSIRingsState *s, + struct PVSCSIRingReqDesc *req, + u16 target, u16 lun, void *cdbcmd, u16 blocksize, + struct disk_op_s *op) +{ + req->bus = 0; + req->target = target; + memset(req->lun, 0, sizeof(req->lun)); + req->lun[1] = lun; + req->senseLen = 0; + req->senseAddr = 0; + req->cdbLen = 16; + req->vcpuHint = 0; + memcpy(req->cdb, cdbcmd, 16); + req->tag = SIMPLE_QUEUE_TAG; + req->flags = cdb_is_read(cdbcmd, blocksize) ? + PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE; + + req->dataLen = op->count * blocksize; + req->dataAddr = (u32)op->buf_fl; + s->reqProdIdx = s->reqProdIdx + 1; +} + +static u32 +pvscsi_get_rsp(struct PVSCSIRingsState *s, + struct PVSCSIRingCmpDesc *rsp) +{ + u32 status = rsp->hostStatus; + s->cmpConsIdx = s->cmpConsIdx + 1; + return status; +} + +static int +pvscsi_cmd(struct pvscsi_lun_s *plun, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct pvscsi_ring_dsc_s *ring_dsc = plun->ring_dsc; + struct PVSCSIRingsState *s = ring_dsc->ring_state; + u32 req_entries = s->reqNumEntriesLog2; + u32 cmp_entries = s->cmpNumEntriesLog2; + struct PVSCSIRingReqDesc *req; + struct PVSCSIRingCmpDesc *rsp; + u32 status; + + if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) { + dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n", + s->reqProdIdx, s->cmpConsIdx); + return DISK_RET_EBADTRACK; + } + + req = ring_dsc->ring_reqs + (s->reqProdIdx & MASK(req_entries)); + pvscsi_fill_req(s, req, target, lun, cdbcmd, blocksize, op); + + pvscsi_kick_rw_io(plun->iobase); + pvscsi_wait_intr_cmpl(plun->iobase); + + rsp = ring_dsc->ring_cmps + (s->cmpConsIdx & MASK(cmp_entries)); + status = pvscsi_get_rsp(s, rsp); + + return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_PVSCSI) + return DISK_RET_EBADTRACK; + + struct pvscsi_lun_s *plun = + container_of(op->drive_gf, struct pvscsi_lun_s, drive); + + return pvscsi_cmd(plun, op, cdbcmd, plun->target, plun->lun, blocksize); +} + +static int +pvscsi_add_lun(struct pci_device *pci, void *iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun) +{ + struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun)); + if (!plun) { + warn_noalloc(); + return -1; + } + memset(plun, 0, sizeof(*plun)); + plun->drive.type = DTYPE_PVSCSI; + plun->drive.cntl_id = pci->bdf; + plun->target = target; + plun->lun = lun; + plun->iobase = iobase; + plun->ring_dsc = ring_dsc; + + char *name = znprintf(16, "pvscsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&plun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(plun); + return -1; +} + +static void +pvscsi_scan_target(struct pci_device *pci, void *iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + pvscsi_add_lun(pci, iobase, ring_dsc, target, 0); +} + +static void +init_pvscsi(struct pci_device *pci) +{ + struct pvscsi_ring_dsc_s *ring_dsc = NULL; + int i; + u16 bdf = pci->bdf; + void *iobase = (void*)(pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found pvscsi at %02x:%02x.%x, io @ %p\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0); + + pvscsi_init_rings(iobase, &ring_dsc); + for (i = 0; i < 7; i++) + pvscsi_scan_target(pci, iobase, ring_dsc, i); + + return; +} + +void +pvscsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PVSCSI) + return; + + dprintf(3, "init pvscsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_VMWARE + || pci->device != PCI_DEVICE_ID_VMWARE_PVSCSI) + continue; + init_pvscsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/pvscsi.h b/qemu/roms/seabios/src/hw/pvscsi.h new file mode 100644 index 000000000..fde9f0b98 --- /dev/null +++ b/qemu/roms/seabios/src/hw/pvscsi.h @@ -0,0 +1,8 @@ +#ifndef _PVSCSI_H_ +#define _PVSCSI_H_ + +struct disk_op_s; +int pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void pvscsi_setup(void); + +#endif /* _PVSCSI_H_ */ diff --git a/qemu/roms/seabios/src/hw/ramdisk.c b/qemu/roms/seabios/src/hw/ramdisk.c new file mode 100644 index 000000000..1177bc00a --- /dev/null +++ b/qemu/roms/seabios/src/hw/ramdisk.c @@ -0,0 +1,111 @@ +// Code for emulating a drive via high-memory accesses. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "malloc.h" // malloc_fseg +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // call16_int +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // process_ramdisk_op + +void +ramdisk_setup(void) +{ + if (!CONFIG_FLASH_FLOPPY) + return; + + // Find image. + struct romfile_s *file = romfile_findprefix("floppyimg/", NULL); + if (!file) + return; + const char *filename = file->name; + u32 size = file->size; + dprintf(3, "Found floppy file %s of size %d\n", filename, size); + int ftype = find_floppy_type(size); + if (ftype < 0) { + dprintf(3, "No floppy type found for ramdisk size\n"); + return; + } + + // Allocate ram for image. + void *pos = memalign_tmphigh(PAGE_SIZE, size); + if (!pos) { + warn_noalloc(); + return; + } + add_e820((u32)pos, size, E820_RESERVED); + + // Copy image into ram. + int ret = file->copy(file, pos, size); + if (ret < 0) + return; + + // Setup driver. + struct drive_s *drive = init_floppy((u32)pos, ftype); + if (!drive) + return; + drive->type = DTYPE_RAMDISK; + dprintf(1, "Mapping CBFS floppy %s to addr %p\n", filename, pos); + char *desc = znprintf(MAXDESCSIZE, "Ramdisk [%s]", &filename[10]); + boot_add_floppy(drive, desc, bootprio_find_named_rom(filename, 0)); +} + +static int +ramdisk_copy(struct disk_op_s *op, int iswrite) +{ + u32 offset = GET_GLOBALFLAT(op->drive_gf->cntl_id); + offset += (u32)op->lba * DISK_SECTOR_SIZE; + u64 opd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)op->buf_fl); + u64 ramd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE(offset); + + u64 gdt[6]; + if (iswrite) { + gdt[2] = opd; + gdt[3] = ramd; + } else { + gdt[2] = ramd; + gdt[3] = opd; + } + + // Call int 1587 to copy data. + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_CF|F_IF; + br.ah = 0x87; + br.es = GET_SEG(SS); + br.si = (u32)gdt; + br.cx = op->count * DISK_SECTOR_SIZE / 2; + call16_int(0x15, &br); + + if (br.flags & F_CF) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int +process_ramdisk_op(struct disk_op_s *op) +{ + if (!CONFIG_FLASH_FLOPPY) + return 0; + + switch (op->command) { + case CMD_READ: + return ramdisk_copy(op, 0); + case CMD_WRITE: + return ramdisk_copy(op, 1); + case CMD_VERIFY: + case CMD_FORMAT: + case CMD_RESET: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} diff --git a/qemu/roms/seabios/src/hw/rtc.c b/qemu/roms/seabios/src/hw/rtc.c new file mode 100644 index 000000000..628d5429f --- /dev/null +++ b/qemu/roms/seabios/src/hw/rtc.c @@ -0,0 +1,93 @@ +// Support for MC146818 Real Time Clock chip. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "util.h" // timer_calc +#include "x86.h" // inb + +u8 +rtc_read(u8 index) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + return inb(PORT_CMOS_DATA); +} + +void +rtc_write(u8 index, u8 val) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + outb(val, PORT_CMOS_DATA); +} + +void +rtc_mask(u8 index, u8 off, u8 on) +{ + outb(index, PORT_CMOS_INDEX); + u8 val = inb(PORT_CMOS_DATA); + outb((val & ~off) | on, PORT_CMOS_DATA); +} + +int +rtc_updating(void) +{ + // This function checks to see if the update-in-progress bit + // is set in CMOS Status Register A. If not, it returns 0. + // If it is set, it tries to wait until there is a transition + // to 0, and will return 0 if such a transition occurs. A -1 + // is returned only after timing out. The maximum period + // that this bit should be set is constrained to (1984+244) + // useconds, but we wait for longer just to be sure. + + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + u32 end = timer_calc(15); + for (;;) { + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + if (timer_check(end)) + // update-in-progress never transitioned to 0 + return -1; + yield(); + } +} + +void +rtc_setup(void) +{ + rtc_write(CMOS_STATUS_A, 0x26); // 32,768Khz src, 976.5625us updates + rtc_mask(CMOS_STATUS_B, ~RTC_B_DSE, RTC_B_24HR); + rtc_read(CMOS_STATUS_C); + rtc_read(CMOS_STATUS_D); +} + +int RTCusers VARLOW; + +void +rtc_use(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count+1); + if (count) + return; + // Turn on the Periodic Interrupt timer + rtc_mask(CMOS_STATUS_B, 0, RTC_B_PIE); +} + +void +rtc_release(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count-1); + if (count != 1) + return; + // Clear the Periodic Interrupt. + rtc_mask(CMOS_STATUS_B, RTC_B_PIE, 0); +} diff --git a/qemu/roms/seabios/src/hw/rtc.h b/qemu/roms/seabios/src/hw/rtc.h new file mode 100644 index 000000000..252e73a41 --- /dev/null +++ b/qemu/roms/seabios/src/hw/rtc.h @@ -0,0 +1,75 @@ +#ifndef __RTC_H +#define __RTC_H + +#define PORT_CMOS_INDEX 0x0070 +#define PORT_CMOS_DATA 0x0071 + +// PORT_CMOS_INDEX nmi disable bit +#define NMI_DISABLE_BIT 0x80 + +// Standard BIOS RTC chip entries +#define CMOS_RTC_SECONDS 0x00 +#define CMOS_RTC_SECONDS_ALARM 0x01 +#define CMOS_RTC_MINUTES 0x02 +#define CMOS_RTC_MINUTES_ALARM 0x03 +#define CMOS_RTC_HOURS 0x04 +#define CMOS_RTC_HOURS_ALARM 0x05 +#define CMOS_RTC_DAY_WEEK 0x06 +#define CMOS_RTC_DAY_MONTH 0x07 +#define CMOS_RTC_MONTH 0x08 +#define CMOS_RTC_YEAR 0x09 +#define CMOS_STATUS_A 0x0a +#define CMOS_STATUS_B 0x0b +#define CMOS_STATUS_C 0x0c +#define CMOS_STATUS_D 0x0d +#define CMOS_RESET_CODE 0x0f + +// QEMU cmos config fields. DO NOT ADD MORE. (All new content should +// be passed via the fw_cfg "file" interface.) +#define CMOS_FLOPPY_DRIVE_TYPE 0x10 +#define CMOS_DISK_DATA 0x12 +#define CMOS_EQUIPMENT_INFO 0x14 +#define CMOS_DISK_DRIVE1_TYPE 0x19 +#define CMOS_DISK_DRIVE2_TYPE 0x1a +#define CMOS_DISK_DRIVE1_CYL 0x1b +#define CMOS_DISK_DRIVE2_CYL 0x24 +#define CMOS_MEM_EXTMEM_LOW 0x30 +#define CMOS_MEM_EXTMEM_HIGH 0x31 +#define CMOS_CENTURY 0x32 +#define CMOS_MEM_EXTMEM2_LOW 0x34 +#define CMOS_MEM_EXTMEM2_HIGH 0x35 +#define CMOS_BIOS_BOOTFLAG1 0x38 +#define CMOS_BIOS_DISKTRANSFLAG 0x39 +#define CMOS_BIOS_BOOTFLAG2 0x3d +#define CMOS_MEM_HIGHMEM_LOW 0x5b +#define CMOS_MEM_HIGHMEM_MID 0x5c +#define CMOS_MEM_HIGHMEM_HIGH 0x5d +#define CMOS_BIOS_SMP_COUNT 0x5f + +// RTC register flags +#define RTC_A_UIP 0x80 + +#define RTC_B_SET 0x80 +#define RTC_B_PIE 0x40 +#define RTC_B_AIE 0x20 +#define RTC_B_UIE 0x10 +#define RTC_B_BIN 0x04 +#define RTC_B_24HR 0x02 +#define RTC_B_DSE 0x01 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u8 + +// rtc.c +u8 rtc_read(u8 index); +void rtc_write(u8 index, u8 val); +void rtc_mask(u8 index, u8 off, u8 on); +int rtc_updating(void); +void rtc_setup(void); +void rtc_use(void); +void rtc_release(void); + +#endif // !__ASSEMBLY__ + +#endif // rtc.h diff --git a/qemu/roms/seabios/src/hw/sdcard.c b/qemu/roms/seabios/src/hw/sdcard.c new file mode 100644 index 000000000..6ff93c856 --- /dev/null +++ b/qemu/roms/seabios/src/hw/sdcard.c @@ -0,0 +1,321 @@ +// PCI SD Host Controller Interface +// +// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "block.h" // struct drive_s +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // malloc_fseg +#include "output.h" // znprintf +#include "pci.h" // pci_config_readl +#include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "stacks.h" // wait_preempt +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // boot_add_hd +#include "x86.h" // writel + +// SDHCI MMIO registers +struct sdhci_s { + u32 sdma_addr; + u16 block_size; + u16 block_count; + u32 arg; + u16 transfer_mode; + u16 cmd; + u32 response[4]; + u32 data; + u32 present_state; + u8 host_control; + u8 power_control; + u8 block_gap_control; + u8 wakeup_control; + u16 clock_control; + u8 timeout_control; + u8 software_reset; + u16 irq_status; + u16 error_irq_status; + u16 irq_enable; + u16 error_irq_enable; + u16 irq_signal; + u16 error_signal; + u16 auto_cmd12; + u8 pad_3E[2]; + u64 cap; + u64 max_current; + u16 force_auto_cmd12; + u16 force_error; + u8 adma_error; + u8 pad_55[3]; + u64 adma_addr; + u8 pad_60[156]; + u16 slot_irq; + u16 controller_version; +} PACKED; + +// SDHCI commands +#define SC_ALL_SEND_CID ((2<<8) | 0x21) +#define SC_SEND_RELATIVE_ADDR ((3<<8) | 0x22) +#define SC_SELECT_DESELECT_CARD ((7<<8) | 0x23) +#define SC_READ_SINGLE ((17<<8) | 0x22) +#define SC_READ_MULTIPLE ((18<<8) | 0x22) +#define SC_WRITE_SINGLE ((24<<8) | 0x22) +#define SC_WRITE_MULTIPLE ((25<<8) | 0x22) +#define SC_APP_CMD ((55<<8) | 0x22) +#define SC_APP_SEND_OP_COND ((41<<8) | 0x22) + +// SDHCI irqs +#define SI_CMD_COMPLETE (1<<0) +#define SI_TRANS_DONE (1<<1) +#define SI_WRITE_READY (1<<4) +#define SI_READ_READY (1<<5) + +// SDHCI present_state flags +#define SP_CMD_INHIBIT (1<<0) +#define SP_DAT_INHIBIT (1<<1) + +// SDHCI transfer_mode flags +#define ST_BLOCKCOUNT (1<<1) +#define ST_AUTO_CMD12 (1<<2) +#define ST_READ (1<<4) +#define ST_MULTIPLE (1<<5) + +// SDHCI result flags +#define SR_OCR_CCS (1<<30) + +// SDHCI timeouts +#define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up +#define SDHCI_TRANSFER_TIMEOUT 10000 + +// Internal 'struct drive_s' storage for a detected card +struct sddrive_s { + struct drive_s drive; + struct sdhci_s *regs; + int card_type; +}; + +// SD card types +#define SF_MMC 0 +#define SF_SDSC 1 +#define SF_SDHC 2 + +// Repeatedly read a u16 register until the specific value is found +static int +waitw(u16 *reg, u16 mask, u16 value, u32 end) +{ + for (;;) { + u16 v = readw(reg); + if ((v & mask) == value) + return 0; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Send a command to the card. +static int +sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + u16 busyf = SP_CMD_INHIBIT | ((cmd & 0x03) == 0x03 ? SP_DAT_INHIBIT : 0); + int ret = waitw((u16*)®s->present_state, busyf, 0, end); + if (ret) + return ret; + // Send command + writel(®s->arg, *param); + writew(®s->cmd, cmd); + ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); + if (ret) + return ret; + writew(®s->irq_status, SI_CMD_COMPLETE); + // Read response + memcpy(param, regs->response, sizeof(regs->response)); + return 0; +} + +// Send an "app specific" command to the card. +static int +sdcard_pio_app(struct sdhci_s *regs, int cmd, u32 *param) +{ + u32 aparam[4] = {}; + int ret = sdcard_pio(regs, SC_APP_CMD, aparam); + if (ret) + return ret; + return sdcard_pio(regs, cmd, param); +} + +// Send a command to the card which transfers data. +static int +sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr + , void *data, int count) +{ + // Send command + writel(&drive->regs->block_size, DISK_SECTOR_SIZE); + writew(&drive->regs->block_count, count); // XXX - SC_SET_BLOCK_COUNT? + int isread = cmd != SC_WRITE_SINGLE && cmd != SC_WRITE_MULTIPLE; + u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) + | (isread ? ST_READ : 0)); + writew(&drive->regs->transfer_mode, tmode); + if (drive->card_type < SF_SDHC) + addr *= DISK_SECTOR_SIZE; + u32 param[4] = { addr }; + int ret = sdcard_pio(drive->regs, cmd, param); + if (ret) + return ret; + // Read/write data + u32 end = timer_calc(SDHCI_TRANSFER_TIMEOUT); + u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; + while (count--) { + ret = waitw(&drive->regs->irq_status, cbit, cbit, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, cbit); + int i; + for (i=0; i<DISK_SECTOR_SIZE/4; i++) { + if (isread) + *(u32*)data = readl(&drive->regs->data); + else + writel(&drive->regs->data, *(u32*)data); + data += 4; + } + } + // Complete command + // XXX - SC_STOP_TRANSMISSION? + ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); + if (ret) + return ret; + writew(&drive->regs->irq_status, SI_TRANS_DONE); + return 0; +} + +// Read/write a block of data to/from the card. +static int +sdcard_readwrite(struct disk_op_s *op, int iswrite) +{ + struct sddrive_s *drive = container_of( + op->drive_gf, struct sddrive_s, drive); + int cmd = iswrite ? SC_WRITE_SINGLE : SC_READ_SINGLE; + if (op->count > 1) + cmd = iswrite ? SC_WRITE_MULTIPLE : SC_READ_MULTIPLE; + int ret = sdcard_pio_transfer(drive, cmd, op->lba, op->buf_fl, op->count); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int VISIBLE32FLAT +process_sdcard_op(struct disk_op_s *op) +{ + if (!CONFIG_SDCARD) + return 0; + switch (op->command) { + case CMD_READ: + return sdcard_readwrite(op, 0); + case CMD_WRITE: + return sdcard_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Initialize an SD card +static int +sdcard_card_setup(struct sdhci_s *regs) +{ + // XXX - works on QEMU; probably wont on real hardware! + u32 param[4] = { 0x01 }; + int ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) + return ret; + int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; + param[0] = 0x00; + ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); + if (ret) + return ret; + param[0] = 0x01 << 16; + ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); + if (ret) + return ret; + u16 rca = param[0] >> 16; + param[0] = rca << 16; + ret = sdcard_pio(regs, SC_SELECT_DESELECT_CARD, param); + if (ret) + return ret; + return card_type; +} + +// Setup and configure an SD card controller +static void +sdcard_controller_setup(void *data) +{ + struct pci_device *pci = data; + u16 bdf = pci->bdf; + wait_preempt(); // Avoid pci_config_readl when preempting + struct sdhci_s *regs = (void*)pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + // Initialize controller + if (!runningOnQEMU()) + // XXX - this init logic will probably only work on qemu! + return; + writew(®s->irq_signal, 0); + writew(®s->irq_enable, 0xffff); + writew(®s->error_signal, 0); + writeb(®s->power_control, 0x0f); + writew(®s->clock_control, 0x0005); + + // Initialize card + int card_type = sdcard_card_setup(regs); + if (card_type < 0) + return; + + // Register drive + struct sddrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return; + } + memset(drive, 0, sizeof(*drive)); + drive->drive.type = DTYPE_SDCARD; + drive->drive.blksize = DISK_SECTOR_SIZE; + drive->drive.sectors = (u64)-1; // XXX + drive->regs = regs; + drive->card_type = card_type; + + dprintf(1, "Found SD Card at %02x:%02x.%x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX + boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); +} + +void +sdcard_setup(void) +{ + if (!CONFIG_SDCARD) + return; + + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_SYSTEM_SDHCI || pci->prog_if >= 2) + // Not an SDHCI controller following SDHCI spec + continue; + run_thread(sdcard_controller_setup, pci); + } +} diff --git a/qemu/roms/seabios/src/hw/serialio.c b/qemu/roms/seabios/src/hw/serialio.c new file mode 100644 index 000000000..6486fc086 --- /dev/null +++ b/qemu/roms/seabios/src/hw/serialio.c @@ -0,0 +1,89 @@ +// Low-level serial (and serial-like) device access. +// +// Copyright (C) 2008-1013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_DEBUG_SERIAL +#include "fw/paravirt.h" // RunningOnQEMU +#include "output.h" // dprintf +#include "serialio.h" // serial_debug_preinit +#include "x86.h" // outb + + +/**************************************************************** + * Serial port debug output + ****************************************************************/ + +#define DEBUG_TIMEOUT 100000 + +// Setup the debug serial port for output. +void +serial_debug_preinit(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + // setup for serial logging: 8N1 + u8 oldparam, newparam = 0x03; + oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + // Disable irqs + u8 oldier, newier = 0; + oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + + if (oldparam != newparam || oldier != newier) + dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" + , oldparam, oldier, newparam, newier); +} + +// Write a character to the serial port. +static void +serial_debug(char c) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + if (!timeout--) + // Ran out of time. + return; + outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); +} + +void +serial_debug_putc(char c) +{ + if (c == '\n') + serial_debug('\r'); + serial_debug(c); +} + +// Make sure all serial port writes have been completely sent. +void +serial_debug_flush(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + if (!timeout--) + // Ran out of time. + return; +} + + +/**************************************************************** + * QEMU debug port + ****************************************************************/ + +u16 DebugOutputPort VARFSEG = 0x402; + +// Write a character to the special debugging port. +void +qemu_debug_putc(char c) +{ + if (CONFIG_DEBUG_IO && runningOnQEMU()) + // Send character to debug port. + outb(c, GET_GLOBAL(DebugOutputPort)); +} diff --git a/qemu/roms/seabios/src/hw/serialio.h b/qemu/roms/seabios/src/hw/serialio.h new file mode 100644 index 000000000..88296fe7f --- /dev/null +++ b/qemu/roms/seabios/src/hw/serialio.h @@ -0,0 +1,29 @@ +#ifndef __SERIALIO_H +#define __SERIALIO_H + +#include "types.h" // u16 + +#define PORT_LPT2 0x0278 +#define PORT_SERIAL4 0x02e8 +#define PORT_SERIAL2 0x02f8 +#define PORT_LPT1 0x0378 +#define PORT_SERIAL3 0x03e8 +#define PORT_SERIAL1 0x03f8 + +// Serial port offsets +#define SEROFF_DATA 0 +#define SEROFF_DLL 0 +#define SEROFF_IER 1 +#define SEROFF_DLH 1 +#define SEROFF_IIR 2 +#define SEROFF_LCR 3 +#define SEROFF_LSR 5 +#define SEROFF_MSR 6 + +void serial_debug_preinit(void); +void serial_debug_putc(char c); +void serial_debug_flush(void); +extern u16 DebugOutputPort; +void qemu_debug_putc(char c); + +#endif // serialio.h diff --git a/qemu/roms/seabios/src/hw/timer.c b/qemu/roms/seabios/src/hw/timer.c new file mode 100644 index 000000000..5edc9fdbb --- /dev/null +++ b/qemu/roms/seabios/src/hw/timer.c @@ -0,0 +1,257 @@ +// Internal timer and Intel 8253 Programmable Interrupt Timer (PIT) support. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "stacks.h" // yield +#include "util.h" // timer_setup +#include "x86.h" // cpuid + +#define PORT_PIT_COUNTER0 0x0040 +#define PORT_PIT_COUNTER1 0x0041 +#define PORT_PIT_COUNTER2 0x0042 +#define PORT_PIT_MODE 0x0043 +#define PORT_PS2_CTRLB 0x0061 + +// Bits for PORT_PIT_MODE +#define PM_SEL_TIMER0 (0<<6) +#define PM_SEL_TIMER1 (1<<6) +#define PM_SEL_TIMER2 (2<<6) +#define PM_SEL_READBACK (3<<6) +#define PM_ACCESS_LATCH (0<<4) +#define PM_ACCESS_LOBYTE (1<<4) +#define PM_ACCESS_HIBYTE (2<<4) +#define PM_ACCESS_WORD (3<<4) +#define PM_MODE0 (0<<1) +#define PM_MODE1 (1<<1) +#define PM_MODE2 (2<<1) +#define PM_MODE3 (3<<1) +#define PM_MODE4 (4<<1) +#define PM_MODE5 (5<<1) +#define PM_CNT_BINARY (0<<0) +#define PM_CNT_BCD (1<<0) +#define PM_READ_COUNTER0 (1<<1) +#define PM_READ_COUNTER1 (1<<2) +#define PM_READ_COUNTER2 (1<<3) +#define PM_READ_STATUSVALUE (0<<4) +#define PM_READ_VALUE (1<<4) +#define PM_READ_STATUS (2<<4) + +// Bits for PORT_PS2_CTRLB +#define PPCB_T2GATE (1<<0) +#define PPCB_SPKR (1<<1) +#define PPCB_T2OUT (1<<5) + +#define PMTIMER_HZ 3579545 // Underlying Hz of the PM Timer +#define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate + +u32 TimerKHz VARFSEG; +u16 TimerPort VARFSEG; +u8 ShiftTSC VARFSEG; + + +/**************************************************************** + * Internal timer setup + ****************************************************************/ + +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +// Calibrate the CPU time-stamp-counter +static void +tsctimer_setup(void) +{ + // Setup "timer2" + u8 orig = inb(PORT_PS2_CTRLB); + outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); + /* LSB of ticks */ + outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); + /* MSB of ticks */ + outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); + + u64 start = rdtscll(); + while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) + ; + u64 end = rdtscll(); + + // Restore PORT_PS2_CTRLB + outb(orig, PORT_PS2_CTRLB); + + // Store calibrated cpu khz. + u64 diff = end - start; + dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n" + , (u32)start, (u32)end, (u32)diff); + u64 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); + while (t >= (1<<24)) { + ShiftTSC++; + t = (t + 1) >> 1; + } + TimerKHz = DIV_ROUND_UP((u32)t, 1000 * PMTIMER_TO_PIT); + + dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); +} + +// Setup internal timers. +void +timer_setup(void) +{ + if (CONFIG_PMTIMER && TimerPort) { + dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + return; + } + + u32 eax, ebx, ecx, edx, cpuid_features = 0; + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax > 0) + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + + if (!(cpuid_features & CPUID_TSC)) { + TimerPort = PORT_PIT_COUNTER0; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + + tsctimer_setup(); +} + +void +pmtimer_setup(u16 ioport) +{ + if (!CONFIG_PMTIMER) + return; + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); + TimerPort = ioport; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); +} + + +/**************************************************************** + * Internal timer reading + ****************************************************************/ + +u32 TimerLast VARLOW; + +// Add extra high bits to timers that have less than 32bits of precision. +static u32 +timer_adjust_bits(u32 value, u32 validbits) +{ + u32 last = GET_LOW(TimerLast); + value = (last & ~validbits) | (value & validbits); + if (value < last) + value += validbits + 1; + SET_LOW(TimerLast, value); + return value; +} + +// Sample the current timer value. +static u32 +timer_read(void) +{ + u16 port = GET_GLOBAL(TimerPort); + if (!port) + // Read from CPU TSC + return rdtscll() >> GET_GLOBAL(ShiftTSC); + if (CONFIG_PMTIMER && port != PORT_PIT_COUNTER0) + // Read from PMTIMER + return timer_adjust_bits(inl(port), 0xffffff); + // Read from PIT. + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + u16 v = inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8); + return timer_adjust_bits(v, 0xffff); +} + +// Check if the current time is past a previously calculated end time. +int +timer_check(u32 end) +{ + return (s32)(timer_read() - end) > 0; +} + +static void +timer_delay(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + cpu_relax(); +} + +static void +timer_sleep(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + yield(); +} + +void ndelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void udelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void mdelay(u32 count) { + timer_delay(count * GET_GLOBAL(TimerKHz)); +} + +void nsleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void usleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void msleep(u32 count) { + timer_sleep(count * GET_GLOBAL(TimerKHz)); +} + +// Return the TSC value that is 'msecs' time in the future. +u32 +timer_calc(u32 msecs) +{ + return timer_read() + (GET_GLOBAL(TimerKHz) * msecs); +} +u32 +timer_calc_usec(u32 usecs) +{ + return timer_read() + DIV_ROUND_UP(GET_GLOBAL(TimerKHz) * usecs, 1000); +} + + +/**************************************************************** + * PIT setup + ****************************************************************/ + +#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer + +// Return the number of milliseconds in 'ticks' number of timer irqs. +u32 +ticks_to_ms(u32 ticks) +{ + u32 t = PIT_TICK_INTERVAL * 1000 * PMTIMER_TO_PIT * ticks; + return DIV_ROUND_UP(t, PMTIMER_HZ); +} + +// Return the number of timer irqs in 'ms' number of milliseconds. +u32 +ticks_from_ms(u32 ms) +{ + u32 t = DIV_ROUND_UP((u64)ms * PMTIMER_HZ, PIT_TICK_INTERVAL); + return DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); +} + +void +pit_setup(void) +{ + // timer0: binary count, 16bit count, mode 2 + outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); + // maximum count of 0000H = 18.2Hz + outb(0x0, PORT_PIT_COUNTER0); + outb(0x0, PORT_PIT_COUNTER0); +} diff --git a/qemu/roms/seabios/src/hw/usb-ehci.c b/qemu/roms/seabios/src/hw/usb-ehci.c new file mode 100644 index 000000000..41f8579ce --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ehci.c @@ -0,0 +1,648 @@ +// Code for handling EHCI USB controllers. +// +// Copyright (C) 2010-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "malloc.h" // free +#include "memmap.h" // PAGE_SIZE +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // struct ehci_qh +#include "util.h" // msleep +#include "x86.h" // readl + +struct usb_ehci_s { + struct usb_s usb; + struct ehci_caps *caps; + struct ehci_regs *regs; + struct ehci_qh *async_qh; + int checkports; +}; + +struct ehci_pipe { + struct ehci_qh qh; + struct ehci_qtd *next_td, *tds; + void *data; + struct usb_pipe pipe; +}; + +static int PendingEHCI; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define EHCI_TIME_POSTPOWER 20 +#define EHCI_TIME_POSTRESET 2 + +// Check if device attached to port +static int +ehci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + if (!(portsc & PORT_CONNECT)) + // No device present + return 0; + + if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) { + // low speed device + writel(portreg, portsc | PORT_OWNER); + return -1; + } + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + portsc = (portsc & ~PORT_PE) | PORT_RESET; + writel(portreg, portsc); + msleep(USB_TIME_DRSTR); + return 1; +} + +// Reset device on port +static int +ehci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Finish reset on port + portsc &= ~PORT_RESET; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTRESET); + + portsc = readl(portreg); + if (!(portsc & PORT_CONNECT)) + // No longer connected + return -1; + if (!(portsc & PORT_PE)) { + // full speed device + writel(portreg, portsc | PORT_OWNER); + return -1; + } + + return USB_HIGHSPEED; +} + +// Disable port +static void +ehci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + writel(portreg, portsc & ~PORT_PE); +} + +static struct usbhub_op_s ehci_HubOp = { + .detect = ehci_hub_detect, + .reset = ehci_hub_reset, + .disconnect = ehci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ehci_ports(struct usb_ehci_s *cntl) +{ + // Power up ports. + int i; + for (i=0; i<cntl->checkports; i++) { + u32 *portreg = &cntl->regs->portsc[i]; + u32 portsc = readl(portreg); + if (!(portsc & PORT_POWER)) { + portsc |= PORT_POWER; + writel(portreg, portsc); + } + } + msleep(EHCI_TIME_POSTPOWER); + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = cntl->checkports; + hub.op = &ehci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB async frame to start - for ensuring safe memory release. +static void +ehci_waittick(struct usb_ehci_s *cntl) +{ + if (MODE16) { + msleep(10); + return; + } + // Wait for access to "doorbell" + barrier(); + u32 cmd, sts; + u32 end = timer_calc(100); + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (!(sts & STS_IAA)) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_IAAD)) + break; + } + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ring "doorbell" + writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); + // Wait for completion + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (sts & STS_IAA) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ack completion + writel(&cntl->regs->usbsts, STS_IAA); +} + +static void +ehci_free_pipes(struct usb_ehci_s *cntl) +{ + dprintf(7, "ehci_free_pipes %p\n", cntl); + + struct ehci_qh *start = cntl->async_qh; + struct ehci_qh *pos = start; + for (;;) { + struct ehci_qh *next = (void*)(pos->next & ~EHCI_PTR_BITS); + if (next == start) + break; + struct ehci_pipe *pipe = container_of(next, struct ehci_pipe, qh); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) + pos->next = next->next; + else + pos = next; + } + ehci_waittick(cntl); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + free(pipe); + } +} + +static void +configure_ehci(void *data) +{ + struct usb_ehci_s *cntl = data; + + // Allocate ram for schedule storage + struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); + struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); + if (!fl || !intr_qh || !async_qh) { + warn_noalloc(); + PendingEHCI--; + goto fail; + } + + // XXX - check for halted? + + // Reset the HC + u32 cmd = readl(&cntl->regs->usbcmd); + writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); + u32 end = timer_calc(250); + for (;;) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_HCRESET)) + break; + if (timer_check(end)) { + warn_timeout(); + PendingEHCI--; + goto fail; + } + yield(); + } + + // Disable interrupts (just to be safe). + writel(&cntl->regs->usbintr, 0); + + // Set schedule to point to primary intr queue head + memset(intr_qh, 0, sizeof(*intr_qh)); + intr_qh->next = EHCI_PTR_TERM; + intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); + intr_qh->token = QTD_STS_HALT; + intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; + writel(&cntl->regs->periodiclistbase, (u32)fl); + + // Set async list to point to primary async queue head + memset(async_qh, 0, sizeof(*async_qh)); + async_qh->next = (u32)async_qh | EHCI_PTR_QH; + async_qh->info1 = QH_HEAD; + async_qh->token = QTD_STS_HALT; + async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; + cntl->async_qh = async_qh; + writel(&cntl->regs->asynclistbase, (u32)async_qh); + + // Enable queues + writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); + + // Set default of high speed for root hub. + writel(&cntl->regs->configflag, 1); + PendingEHCI--; + + // Find devices + int count = check_ehci_ports(cntl); + ehci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); + msleep(4); // 2ms to stop reading memory - XXX +fail: + free(fl); + free(intr_qh); + free(async_qh); + free(cntl); +} + +static void +ehci_controller_setup(struct pci_device *pci) +{ + wait_preempt(); // Avoid pci_config_readl when preempting + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + u32 hcc_params = readl(&caps->hccparams); + + struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_EHCI; + cntl->caps = caps; + cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; + cntl->regs = (void*)caps + readb(&caps->caplength); + if (hcc_params & HCC_64BIT_ADDR) + cntl->regs->ctrldssegment = 0; + PendingEHCI++; + + dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // XXX - check for and disable SMM control? + + run_thread(configure_ehci, cntl); +} + +void +ehci_setup(void) +{ + if (! CONFIG_USB_EHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_EHCI) + ehci_controller_setup(pci); + } + + // Wait for all EHCI controllers to initialize. This forces OHCI/UHCI + // setup to always be after any EHCI ports are routed to EHCI. + while (PendingEHCI) + yield(); +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in qh +static void +ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + + pipe->qh.info1 = ((pipe->pipe.maxpacket << QH_MAXPACKET_SHIFT) + | (pipe->pipe.speed << QH_SPEED_SHIFT) + | (pipe->pipe.ep << QH_EP_SHIFT) + | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); + + pipe->qh.info2 = (1 << QH_MULT_SHIFT); + struct usbdevice_s *hubdev = usbdev->hub->usbdev; + if (hubdev) { + struct ehci_pipe *hpipe = container_of( + hubdev->defpipe, struct ehci_pipe, pipe); + if (hpipe->pipe.speed == USB_HIGHSPEED) + pipe->qh.info2 |= (((usbdev->port+1) << QH_HUBPORT_SHIFT) + | (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT)); + else + pipe->qh.info2 = hpipe->qh.info2; + } + + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe->qh.info1 |= ((pipe->pipe.speed != USB_HIGHSPEED ? QH_CONTROL : 0) + | QH_TOGGLECONTROL); + else if (eptype == USB_ENDPOINT_XFER_INT) + pipe->qh.info2 |= (0x01 << QH_SMASK_SHIFT) | (0x1c << QH_CMASK_SHIFT); +} + +static struct usb_pipe * +ehci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + memset(tds, 0, sizeof(*tds) * count); + memset(data, 0, maxpacket * count); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->next_td = pipe->tds = tds; + pipe->data = data; + pipe->qh.qtd_next = (u32)tds; + + int i; + for (i=0; i<count; i++) { + struct ehci_qtd *td = &tds[i]; + td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3)); + td->buf[0] = (u32)data + maxpacket * i; + } + + // Add to interrupt schedule. + struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); + pipe->qh.next = intr_qh->next; + barrier(); + intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.next = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ehci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_EHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ehci_alloc_intr_pipe(usbdev, epdesc); + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + ehci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ehci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_tmphigh(EHCI_QH_ALIGN, sizeof(*pipe)); + else + pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + + // Add queue head to controller list. + struct ehci_qh *async_qh = cntl->async_qh; + pipe->qh.next = async_qh->next; + barrier(); + async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + return &pipe->pipe; +} + +static void +ehci_reset_pipe(struct ehci_pipe *pipe) +{ + SET_LOWFLAT(pipe->qh.qtd_next, EHCI_PTR_TERM); + SET_LOWFLAT(pipe->qh.alt_next, EHCI_PTR_TERM); + barrier(); + SET_LOWFLAT(pipe->qh.token, GET_LOWFLAT(pipe->qh.token) & QTD_TOGGLE); +} + +static int +ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, u32 end) +{ + u32 status; + for (;;) { + status = td->token; + if (!(status & QTD_STS_ACTIVE)) + break; + if (timer_check(end)) { + u32 cur = GET_LOWFLAT(pipe->qh.current); + u32 tok = GET_LOWFLAT(pipe->qh.token); + u32 next = GET_LOWFLAT(pipe->qh.qtd_next); + warn_timeout(); + dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" + , pipe, cur, tok, next, td, status); + ehci_reset_pipe(pipe); + struct usb_ehci_s *cntl = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); + ehci_waittick(cntl); + return -1; + } + yield(); + } + if (status & QTD_STS_HALT) { + dprintf(1, "ehci_wait_td error - status=%x\n", status); + ehci_reset_pipe(pipe); + return -2; + } + return 0; +} + +static void +ehci_fill_tdbuf(struct ehci_qtd *td, u32 dest, int transfer) +{ + u32 *pos = td->buf, end = dest + transfer; + for (; dest < end; dest = ALIGN_DOWN(dest + PAGE_SIZE, PAGE_SIZE)) + *pos++ = dest; +} + +#define STACKQTDS 6 + +int +ehci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + dprintf(7, "ehci_send_pipe qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + + // Allocate tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; + struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKQTDS); + + // Setup transfer descriptors + u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + u32 toggle = 0; + if (cmd) { + // Send setup pid on control transfers + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(USB_CONTROL_SETUP_SIZE) | QTD_STS_ACTIVE + | QTD_PID_SETUP | ehci_maxerr(3)); + ehci_fill_tdbuf(td, (u32)cmd, USB_CONTROL_SETUP_SIZE); + td++; + toggle = QTD_TOGGLE; + } + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 5*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->qtd_next = (u32)MAKE_FLATPTR(GET_SEG(SS), td+1); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(transfer) | toggle | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + ehci_fill_tdbuf(td, dest, transfer); + td++; + dest += transfer; + } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKQTDS]) { + warn_noalloc(); + return -1; + } + td->qtd_next = EHCI_PTR_TERM; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | QTD_STS_ACTIVE + | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); + td++; + } + + // Transfer data + (td-1)->qtd_next = EHCI_PTR_TERM; + barrier(); + SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + u32 end = timer_calc(usb_xfer_time(p, datasize)); + int i; + for (i=0, td=tds; i<STACKQTDS; i++, td++) { + int ret = ehci_wait_td(pipe, td, end); + if (ret) + return -1; + } + + return 0; +} + +int +ehci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct ehci_qtd *td = GET_LOWFLAT(pipe->next_td); + u32 token = GET_LOWFLAT(td->token); + if (token & QTD_STS_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int pos = td - GET_LOWFLAT(pipe->tds); + void *tddata = GET_LOWFLAT(pipe->data) + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata), maxpacket); + + // Reenable this td. + struct ehci_qtd *next = (void*)(GET_LOWFLAT(td->qtd_next) & ~EHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + SET_LOWFLAT(td->buf[0], (u32)tddata); + barrier(); + SET_LOWFLAT(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3))); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-ehci.h b/qemu/roms/seabios/src/hw/usb-ehci.h new file mode 100644 index 000000000..88f7b6a1e --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ehci.h @@ -0,0 +1,176 @@ +#ifndef __USB_EHCI_H +#define __USB_EHCI_H + +// usb-ehci.c +void ehci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *ehci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int ehci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int ehci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ehci structs and flags + ****************************************************************/ + +struct ehci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams; + u32 hccparams; + u64 portroute; +} PACKED; + +#define HCC_64BIT_ADDR 1 + +#define HCS_N_PORTS_MASK 0xf + +struct ehci_regs { + u32 usbcmd; + u32 usbsts; + u32 usbintr; + u32 frindex; + u32 ctrldssegment; + u32 periodiclistbase; + u32 asynclistbase; + u32 reserved[9]; + u32 configflag; + u32 portsc[0]; +} PACKED; + +#define CMD_PARK (1<<11) +#define CMD_PARK_CNT(c) (((c)>>8)&3) +#define CMD_LRESET (1<<7) +#define CMD_IAAD (1<<6) +#define CMD_ASE (1<<5) +#define CMD_PSE (1<<4) +#define CMD_HCRESET (1<<1) +#define CMD_RUN (1<<0) + +#define STS_ASS (1<<15) +#define STS_PSS (1<<14) +#define STS_RECL (1<<13) +#define STS_HALT (1<<12) +#define STS_IAA (1<<5) +#define STS_FATAL (1<<4) +#define STS_FLR (1<<3) +#define STS_PCD (1<<2) +#define STS_ERR (1<<1) +#define STS_INT (1<<0) + +#define FLAG_CF (1<<0) + +#define PORT_WKOC_E (1<<22) +#define PORT_WKDISC_E (1<<21) +#define PORT_WKCONN_E (1<<20) +#define PORT_TEST_PKT (0x4<<16) +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) +#define PORT_POWER (1<<12) +#define PORT_LINESTATUS_MASK (3<<10) +#define PORT_LINESTATUS_KSTATE (1<<10) +#define PORT_RESET (1<<8) +#define PORT_SUSPEND (1<<7) +#define PORT_RESUME (1<<6) +#define PORT_OCC (1<<5) +#define PORT_OC (1<<4) +#define PORT_PEC (1<<3) +#define PORT_PE (1<<2) +#define PORT_CSC (1<<1) +#define PORT_CONNECT (1<<0) +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + + +#define EHCI_QH_ALIGN 128 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qh { + u32 next; + u32 info1; + u32 info2; + u32 current; + + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; +} PACKED; + +#define QH_CONTROL (1 << 27) +#define QH_MAXPACKET_SHIFT 16 +#define QH_MAXPACKET_MASK (0x7ff << QH_MAXPACKET_SHIFT) +#define QH_HEAD (1 << 15) +#define QH_TOGGLECONTROL (1 << 14) +#define QH_SPEED_SHIFT 12 +#define QH_SPEED_MASK (0x3 << QH_SPEED_SHIFT) +#define QH_EP_SHIFT 8 +#define QH_EP_MASK (0xf << QH_EP_SHIFT) +#define QH_DEVADDR_SHIFT 0 +#define QH_DEVADDR_MASK (0x7f << QH_DEVADDR_SHIFT) + +#define QH_SMASK_SHIFT 0 +#define QH_SMASK_MASK (0xff << QH_SMASK_SHIFT) +#define QH_CMASK_SHIFT 8 +#define QH_CMASK_MASK (0xff << QH_CMASK_SHIFT) +#define QH_HUBADDR_SHIFT 16 +#define QH_HUBADDR_MASK (0x7f << QH_HUBADDR_SHIFT) +#define QH_HUBPORT_SHIFT 23 +#define QH_HUBPORT_MASK (0x7f << QH_HUBPORT_SHIFT) +#define QH_MULT_SHIFT 30 +#define QH_MULT_MASK (0x3 << QH_MULT_SHIFT) + +#define EHCI_PTR_BITS 0x001F +#define EHCI_PTR_TERM 0x0001 +#define EHCI_PTR_QH 0x0002 + + +#define EHCI_QTD_ALIGN 64 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qtd { + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; + /* keep struct size a multiple of 64 bytes, as we're allocating + arrays. Without this padding, the second qtd could have the + wrong alignment. */ +} PACKED __aligned(EHCI_QTD_ALIGN); + +#define QTD_TOGGLE (1 << 31) +#define QTD_LENGTH_SHIFT 16 +#define QTD_LENGTH_MASK (0x7fff << QTD_LENGTH_SHIFT) +#define QTD_CERR_SHIFT 10 +#define QTD_CERR_MASK (0x3 << QTD_CERR_SHIFT) +#define QTD_IOC (1 << 15) +#define QTD_PID_OUT (0x0 << 8) +#define QTD_PID_IN (0x1 << 8) +#define QTD_PID_SETUP (0x2 << 8) +#define QTD_STS_ACTIVE (1 << 7) +#define QTD_STS_HALT (1 << 6) +#define QTD_STS_DBE (1 << 5) +#define QTD_STS_BABBLE (1 << 4) +#define QTD_STS_XACT (1 << 3) +#define QTD_STS_MMF (1 << 2) +#define QTD_STS_STS (1 << 1) +#define QTD_STS_PING (1 << 0) + +#define ehci_explen(len) (((len) << QTD_LENGTH_SHIFT) & QTD_LENGTH_MASK) + +#define ehci_maxerr(err) (((err) << QTD_CERR_SHIFT) & QTD_CERR_MASK) + + +struct ehci_framelist { + u32 links[1024]; +} PACKED; + +#endif // usb-ehci.h diff --git a/qemu/roms/seabios/src/hw/usb-hid.c b/qemu/roms/seabios/src/hw/usb-hid.c new file mode 100644 index 000000000..09a6cf455 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hid.c @@ -0,0 +1,432 @@ +// Code for handling USB Human Interface Devices (HID). +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "ps2port.h" // ATKBD_CMD_GETID +#include "usb.h" // usb_ctrlrequest +#include "usb-hid.h" // usb_keyboard_setup +#include "util.h" // process_key + +struct usb_pipe *keyboard_pipe VARFSEG; +struct usb_pipe *mouse_pipe VARFSEG; + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Send USB HID protocol message. +static int +set_protocol(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_PROTOCOL; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +// Send USB HID SetIdle request. +static int +set_idle(struct usb_pipe *pipe, int ms) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_IDLE; + req.wValue = (ms/4)<<8; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +#define KEYREPEATWAITMS 500 +#define KEYREPEATMS 33 + +static int +usb_kbd_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + if (keyboard_pipe) + // XXX - this enables the first found keyboard (could be random) + return -1; + + if (epdesc->wMaxPacketSize != 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + // Periodically send reports to enable key repeat. + ret = set_idle(usbdev->defpipe, KEYREPEATMS); + if (ret) + return -1; + + keyboard_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!keyboard_pipe) + return -1; + + dprintf(1, "USB keyboard initialized\n"); + return 0; +} + +static int +usb_mouse_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_MOUSE) + return -1; + if (mouse_pipe) + // XXX - this enables the first found mouse (could be random) + return -1; + + if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + + mouse_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!mouse_pipe) + return -1; + + dprintf(1, "USB mouse initialized\n"); + return 0; +} + +// Initialize a found USB HID device (if applicable). +int +usb_hid_setup(struct usbdevice_s *usbdev) +{ + if (! CONFIG_USB_KEYBOARD && ! CONFIG_USB_MOUSE) + return -1; + dprintf(2, "usb_hid_setup %p\n", usbdev->defpipe); + + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT) + // Doesn't support boot protocol. + return -1; + + // Find intr in endpoint. + struct usb_endpoint_descriptor *epdesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_INT, USB_DIR_IN); + if (!epdesc) { + dprintf(1, "No usb hid intr in?\n"); + return -1; + } + + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD) + return usb_kbd_setup(usbdev, epdesc); + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return usb_mouse_setup(usbdev, epdesc); + return -1; +} + + +/**************************************************************** + * Keyboard events + ****************************************************************/ + +// Mapping from USB key id to ps2 key sequence. +static u16 KeyToScanCode[] VAR16 = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020, + 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, + 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, + 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, + 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a, + 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034, + 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, + 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046, + 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d, + 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e, + 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047, + 0x0048, 0x0049, 0x0052, 0x0053 +}; + +// Mapping from USB modifier id to ps2 key sequence. +static u16 ModifierToScanCode[] VAR16 = { + //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui + 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c +}; + +#define RELEASEBIT 0x80 + +// Format of USB keyboard event data +struct keyevent { + u8 modifiers; + u8 reserved; + u8 keys[6]; +}; + +// Translate data from KeyToScanCode[] to calls to process_key(). +static void +prockeys(u16 keys) +{ + if (keys > 0xff) { + u8 key = keys>>8; + if (key == 0xe1) { + // Pause key + process_key(0xe1); + process_key(0x1d | (keys & RELEASEBIT)); + process_key(0x45 | (keys & RELEASEBIT)); + return; + } + process_key(key); + } + process_key(keys); +} + +// Handle a USB key press/release event. +static void +procscankey(u8 key, u8 flags) +{ + if (key >= ARRAY_SIZE(KeyToScanCode)) + return; + u16 keys = GET_GLOBAL(KeyToScanCode[key]); + if (keys) + prockeys(keys | flags); +} + +// Handle a USB modifier press/release event. +static void +procmodkey(u8 mods, u8 flags) +{ + int i; + for (i=0; mods; i++) + if (mods & (1<<i)) { + // Modifier key change. + prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags); + mods &= ~(1<<i); + } +} + +struct usbkeyinfo { + union { + struct { + u8 modifiers; + u8 repeatcount; + u8 keys[6]; + }; + u64 data; + }; +}; +struct usbkeyinfo LastUSBkey VARLOW; + +// Process USB keyboard data. +static void +handle_key(struct keyevent *data) +{ + dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]); + + // Load old keys. + struct usbkeyinfo old; + old.data = GET_LOW(LastUSBkey.data); + + // Check for keys no longer pressed. + int addpos = 0; + int i; + for (i=0; i<ARRAY_SIZE(old.keys); i++) { + u8 key = old.keys[i]; + if (!key) + break; + int j; + for (j=0;; j++) { + if (j>=ARRAY_SIZE(data->keys)) { + // Key released. + procscankey(key, RELEASEBIT); + if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1]) + // Last pressed key released - disable repeat. + old.repeatcount = 0xff; + break; + } + if (data->keys[j] == key) { + // Key still pressed. + data->keys[j] = 0; + old.keys[addpos++] = key; + break; + } + } + } + procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT); + + // Process new keys + procmodkey(data->modifiers & ~old.modifiers, 0); + old.modifiers = data->modifiers; + for (i=0; i<ARRAY_SIZE(data->keys); i++) { + u8 key = data->keys[i]; + if (!key) + continue; + // New key pressed. + procscankey(key, 0); + old.keys[addpos++] = key; + old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1; + } + if (addpos < ARRAY_SIZE(old.keys)) + old.keys[addpos] = 0; + + // Check for key repeat event. + if (addpos) { + if (!old.repeatcount) + procscankey(old.keys[addpos-1], 0); + else if (old.repeatcount != 0xff) + old.repeatcount--; + } + + // Update old keys + SET_LOW(LastUSBkey.data, old.data); +} + +// Check if a USB keyboard event is pending and process it if so. +static void +usb_check_key(void) +{ + if (! CONFIG_USB_KEYBOARD) + return; + struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe); + if (!pipe) + return; + + for (;;) { + struct keyevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_key(&data); + } +} + +// Test if USB keyboard is active. +inline int +usb_kbd_active(void) +{ + if (! CONFIG_USB_KEYBOARD) + return 0; + return GET_GLOBAL(keyboard_pipe) != NULL; +} + +// Handle a ps2 style keyboard command. +inline int +usb_kbd_command(int command, u8 *param) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + dprintf(9, "usb keyboard cmd=%x\n", command); + switch (command) { + case ATKBD_CMD_GETID: + // Return the id of a standard AT keyboard. + param[0] = 0xab; + param[1] = 0x83; + return 0; + default: + return -1; + } +} + + +/**************************************************************** + * Mouse events + ****************************************************************/ + +// Format of USB mouse event data +struct mouseevent { + u8 buttons; + u8 x, y; + u8 reserved[5]; +}; + +// Process USB mouse data. +static void +handle_mouse(struct mouseevent *data) +{ + dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y); + + s8 x = data->x, y = -data->y; + u8 flag = ((data->buttons & 0x7) | (1<<3) + | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0)); + process_mouse(flag); + process_mouse(x); + process_mouse(y); +} + +// Check if a USB mouse event is pending and process it if so. +static void +usb_check_mouse(void) +{ + if (! CONFIG_USB_MOUSE) + return; + struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe); + if (!pipe) + return; + + for (;;) { + struct mouseevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_mouse(&data); + } +} + +// Test if USB mouse is active. +inline int +usb_mouse_active(void) +{ + if (! CONFIG_USB_MOUSE) + return 0; + return GET_GLOBAL(mouse_pipe) != NULL; +} + +// Handle a ps2 style mouse command. +inline int +usb_mouse_command(int command, u8 *param) +{ + if (! CONFIG_USB_MOUSE) + return -1; + dprintf(9, "usb mouse cmd=%x\n", command); + switch (command) { + case PSMOUSE_CMD_ENABLE: + case PSMOUSE_CMD_DISABLE: + case PSMOUSE_CMD_SETSCALE11: + return 0; + case PSMOUSE_CMD_SETSCALE21: + case PSMOUSE_CMD_SETRATE: + case PSMOUSE_CMD_SETRES: + // XXX + return 0; + case PSMOUSE_CMD_RESET_BAT: + case PSMOUSE_CMD_GETID: + // Return the id of a standard AT mouse. + param[0] = 0xaa; + param[1] = 0x00; + return 0; + + case PSMOUSE_CMD_GETINFO: + param[0] = 0x00; + param[1] = 4; + param[2] = 100; + return 0; + + default: + return -1; + } +} + +// Check for USB events pending - called periodically from timer interrupt. +void +usb_check_event(void) +{ + usb_check_key(); + usb_check_mouse(); +} diff --git a/qemu/roms/seabios/src/hw/usb-hid.h b/qemu/roms/seabios/src/hw/usb-hid.h new file mode 100644 index 000000000..ef34e7963 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hid.h @@ -0,0 +1,29 @@ +#ifndef __USB_HID_H +#define __USB_HID_H + +// usb-hid.c +struct usbdevice_s; +int usb_hid_setup(struct usbdevice_s *usbdev); +inline int usb_kbd_active(void); +inline int usb_kbd_command(int command, u8 *param); +inline int usb_mouse_active(void); +inline int usb_mouse_command(int command, u8 *param); +void usb_check_event(void); + + +/**************************************************************** + * hid flags + ****************************************************************/ + +#define USB_INTERFACE_SUBCLASS_BOOT 1 +#define USB_INTERFACE_PROTOCOL_KEYBOARD 1 +#define USB_INTERFACE_PROTOCOL_MOUSE 2 + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +#endif // ush-hid.h diff --git a/qemu/roms/seabios/src/hw/usb-hub.c b/qemu/roms/seabios/src/hw/usb-hub.c new file mode 100644 index 000000000..54e341b73 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hub.c @@ -0,0 +1,205 @@ +// Code for handling standard USB hubs. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_USB_HUB +#include "output.h" // dprintf +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-hub.h" // struct usb_hub_descriptor +#include "util.h" // timer_calc + +static int +get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + if (pipe->speed == USB_SUPERSPEED) + req.wValue = USB_DT_HUB3<<8; + else + req.wValue = USB_DT_HUB<<8; + req.wIndex = 0; + req.wLength = sizeof(*desc); + return usb_send_default_control(pipe, &req, desc); +} + +static int +set_hub_depth(struct usb_pipe *pipe, u16 depth) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = HUB_REQ_SET_HUB_DEPTH; + req.wValue = depth; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + +static int +set_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_SET_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +clear_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_CLEAR_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = port + 1; + req.wLength = sizeof(*sts); + mutex_lock(&hub->lock); + int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts); + mutex_unlock(&hub->lock); + return ret; +} + +// Check if device attached to port +static int +usb_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_port_status sts; + int ret = get_port_status(hub, port, &sts); + if (ret) { + dprintf(1, "Failure on hub port %d detect\n", port); + return -1; + } + return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0; +} + +// Disable port +static void +usb_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); + if (ret) + dprintf(1, "Failure on hub port %d disconnect\n", port); +} + +// Reset device on port +static int +usb_hub_reset(struct usbhub_s *hub, u32 port) +{ + int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); + if (ret) + goto fail; + + // Wait for reset to complete. + struct usb_port_status sts; + u32 end = timer_calc(USB_TIME_DRST * 2); + for (;;) { + ret = get_port_status(hub, port, &sts); + if (ret) + goto fail; + if (!(sts.wPortStatus & USB_PORT_STAT_RESET) + && (hub->usbdev->speed != USB_SUPERSPEED + || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK))) + break; + if (timer_check(end)) { + warn_timeout(); + goto fail; + } + msleep(5); + } + + // Reset complete. + if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) + // Device no longer present + return -1; + + if (hub->usbdev->speed == USB_SUPERSPEED) + return USB_SUPERSPEED; + return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) + >> USB_PORT_STAT_SPEED_SHIFT); + +fail: + dprintf(1, "Failure on hub port %d reset\n", port); + usb_hub_disconnect(hub, port); + return -1; +} + +static struct usbhub_op_s HubOp = { + .detect = usb_hub_detect, + .reset = usb_hub_reset, + .disconnect = usb_hub_disconnect, +}; + +// Configure a usb hub and then find devices connected to it. +int +usb_hub_setup(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_HUB) + return -1; + + struct usb_hub_descriptor desc; + int ret = get_hub_desc(usbdev->defpipe, &desc); + if (ret) + return ret; + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.usbdev = usbdev; + hub.cntl = usbdev->defpipe->cntl; + hub.portcount = desc.bNbrPorts; + hub.op = &HubOp; + + if (usbdev->speed == USB_SUPERSPEED) { + int depth = 0; + struct usbdevice_s *parent = usbdev->hub->usbdev; + while (parent) { + depth++; + parent = parent->hub->usbdev; + } + + ret = set_hub_depth(usbdev->defpipe, depth); + if (ret) + return ret; + } + + // Turn on power to ports. + int port; + for (port=0; port<desc.bNbrPorts; port++) { + ret = set_port_feature(&hub, port, USB_PORT_FEAT_POWER); + if (ret) + return ret; + } + // Wait for port power to stabilize. + msleep(desc.bPwrOn2PwrGood * 2); + + usb_enumerate(&hub); + + dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount); + if (hub.devcount) + return 0; + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-hub.h b/qemu/roms/seabios/src/hw/usb-hub.h new file mode 100644 index 000000000..880378ca1 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-hub.h @@ -0,0 +1,64 @@ +#ifndef __USB_HUB_H +#define __USB_HUB_H + +// usb-hub.c +struct usbdevice_s; +int usb_hub_setup(struct usbdevice_s *usbdev); + + +/**************************************************************** + * hub flags + ****************************************************************/ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +#define USB_DT_HUB3 (USB_TYPE_CLASS | 0x0a) + +#define HUB_REQ_SET_HUB_DEPTH 0x0C + +struct usb_hub_descriptor { + u8 bDescLength; + u8 bDescriptorType; + u8 bNbrPorts; + u16 wHubCharacteristics; + u8 bPwrOn2PwrGood; + u8 bHubContrCurrent; + // Variable length fields for DeviceRemovable[], PortPwrCtrlMask[] follow. +} PACKED; + +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 +#define USB_PORT_FEAT_C_PORT_L1 23 + +struct usb_port_status { + u16 wPortStatus; + u16 wPortChange; +} PACKED; + +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_LINK_SHIFT 5 +#define USB_PORT_STAT_LINK_MASK (0x7 << USB_PORT_STAT_LINK_SHIFT) +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_SPEED_SHIFT 9 +#define USB_PORT_STAT_SPEED_MASK (0x3 << USB_PORT_STAT_SPEED_SHIFT) +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 + +#endif // ush-hid.h diff --git a/qemu/roms/seabios/src/hw/usb-msc.c b/qemu/roms/seabios/src/hw/usb-msc.c new file mode 100644 index 000000000..d90319f51 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-msc.c @@ -0,0 +1,220 @@ +// Code for handling USB Mass Storage Controller devices. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_MSC +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-msc.h" // usb_msc_setup +#include "util.h" // bootprio_find_usb + +struct usbdrive_s { + struct drive_s drive; + struct usb_pipe *bulkin, *bulkout; + int lun; +}; + + +/**************************************************************** + * Bulk-only drive command processing + ****************************************************************/ + +#define USB_CDB_SIZE 12 + +#define CBW_SIGNATURE 0x43425355 // USBC + +struct cbw_s { + u32 dCBWSignature; + u32 dCBWTag; + u32 dCBWDataTransferLength; + u8 bmCBWFlags; + u8 bCBWLUN; + u8 bCBWCBLength; + u8 CBWCB[16]; +} PACKED; + +#define CSW_SIGNATURE 0x53425355 // USBS + +struct csw_s { + u32 dCSWSignature; + u32 dCSWTag; + u32 dCSWDataResidue; + u8 bCSWStatus; +} PACKED; + +static int +usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes) +{ + struct usb_pipe *pipe; + if (dir == USB_DIR_OUT) + pipe = GET_GLOBALFLAT(udrive_gf->bulkout); + else + pipe = GET_GLOBALFLAT(udrive_gf->bulkin); + return usb_send_bulk(pipe, dir, buf, bytes); +} + +// Low-level usb command transmit function. +int +usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_MSC) + return 0; + + dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, 0, op->count, blocksize, op->buf_fl); + struct usbdrive_s *udrive_gf = container_of( + op->drive_gf, struct usbdrive_s, drive); + + // Setup command block wrapper. + u32 bytes = blocksize * op->count; + struct cbw_s cbw; + memset(&cbw, 0, sizeof(cbw)); + memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE); + cbw.dCBWSignature = CBW_SIGNATURE; + cbw.dCBWTag = 999; // XXX + cbw.dCBWDataTransferLength = bytes; + cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT; + cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun); + cbw.bCBWCBLength = USB_CDB_SIZE; + + // Transfer cbw to device. + int ret = usb_msc_send(udrive_gf, USB_DIR_OUT + , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw)); + if (ret) + goto fail; + + // Transfer data to/from device. + if (bytes) { + ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes); + if (ret) + goto fail; + } + + // Transfer csw info. + struct csw_s csw; + ret = usb_msc_send(udrive_gf, USB_DIR_IN + , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw)); + if (ret) + goto fail; + + if (!csw.bCSWStatus) + return DISK_RET_SUCCESS; + if (csw.bCSWStatus == 2) + goto fail; + + if (blocksize) + op->count -= csw.dCSWDataResidue / blocksize; + return DISK_RET_EBADTRACK; + +fail: + // XXX - reset connection + dprintf(1, "USB transmission failed\n"); + return DISK_RET_EBADTRACK; +} + +static int +usb_msc_maxlun(struct usb_pipe *pipe) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = 0xfe; + req.wValue = 0; + req.wIndex = 0; + req.wLength = 1; + unsigned char maxlun; + int ret = usb_send_default_control(pipe, &req, &maxlun); + if (ret) + return 0; + return maxlun; +} + +static int +usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe, + struct usbdevice_s *usbdev, int lun) +{ + // Allocate drive structure. + struct usbdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + if (usb_32bit_pipe(inpipe)) + drive->drive.type = DTYPE_USB_32; + else + drive->drive.type = DTYPE_USB; + drive->bulkin = inpipe; + drive->bulkout = outpipe; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB MSC", prio); + if (ret) { + dprintf(1, "Unable to configure USB MSC drive.\n"); + free(drive); + return -1; + } + return 0; +} + +/**************************************************************** + * Setup + ****************************************************************/ + +// Configure a usb msc device. +int +usb_msc_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_MSC) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if ((iface->bInterfaceSubClass != US_SC_SCSI && + iface->bInterfaceSubClass != US_SC_ATAPI_8070 && + iface->bInterfaceSubClass != US_SC_ATAPI_8020) + || iface->bInterfaceProtocol != US_PR_BULK) { + dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + // Find bulk in and bulk out endpoints. + struct usb_pipe *inpipe = NULL, *outpipe = NULL; + struct usb_endpoint_descriptor *indesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); + struct usb_endpoint_descriptor *outdesc = usb_find_desc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); + if (!indesc || !outdesc) + goto fail; + inpipe = usb_alloc_pipe(usbdev, indesc); + outpipe = usb_alloc_pipe(usbdev, outdesc); + if (!inpipe || !outpipe) + goto fail; + + int maxlun = usb_msc_maxlun(usbdev->defpipe); + int lun, pipesused = 0; + for (lun = 0; lun < maxlun + 1; lun++) { + int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun); + if (!ret) + pipesused = 1; + } + + if (!pipesused) + goto fail; + + return 0; +fail: + dprintf(1, "Unable to configure USB MSC device.\n"); + usb_free_pipe(usbdev, inpipe); + usb_free_pipe(usbdev, outpipe); + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-msc.h b/qemu/roms/seabios/src/hw/usb-msc.h new file mode 100644 index 000000000..c40d75556 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-msc.h @@ -0,0 +1,10 @@ +#ifndef __USB_MSC_H +#define __USB_MSC_H + +// usb-msc.c +struct disk_op_s; +int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_msc_setup(struct usbdevice_s *usbdev); + +#endif // ush-msc.h diff --git a/qemu/roms/seabios/src/hw/usb-ohci.c b/qemu/roms/seabios/src/hw/usb-ohci.c new file mode 100644 index 000000000..42f8a0681 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ohci.c @@ -0,0 +1,569 @@ +// Code for handling OHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "memmap.h" // PAGE_SIZE +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ohci.h" // struct ohci_hcca +#include "util.h" // msleep +#include "x86.h" // readl + +#define FIT (1 << 31) + +struct usb_ohci_s { + struct usb_s usb; + struct ohci_regs *regs; +}; + +struct ohci_pipe { + struct ohci_ed ed; + struct usb_pipe pipe; + struct ohci_regs *regs; + void *data; + int count; + struct ohci_td *tds; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to port +static int +ohci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + u32 sts = readl(&cntl->regs->roothub_portstatus[port]); + return (sts & RH_PS_CCS) ? 1 : 0; +} + +// Disable port +static void +ohci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); +} + +// Reset device on port +static int +ohci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); + u32 sts; + u32 end = timer_calc(USB_TIME_DRSTR * 2); + for (;;) { + sts = readl(&cntl->regs->roothub_portstatus[port]); + if (!(sts & RH_PS_PRS)) + // XXX - need to ensure USB_TIME_DRSTR time in reset? + break; + if (timer_check(end)) { + // Timeout. + warn_timeout(); + ohci_hub_disconnect(hub, port); + return -1; + } + yield(); + } + + if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES)) + // Device no longer present + return -1; + + return !!(sts & RH_PS_LSDA); +} + +static struct usbhub_op_s ohci_HubOp = { + .detect = ohci_hub_detect, + .reset = ohci_hub_reset, + .disconnect = ohci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ohci_ports(struct usb_ohci_s *cntl) +{ + ASSERT32FLAT(); + // Turn on power for all devices on roothub. + u32 rha = readl(&cntl->regs->roothub_a); + rha &= ~(RH_A_PSM | RH_A_OCPM); + writel(&cntl->regs->roothub_status, RH_HS_LPSC); + writel(&cntl->regs->roothub_b, RH_B_PPCM); + msleep((rha >> 24) * 2); + // XXX - need to sleep for USB_TIME_SIGATT if just powered up? + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = rha & RH_A_NDP; + hub.op = &ohci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +ohci_waittick(struct ohci_regs *regs) +{ + barrier(); + struct ohci_hcca *hcca = (void*)regs->hcca; + u32 startframe = hcca->frame_no; + u32 end = timer_calc(1000 * 5); + for (;;) { + if (hcca->frame_no != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +ohci_free_pipes(struct usb_ohci_s *cntl) +{ + dprintf(7, "ohci_free_pipes %p\n", cntl); + + u32 creg = readl(&cntl->regs->control); + if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { + writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); + ohci_waittick(cntl->regs); + } + + u32 *pos = &cntl->regs->ed_controlhead; + for (;;) { + struct ohci_ed *next = (void*)*pos; + if (!next) + break; + struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) { + *pos = next->hwNextED; + free(pipe); + } else { + pos = &next->hwNextED; + } + } + + writel(&cntl->regs->ed_controlcurrent, 0); + writel(&cntl->regs->ed_bulkcurrent, 0); + writel(&cntl->regs->control, creg); + cntl->usb.freelist = NULL; +} + +static int +start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) +{ + u32 oldfminterval = readl(&cntl->regs->fminterval); + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + + // XXX - check if already running? + + // Do reset + writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc); + readl(&cntl->regs->control); // flush writes + msleep(USB_TIME_DRSTR); + + // Do software init (min 10us, max 2ms) + u32 end = timer_calc_usec(10); + writel(&cntl->regs->cmdstatus, OHCI_HCR); + for (;;) { + u32 status = readl(&cntl->regs->cmdstatus); + if (! status & OHCI_HCR) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + } + + // Init memory + writel(&cntl->regs->ed_controlhead, 0); + writel(&cntl->regs->ed_bulkhead, 0); + writel(&cntl->regs->hcca, (u32)hcca); + + // Init fminterval + u32 fi = oldfminterval & 0x3fff; + writel(&cntl->regs->fminterval + , (((oldfminterval & FIT) ^ FIT) + | fi | (((6 * (fi - 210)) / 7) << 16))); + writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff); + readl(&cntl->regs->control); // flush writes + + // XXX - verify that fminterval was setup correctly. + + // Go into operational state + writel(&cntl->regs->control + , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_BLE | OHCI_CTRL_PLE + | OHCI_USB_OPER | oldrwc)); + readl(&cntl->regs->control); // flush writes + + return 0; +} + +static void +stop_ohci(struct usb_ohci_s *cntl) +{ + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + writel(&cntl->regs->control, oldrwc); + readl(&cntl->regs->control); // flush writes +} + +static void +configure_ohci(void *data) +{ + struct usb_ohci_s *cntl = data; + + // Allocate memory + struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca)); + struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed)); + if (!hcca || !intr_ed) { + warn_noalloc(); + goto free; + } + memset(hcca, 0, sizeof(*hcca)); + memset(intr_ed, 0, sizeof(*intr_ed)); + intr_ed->hwINFO = ED_SKIP; + int i; + for (i=0; i<ARRAY_SIZE(hcca->int_table); i++) + hcca->int_table[i] = (u32)intr_ed; + + int ret = start_ohci(cntl, hcca); + if (ret) + goto err; + + int count = check_ohci_ports(cntl); + ohci_free_pipes(cntl); + if (! count) + goto err; + return; + +err: + stop_ohci(cntl); +free: + free(hcca); + free(intr_ed); +} + +static void +ohci_controller_setup(struct pci_device *pci) +{ + struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_OHCI; + + wait_preempt(); // Avoid pci_config_readl when preempting + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + + dprintf(1, "OHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + // Enable bus mastering and memory access. + pci_config_maskw(bdf, PCI_COMMAND + , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); + + // XXX - check for and disable SMM control? + + // Disable interrupts + writel(&cntl->regs->intrdisable, ~0); + writel(&cntl->regs->intrstatus, ~0); + + run_thread(configure_ohci, cntl); +} + +void +ohci_setup(void) +{ + if (! CONFIG_USB_OHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI) + ohci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in ed +static void +ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7) + | (epdesc->wMaxPacketSize << 16) + | (usbdev->speed ? ED_LOWSPEED : 0)); + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + pipe->regs = cntl->regs; +} + +static struct usb_pipe * +ohci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 5) + frameexp = 5; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms) + 1; + struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct ohci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) + goto err; + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + pipe->ed.hwINFO &= ~ED_SKIP; + pipe->data = data; + pipe->count = count; + pipe->tds = tds; + + struct ohci_ed *ed = &pipe->ed; + ed->hwHeadP = (u32)&tds[0]; + ed->hwTailP = (u32)&tds[count-1]; + + int i; + for (i=0; i<count-1; i++) { + tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC; + tds[i].hwCBP = (u32)data + maxpacket * i; + tds[i].hwNextTD = (u32)&tds[i+1]; + tds[i].hwBE = tds[i].hwCBP + maxpacket - 1; + } + + // Add to interrupt schedule. + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ohci_ed *intr_ed = (void*)hcca->int_table[0]; + ed->hwNextED = intr_ed->hwNextED; + barrier(); + intr_ed->hwNextED = (u32)ed; + } else { + int startpos = 1<<(frameexp-1); + ed->hwNextED = hcca->int_table[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(hcca->int_table); i+=ms) + hcca->int_table[i] = (u32)ed; + } + + return &pipe->pipe; + +err: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_OHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ohci_alloc_intr_pipe(usbdev, epdesc); + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ohci_pipe *pipe = container_of(usbpipe, struct ohci_pipe, pipe); + ohci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ohci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + + // Add queue head to controller list. + u32 *head = &cntl->regs->ed_controlhead; + if (eptype != USB_ENDPOINT_XFER_CONTROL) + head = &cntl->regs->ed_bulkhead; + pipe->ed.hwNextED = *head; + barrier(); + *head = (u32)&pipe->ed; + return &pipe->pipe; +} + +static int +wait_ed(struct ohci_ed *ed, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + if ((ed->hwHeadP & ~(ED_C|ED_H)) == ed->hwTailP) + return 0; + if (timer_check(end)) { + warn_timeout(); + dprintf(1, "ohci ed info=%x tail=%x head=%x next=%x\n" + , ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED); + return -1; + } + yield(); + } +} + +#define STACKOTDS 18 +#define OHCI_TD_ALIGN 16 + +int +ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + ASSERT32FLAT(); + if (! CONFIG_USB_OHCI) + return -1; + dprintf(7, "ohci_send_pipe %p\n", p); + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + + // Allocate tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ohci_td) * STACKOTDS + OHCI_TD_ALIGN - 1]; + struct ohci_td *tds = (void*)ALIGN((u32)tdsbuf, OHCI_TD_ALIGN), *td = tds; + memset(tds, 0, sizeof(*tds) * STACKOTDS); + + // Setup transfer descriptors + u16 maxpacket = pipe->pipe.maxpacket; + u32 toggle = 0, statuscmd = OHCI_BLF; + if (cmd) { + // Send setup pid on control transfers + td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + td->hwCBP = (u32)cmd; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)cmd + USB_CONTROL_SETUP_SIZE - 1; + td++; + toggle = TD_T_DATA1; + statuscmd = OHCI_CLF; + } + u32 dest = (u32)data, dataend = dest + datasize; + while (dest < dataend) { + // Send data pids + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + int maxtransfer = 2*PAGE_SIZE - (dest & (PAGE_SIZE-1)); + int transfer = dataend - dest; + if (transfer > maxtransfer) + transfer = ALIGN_DOWN(maxtransfer, maxpacket); + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | toggle | TD_CC; + td->hwCBP = dest; + td->hwNextTD = (u32)&td[1]; + td->hwBE = dest + transfer - 1; + td++; + dest += transfer; + } + if (cmd) { + // Send status pid on control transfers + if (td >= &tds[STACKOTDS]) { + warn_noalloc(); + return -1; + } + td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + td->hwCBP = 0; + td->hwNextTD = (u32)&td[1]; + td->hwBE = 0; + td++; + } + + // Transfer data + pipe->ed.hwHeadP = (u32)tds | (pipe->ed.hwHeadP & ED_C); + pipe->ed.hwTailP = (u32)td; + barrier(); + pipe->ed.hwINFO &= ~ED_SKIP; + writel(&pipe->regs->cmdstatus, statuscmd); + + int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize)); + pipe->ed.hwINFO |= ED_SKIP; + if (ret) + ohci_waittick(pipe->regs); + return ret; +} + +int +ohci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_OHCI) + return -1; + + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + struct ohci_td *tds = GET_LOWFLAT(pipe->tds); + struct ohci_td *head = (void*)(GET_LOWFLAT(pipe->ed.hwHeadP) & ~(ED_C|ED_H)); + struct ohci_td *tail = (void*)GET_LOWFLAT(pipe->ed.hwTailP); + int count = GET_LOWFLAT(pipe->count); + int pos = (tail - tds + 1) % count; + struct ohci_td *next = &tds[pos]; + if (head == next) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + void *pipedata = GET_LOWFLAT((pipe->data)); + void *intrdata = pipedata + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(intrdata), maxpacket); + + // Reenable this td. + SET_LOWFLAT(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC); + intrdata = pipedata + maxpacket * (tail-tds); + SET_LOWFLAT(tail->hwCBP, (u32)intrdata); + SET_LOWFLAT(tail->hwNextTD, (u32)next); + SET_LOWFLAT(tail->hwBE, (u32)intrdata + maxpacket - 1); + barrier(); + SET_LOWFLAT(pipe->ed.hwTailP, (u32)next); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-ohci.h b/qemu/roms/seabios/src/hw/usb-ohci.h new file mode 100644 index 000000000..5a275a334 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-ohci.h @@ -0,0 +1,144 @@ +#ifndef __USB_OHCI_H +#define __USB_OHCI_H + +// usb-ohci.c +void ohci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *ohci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int ohci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int ohci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ohci structs and flags + ****************************************************************/ + +struct ohci_ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; +} PACKED; + +#define ED_ISO (1 << 15) +#define ED_SKIP (1 << 14) +#define ED_LOWSPEED (1 << 13) +#define ED_OUT (0x01 << 11) +#define ED_IN (0x02 << 11) + +#define ED_C (0x02) +#define ED_H (0x01) + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; + u32 hwNextTD; + u32 hwBE; +} PACKED; + +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_DI 0x00E00000 + +#define TD_DONE 0x00020000 +#define TD_ISO 0x00010000 + +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_R 0x00040000 + +struct ohci_hcca { + u32 int_table[32]; + u32 frame_no; + u32 done_head; + u8 reserved[120]; +} PACKED; + +struct ohci_regs { + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + + u32 roothub_a; + u32 roothub_b; + u32 roothub_status; + u32 roothub_portstatus[15]; +} PACKED; + +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_BLE (1 << 5) +#define OHCI_CTRL_HCFS (3 << 6) +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_OPER (2 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +#define OHCI_HCR (1 << 0) +#define OHCI_CLF (1 << 1) +#define OHCI_BLF (1 << 2) + +#define OHCI_INTR_MIE (1 << 31) + +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + +#define RH_HS_LPS 0x00000001 +#define RH_HS_OCI 0x00000002 +#define RH_HS_DRWE 0x00008000 +#define RH_HS_LPSC 0x00010000 +#define RH_HS_OCIC 0x00020000 +#define RH_HS_CRWE 0x80000000 + +#define RH_B_DR 0x0000ffff +#define RH_B_PPCM 0xffff0000 + +#define RH_A_NDP (0xff << 0) +#define RH_A_PSM (1 << 8) +#define RH_A_NPS (1 << 9) +#define RH_A_DT (1 << 10) +#define RH_A_OCPM (1 << 11) +#define RH_A_NOCP (1 << 12) +#define RH_A_POTPGT (0xff << 24) + +#endif // usb-ohci.h diff --git a/qemu/roms/seabios/src/hw/usb-uas.c b/qemu/roms/seabios/src/hw/usb-uas.c new file mode 100644 index 000000000..6ef8d0912 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uas.c @@ -0,0 +1,274 @@ +// Code for handling usb attached scsi devices. +// +// only usb 2.0 for now. +// +// once we have xhci driver with usb 3.0 support this must +// be updated to use usb3 streams so booting from usb3 +// devices actually works. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on usb-msc.c which is written by: +// Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_UAS +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uas.h" // usb_uas_init +#include "util.h" // bootprio_find_usb + +#define UAS_UI_COMMAND 0x01 +#define UAS_UI_SENSE 0x03 +#define UAS_UI_RESPONSE 0x04 +#define UAS_UI_TASK_MGMT 0x05 +#define UAS_UI_READ_READY 0x06 +#define UAS_UI_WRITE_READY 0x07 + +#define UAS_PIPE_ID_COMMAND 0x01 +#define UAS_PIPE_ID_STATUS 0x02 +#define UAS_PIPE_ID_DATA_IN 0x03 +#define UAS_PIPE_ID_DATA_OUT 0x04 + +typedef struct { + u8 id; + u8 reserved; + u16 tag; +} PACKED uas_ui_header; + +typedef struct { + u8 prio_taskattr; /* 6:3 priority, 2:0 task attribute */ + u8 reserved_1; + u8 add_cdb_length; /* 7:2 additional adb length (dwords) */ + u8 reserved_2; + u8 lun[8]; + u8 cdb[16]; + u8 add_cdb[]; +} PACKED uas_ui_command; + +typedef struct { + u16 status_qualifier; + u8 status; + u8 reserved[7]; + u16 sense_length; + u8 sense_data[18]; +} PACKED uas_ui_sense; + +typedef struct { + u16 add_response_info; + u8 response_code; +} PACKED uas_ui_response; + +typedef struct { + u8 function; + u8 reserved; + u16 task_tag; + u8 lun[8]; +} PACKED uas_ui_task_mgmt; + +typedef struct { + uas_ui_header hdr; + union { + uas_ui_command command; + uas_ui_sense sense; + uas_ui_task_mgmt task; + uas_ui_response response; + }; +} PACKED uas_ui; + +struct uasdrive_s { + struct drive_s drive; + struct usb_pipe *command, *status, *data_in, *data_out; + int lun; +}; + +int +uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_UAS) + return DISK_RET_EBADTRACK; + + struct uasdrive_s *drive_gf = container_of( + op->drive_gf, struct uasdrive_s, drive); + + uas_ui ui; + memset(&ui, 0, sizeof(ui)); + ui.hdr.id = UAS_UI_COMMAND; + ui.hdr.tag = 0xdead; + ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun); + memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb)); + int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command), + USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui), + sizeof(ui.hdr) + sizeof(ui.command)); + if (ret) { + dprintf(1, "uas: command send fail"); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + + switch (ui.hdr.id) { + case UAS_UI_SENSE: + goto have_sense; + case UAS_UI_READ_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in), + USB_DIR_IN, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data read fail"); + goto fail; + } + break; + case UAS_UI_WRITE_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out), + USB_DIR_OUT, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data write fail"); + goto fail; + } + break; + default: + dprintf(1, "uas: unknown status ui id %d", ui.hdr.id); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + if (ui.hdr.id != UAS_UI_SENSE) { + dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id); + goto fail; + } + +have_sense: + if (ui.sense.status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +static int +uas_lun_setup(struct usbdevice_s *usbdev, + struct usb_pipe *command, struct usb_pipe *status, + struct usb_pipe *data_in, struct usb_pipe *data_out, + int lun) +{ + // Allocate drive structure. + struct uasdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + if (usb_32bit_pipe(data_in)) + drive->drive.type = DTYPE_UAS_32; + else + drive->drive.type = DTYPE_UAS; + drive->command = command; + drive->status = status; + drive->data_in = data_in; + drive->data_out = data_out; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio); + if (ret) { + free(drive); + return -1; + } + return 0; +} + +int +usb_uas_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_UAS) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != US_SC_SCSI || + iface->bInterfaceProtocol != US_PR_UAS) { + dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + /* find & allocate pipes */ + struct usb_endpoint_descriptor *ep = NULL; + struct usb_pipe *command = NULL; + struct usb_pipe *status = NULL; + struct usb_pipe *data_in = NULL; + struct usb_pipe *data_out = NULL; + u8 *desc = (u8*)iface; + while (desc) { + desc += desc[0]; + switch (desc[1]) { + case USB_DT_ENDPOINT: + ep = (void*)desc; + break; + case USB_DT_ENDPOINT_COMPANION: + /* No support (yet) for usb3 streams */ + dprintf(1, "Superspeed UAS devices not supported (yet)\n"); + goto fail; + case 0x24: + switch (desc[2]) { + case UAS_PIPE_ID_COMMAND: + command = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_STATUS: + status = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_IN: + data_in = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_OUT: + data_out = usb_alloc_pipe(usbdev, ep); + break; + default: + goto fail; + } + break; + default: + desc = NULL; + break; + } + } + if (!command || !status || !data_in || !data_out) + goto fail; + + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0); + if (ret < 0) { + dprintf(1, "Unable to configure UAS drive.\n"); + goto fail; + } + + return 0; + +fail: + usb_free_pipe(usbdev, command); + usb_free_pipe(usbdev, status); + usb_free_pipe(usbdev, data_in); + usb_free_pipe(usbdev, data_out); + return -1; +} diff --git a/qemu/roms/seabios/src/hw/usb-uas.h b/qemu/roms/seabios/src/hw/usb-uas.h new file mode 100644 index 000000000..ad91c5f60 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uas.h @@ -0,0 +1,9 @@ +#ifndef __USB_UAS_H +#define __USB_UAS_H + +struct disk_op_s; +int uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_uas_setup(struct usbdevice_s *usbdev); + +#endif /* __USB_UAS_H */ diff --git a/qemu/roms/seabios/src/hw/usb-uhci.c b/qemu/roms/seabios/src/hw/usb-uhci.c new file mode 100644 index 000000000..69c33ee3b --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uhci.c @@ -0,0 +1,567 @@ +// Code for handling UHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_4 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uhci.h" // USBLEGSUP +#include "util.h" // msleep +#include "x86.h" // outw + +struct usb_uhci_s { + struct usb_s usb; + u16 iobase; + struct uhci_qh *control_qh; + struct uhci_framelist *framelist; +}; + +struct uhci_pipe { + struct uhci_qh qh; + struct uhci_td *next_td; + struct usb_pipe pipe; + u16 iobase; + u8 toggle; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to a given port +static int +uhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No device found. + return 0; + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + outw(USBPORTSC_PR, ioport); + msleep(USB_TIME_DRSTR); + return 1; +} + +// Reset device on port +static int +uhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + // Finish reset on port + outw(0, ioport); + udelay(6); // 64 high-speed bit times + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No longer connected + return -1; + outw(USBPORTSC_PE, ioport); + return !!(status & USBPORTSC_LSDA); +} + +// Disable port +static void +uhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + outw(0, ioport); +} + +static struct usbhub_op_s uhci_HubOp = { + .detect = uhci_hub_detect, + .reset = uhci_hub_reset, + .disconnect = uhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_uhci_ports(struct usb_uhci_s *cntl) +{ + ASSERT32FLAT(); + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = 2; + hub.op = &uhci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +uhci_waittick(u16 iobase) +{ + barrier(); + u16 startframe = inw(iobase + USBFRNUM); + u32 end = timer_calc(1000 * 5); + for (;;) { + if (inw(iobase + USBFRNUM) != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +uhci_free_pipes(struct usb_uhci_s *cntl) +{ + dprintf(7, "uhci_free_pipes %p\n", cntl); + + struct uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS); + for (;;) { + u32 link = pos->link; + if (link == UHCI_PTR_TERM) + break; + struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS); + struct uhci_pipe *pipe = container_of(next, struct uhci_pipe, qh); + if (usb_is_freelist(&cntl->usb, &pipe->pipe)) + pos->link = next->link; + else + pos = next; + } + uhci_waittick(cntl->iobase); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct uhci_pipe *pipe = container_of(usbpipe, struct uhci_pipe, pipe); + free(pipe); + } +} + +static void +reset_uhci(struct usb_uhci_s *cntl, u16 bdf) +{ + // XXX - don't reset if not needed. + + // Reset PIRQ and SMI + pci_config_writew(bdf, USBLEGSUP, USBLEGSUP_RWC); + + // Reset the HC + outw(USBCMD_HCRESET, cntl->iobase + USBCMD); + udelay(5); + + // Disable interrupts and commands (just to be safe). + outw(0, cntl->iobase + USBINTR); + outw(0, cntl->iobase + USBCMD); +} + +static void +configure_uhci(void *data) +{ + struct usb_uhci_s *cntl = data; + + // Allocate ram for schedule storage + struct uhci_td *term_td = malloc_high(sizeof(*term_td)); + struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct uhci_pipe *intr_pipe = malloc_high(sizeof(*intr_pipe)); + struct uhci_pipe *term_pipe = malloc_high(sizeof(*term_pipe)); + if (!term_td || !fl || !intr_pipe || !term_pipe) { + warn_noalloc(); + goto fail; + } + + // Work around for PIIX errata + memset(term_td, 0, sizeof(*term_td)); + term_td->link = UHCI_PTR_TERM; + term_td->token = (uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + memset(term_pipe, 0, sizeof(*term_pipe)); + term_pipe->qh.element = (u32)term_td; + term_pipe->qh.link = UHCI_PTR_TERM; + term_pipe->pipe.cntl = &cntl->usb; + + // Set schedule to point to primary intr queue head + memset(intr_pipe, 0, sizeof(*intr_pipe)); + intr_pipe->qh.element = UHCI_PTR_TERM; + intr_pipe->qh.link = (u32)&term_pipe->qh | UHCI_PTR_QH; + intr_pipe->pipe.cntl = &cntl->usb; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)&intr_pipe->qh | UHCI_PTR_QH; + cntl->framelist = fl; + cntl->control_qh = &intr_pipe->qh; + barrier(); + + // Set the frame length to the default: 1 ms exactly + outb(USBSOF_DEFAULT, cntl->iobase + USBSOF); + + // Store the frame list base address + outl((u32)fl->links, cntl->iobase + USBFLBASEADD); + + // Set the current frame number + outw(0, cntl->iobase + USBFRNUM); + + // Mark as configured and running with a 64-byte max packet. + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD); + + // Find devices + int count = check_uhci_ports(cntl); + uhci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + outw(0, cntl->iobase + USBCMD); +fail: + free(term_td); + free(fl); + free(intr_pipe); + free(term_pipe); + free(cntl); +} + +static void +uhci_controller_setup(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + wait_preempt(); // Avoid pci_config_readl when preempting + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_UHCI; + cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4) + & PCI_BASE_ADDRESS_IO_MASK); + + dprintf(1, "UHCI init on dev %02x:%02x.%x (io=%x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + reset_uhci(cntl, bdf); + + run_thread(configure_uhci, cntl); +} + +void +uhci_setup(void) +{ + if (! CONFIG_USB_UHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI) + uhci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static struct usb_pipe * +uhci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + int frameexp = usb_get_period(usbdev, epdesc); + dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + count = ALIGN(count, 2); + struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct uhci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + int lowspeed = pipe->pipe.speed; + int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); + pipe->qh.element = (u32)tds; + pipe->next_td = &tds[0]; + pipe->iobase = cntl->iobase; + + int toggle = 0; + int i; + for (i=0; i<count; i++) { + tds[i].link = (i==count-1 ? (u32)&tds[0] : (u32)&tds[i+1]); + tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + tds[i].token = (uhci_explen(maxpacket) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + tds[i].buffer = data + maxpacket * i; + toggle ^= TD_TOKEN_TOGGLE; + } + + // Add to interrupt schedule. + struct uhci_framelist *fl = cntl->framelist; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS); + pipe->qh.link = intr_qh->link; + barrier(); + intr_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (cntl->control_qh == intr_qh) + cntl->control_qh = &pipe->qh; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.link = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +uhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_UHCI) + return NULL; + usb_add_freelist(upipe); + if (!epdesc) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return uhci_alloc_intr_pipe(usbdev, epdesc); + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_get_freelist(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + usb_desc2pipe(usbpipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct uhci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; + + // Add queue head to controller list. + struct uhci_qh *control_qh = cntl->control_qh; + pipe->qh.link = control_qh->link; + barrier(); + control_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + cntl->control_qh = &pipe->qh; + return &pipe->pipe; +} + +static int +wait_pipe(struct uhci_pipe *pipe, u32 end) +{ + for (;;) { + u32 el_link = GET_LOWFLAT(pipe->qh.element); + if (el_link & UHCI_PTR_TERM) + return 0; + if (timer_check(end)) { + warn_timeout(); + u16 iobase = GET_LOWFLAT(pipe->iobase); + struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS); + dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n" + , pipe, (void*)el_link, GET_LOWFLAT(td->status) + , inw(iobase + USBCMD) + , inw(iobase + USBSTS)); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(iobase); + return -1; + } + yield(); + } +} + +static int +wait_td(struct uhci_td *td, u32 end) +{ + u32 status; + for (;;) { + status = td->status; + if (!(status & TD_CTRL_ACTIVE)) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & TD_CTRL_ANY_ERROR) { + dprintf(1, "wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +#define STACKTDS 16 +#define TDALIGN 16 + +int +uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize) +{ + if (! CONFIG_USB_UHCI) + return -1; + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + dprintf(7, "uhci_send_pipe qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int lowspeed = GET_LOWFLAT(pipe->pipe.speed); + int devaddr = (GET_LOWFLAT(pipe->pipe.devaddr) + | (GET_LOWFLAT(pipe->pipe.ep) << 7)); + int toggle = GET_LOWFLAT(pipe->toggle) ? TD_TOKEN_TOGGLE : 0; + + // Allocate 16 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; + struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); + memset(tds, 0, sizeof(*tds) * STACKTDS); + int tdpos = 0; + + // Enable tds + u32 end = timer_calc(usb_xfer_time(p, datasize)); + barrier(); + SET_LOWFLAT(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + // Setup transfer descriptors + if (cmd) { + // Send setup pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = nexttd | UHCI_PTR_DEPTH; + td->token = (uhci_explen(USB_CONTROL_SETUP_SIZE) + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_SETUP); + td->buffer = (void*)cmd; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle = TD_TOKEN_TOGGLE; + } + while (datasize) { + // Send data pids + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td, end); + if (ret) + goto fail; + + int transfer = datasize; + if (transfer > maxpacket) + transfer = maxpacket; + u32 nexttd = (u32)MAKE_FLATPTR(GET_SEG(SS), &tds[tdpos % STACKTDS]); + td->link = ((transfer==datasize && !cmd) + ? UHCI_PTR_TERM : (nexttd | UHCI_PTR_DEPTH)); + td->token = (uhci_explen(transfer) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + td->buffer = data; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle ^= TD_TOKEN_TOGGLE; + + data += transfer; + datasize -= transfer; + } + if (cmd) { + // Send status pid on control transfers + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td, end); + if (ret) + goto fail; + td->link = UHCI_PTR_TERM; + td->token = (uhci_explen(0) | TD_TOKEN_TOGGLE + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_OUT : USB_PID_IN)); + td->buffer = 0; + barrier(); + td->status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + } + SET_LOWFLAT(pipe->toggle, !!toggle); + return wait_pipe(pipe, end); +fail: + dprintf(1, "uhci_send_bulk failed\n"); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(GET_LOWFLAT(pipe->iobase)); + return -1; +} + +int +uhci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_UHCI) + return -1; + + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + struct uhci_td *td = GET_LOWFLAT(pipe->next_td); + u32 status = GET_LOWFLAT(td->status); + u32 token = GET_LOWFLAT(td->token); + if (status & TD_CTRL_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + void *tddata = GET_LOWFLAT(td->buffer); + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata) + , uhci_expected_length(token)); + + // Reenable this td. + struct uhci_td *next = (void*)(GET_LOWFLAT(td->link) & ~UHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + barrier(); + SET_LOWFLAT(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS) + | TD_CTRL_ACTIVE)); + + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-uhci.h b/qemu/roms/seabios/src/hw/usb-uhci.h new file mode 100644 index 000000000..bff70c667 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-uhci.h @@ -0,0 +1,128 @@ +#ifndef __USB_UHCI_H +#define __USB_UHCI_H + +// usb-uhci.c +void uhci_setup(void); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; +struct usb_pipe *uhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int uhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int uhci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * uhci structs and flags + ****************************************************************/ + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status + * ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */ +#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error: + * the schedule is buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 +#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */ + +struct uhci_framelist { + u32 links[1024]; +} PACKED; + +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | \ + TD_CTRL_BITSTUFF) +#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) + +#define TD_TOKEN_DEVADDR_SHIFT 8 +#define TD_TOKEN_TOGGLE_SHIFT 19 +#define TD_TOKEN_TOGGLE (1 << 19) +#define TD_TOKEN_EXPLEN_SHIFT 21 +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */ +#define TD_TOKEN_PID_MASK 0xFF + +#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \ + TD_TOKEN_EXPLEN_SHIFT) + +#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \ + 1) & TD_TOKEN_EXPLEN_MASK) + +struct uhci_td { + u32 link; + u32 status; + u32 token; + void *buffer; +} PACKED; + +struct uhci_qh { + u32 link; + u32 element; +} PACKED; + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 +#define UHCI_PTR_BREADTH 0x0000 + +#endif // usb-uhci.h diff --git a/qemu/roms/seabios/src/hw/usb-xhci.c b/qemu/roms/seabios/src/hw/usb-xhci.c new file mode 100644 index 000000000..fd58334dc --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-xhci.c @@ -0,0 +1,1139 @@ +// Code for handling XHCI "Super speed" USB controllers. +// +// Copyright (C) 2013 Gerd Hoffmann <kraxel@redhat.com> +// Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // memalign_low +#include "memmap.h" // PAGE_SIZE +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memcpy +#include "usb.h" // struct usb_s +#include "usb-xhci.h" // struct ehci_qh +#include "util.h" // timer_calc +#include "x86.h" // readl + +// -------------------------------------------------------------- +// configuration + +#define XHCI_RING_ITEMS 16 +#define XHCI_RING_SIZE (XHCI_RING_ITEMS*sizeof(struct xhci_trb)) + +/* + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, + * then we can get it from a trb pointer (provided by evt ring). + */ +#define XHCI_RING(_trb) \ + ((struct xhci_ring*)((u32)(_trb) & ~(XHCI_RING_SIZE-1))) + +// -------------------------------------------------------------- +// bit definitions + +#define XHCI_CMD_RS (1<<0) +#define XHCI_CMD_HCRST (1<<1) +#define XHCI_CMD_INTE (1<<2) +#define XHCI_CMD_HSEE (1<<3) +#define XHCI_CMD_LHCRST (1<<7) +#define XHCI_CMD_CSS (1<<8) +#define XHCI_CMD_CRS (1<<9) +#define XHCI_CMD_EWE (1<<10) +#define XHCI_CMD_EU3S (1<<11) + +#define XHCI_STS_HCH (1<<0) +#define XHCI_STS_HSE (1<<2) +#define XHCI_STS_EINT (1<<3) +#define XHCI_STS_PCD (1<<4) +#define XHCI_STS_SSS (1<<8) +#define XHCI_STS_RSS (1<<9) +#define XHCI_STS_SRE (1<<10) +#define XHCI_STS_CNR (1<<11) +#define XHCI_STS_HCE (1<<12) + +#define XHCI_PORTSC_CCS (1<<0) +#define XHCI_PORTSC_PED (1<<1) +#define XHCI_PORTSC_OCA (1<<3) +#define XHCI_PORTSC_PR (1<<4) +#define XHCI_PORTSC_PLS_SHIFT 5 +#define XHCI_PORTSC_PLS_MASK 0xf +#define XHCI_PORTSC_PP (1<<9) +#define XHCI_PORTSC_SPEED_SHIFT 10 +#define XHCI_PORTSC_SPEED_MASK 0xf +#define XHCI_PORTSC_SPEED_FULL (1<<10) +#define XHCI_PORTSC_SPEED_LOW (2<<10) +#define XHCI_PORTSC_SPEED_HIGH (3<<10) +#define XHCI_PORTSC_SPEED_SUPER (4<<10) +#define XHCI_PORTSC_PIC_SHIFT 14 +#define XHCI_PORTSC_PIC_MASK 0x3 +#define XHCI_PORTSC_LWS (1<<16) +#define XHCI_PORTSC_CSC (1<<17) +#define XHCI_PORTSC_PEC (1<<18) +#define XHCI_PORTSC_WRC (1<<19) +#define XHCI_PORTSC_OCC (1<<20) +#define XHCI_PORTSC_PRC (1<<21) +#define XHCI_PORTSC_PLC (1<<22) +#define XHCI_PORTSC_CEC (1<<23) +#define XHCI_PORTSC_CAS (1<<24) +#define XHCI_PORTSC_WCE (1<<25) +#define XHCI_PORTSC_WDE (1<<26) +#define XHCI_PORTSC_WOE (1<<27) +#define XHCI_PORTSC_DR (1<<30) +#define XHCI_PORTSC_WPR (1<<31) + +#define TRB_C (1<<0) +#define TRB_TYPE_SHIFT 10 +#define TRB_TYPE_MASK 0x3f +#define TRB_TYPE(t) (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK) + +#define TRB_EV_ED (1<<2) + +#define TRB_TR_ENT (1<<1) +#define TRB_TR_ISP (1<<2) +#define TRB_TR_NS (1<<3) +#define TRB_TR_CH (1<<4) +#define TRB_TR_IOC (1<<5) +#define TRB_TR_IDT (1<<6) +#define TRB_TR_TBC_SHIFT 7 +#define TRB_TR_TBC_MASK 0x3 +#define TRB_TR_BEI (1<<9) +#define TRB_TR_TLBPC_SHIFT 16 +#define TRB_TR_TLBPC_MASK 0xf +#define TRB_TR_FRAMEID_SHIFT 20 +#define TRB_TR_FRAMEID_MASK 0x7ff +#define TRB_TR_SIA (1<<31) + +#define TRB_TR_DIR (1<<16) + +#define TRB_CR_SLOTID_SHIFT 24 +#define TRB_CR_SLOTID_MASK 0xff +#define TRB_CR_EPID_SHIFT 16 +#define TRB_CR_EPID_MASK 0x1f + +#define TRB_CR_BSR (1<<9) +#define TRB_CR_DC (1<<9) + +#define TRB_LK_TC (1<<1) + +#define TRB_INTR_SHIFT 22 +#define TRB_INTR_MASK 0x3ff +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) + +typedef enum TRBType { + TRB_RESERVED = 0, + TR_NORMAL, + TR_SETUP, + TR_DATA, + TR_STATUS, + TR_ISOCH, + TR_LINK, + TR_EVDATA, + TR_NOOP, + CR_ENABLE_SLOT, + CR_DISABLE_SLOT, + CR_ADDRESS_DEVICE, + CR_CONFIGURE_ENDPOINT, + CR_EVALUATE_CONTEXT, + CR_RESET_ENDPOINT, + CR_STOP_ENDPOINT, + CR_SET_TR_DEQUEUE, + CR_RESET_DEVICE, + CR_FORCE_EVENT, + CR_NEGOTIATE_BW, + CR_SET_LATENCY_TOLERANCE, + CR_GET_PORT_BANDWIDTH, + CR_FORCE_HEADER, + CR_NOOP, + ER_TRANSFER = 32, + ER_COMMAND_COMPLETE, + ER_PORT_STATUS_CHANGE, + ER_BANDWIDTH_REQUEST, + ER_DOORBELL, + ER_HOST_CONTROLLER, + ER_DEVICE_NOTIFICATION, + ER_MFINDEX_WRAP, +} TRBType; + +typedef enum TRBCCode { + CC_INVALID = 0, + CC_SUCCESS, + CC_DATA_BUFFER_ERROR, + CC_BABBLE_DETECTED, + CC_USB_TRANSACTION_ERROR, + CC_TRB_ERROR, + CC_STALL_ERROR, + CC_RESOURCE_ERROR, + CC_BANDWIDTH_ERROR, + CC_NO_SLOTS_ERROR, + CC_INVALID_STREAM_TYPE_ERROR, + CC_SLOT_NOT_ENABLED_ERROR, + CC_EP_NOT_ENABLED_ERROR, + CC_SHORT_PACKET, + CC_RING_UNDERRUN, + CC_RING_OVERRUN, + CC_VF_ER_FULL, + CC_PARAMETER_ERROR, + CC_BANDWIDTH_OVERRUN, + CC_CONTEXT_STATE_ERROR, + CC_NO_PING_RESPONSE_ERROR, + CC_EVENT_RING_FULL_ERROR, + CC_INCOMPATIBLE_DEVICE_ERROR, + CC_MISSED_SERVICE_ERROR, + CC_COMMAND_RING_STOPPED, + CC_COMMAND_ABORTED, + CC_STOPPED, + CC_STOPPED_LENGTH_INVALID, + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, + CC_ISOCH_BUFFER_OVERRUN = 31, + CC_EVENT_LOST_ERROR, + CC_UNDEFINED_ERROR, + CC_INVALID_STREAM_ID_ERROR, + CC_SECONDARY_BANDWIDTH_ERROR, + CC_SPLIT_TRANSACTION_ERROR +} TRBCCode; + +enum { + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; + +#define xhci_get_field(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +// -------------------------------------------------------------- +// state structs + +struct xhci_ring { + struct xhci_trb ring[XHCI_RING_ITEMS]; + struct xhci_trb evt; + u32 eidx; + u32 nidx; + u32 cs; + struct mutex_s lock; +}; + +struct usb_xhci_s { + struct usb_s usb; + + /* devinfo */ + u32 baseaddr; + u32 xcap; + u32 ports; + u32 slots; + u8 context64; + + /* xhci registers */ + struct xhci_caps *caps; + struct xhci_op *op; + struct xhci_pr *pr; + struct xhci_ir *ir; + struct xhci_db *db; + + /* xhci data structures */ + struct xhci_devlist *devs; + struct xhci_ring *cmds; + struct xhci_ring *evts; + struct xhci_er_seg *eseg; +}; + +struct xhci_pipe { + struct xhci_ring reqs; + + struct usb_pipe pipe; + u32 slotid; + u32 epid; + void *buf; + int bufused; +}; + +// -------------------------------------------------------------- +// tables + +static const char *speed_name[16] = { + [ 0 ] = " - ", + [ 1 ] = "Full", + [ 2 ] = "Low", + [ 3 ] = "High", + [ 4 ] = "Super", +}; + +static const int speed_from_xhci[16] = { + [ 0 ] = -1, + [ 1 ] = USB_FULLSPEED, + [ 2 ] = USB_LOWSPEED, + [ 3 ] = USB_HIGHSPEED, + [ 4 ] = USB_SUPERSPEED, + [ 5 ... 15 ] = -1, +}; + +static const int speed_to_xhci[] = { + [ USB_FULLSPEED ] = 1, + [ USB_LOWSPEED ] = 2, + [ USB_HIGHSPEED ] = 3, + [ USB_SUPERSPEED ] = 4, +}; + +static const int eptype_to_xhci_in[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 5, + [ USB_ENDPOINT_XFER_BULK ] = 6, + [ USB_ENDPOINT_XFER_INT ] = 7, +}; + +static const int eptype_to_xhci_out[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 1, + [ USB_ENDPOINT_XFER_BULK ] = 2, + [ USB_ENDPOINT_XFER_INT ] = 3, +}; + +static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout) +{ + u32 end = timer_calc(timeout); + + while ((readl(reg) & mask) != value) { + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + return 0; +} + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define XHCI_TIME_POSTPOWER 20 + +// Check if device attached to port +static void +xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc) +{ + u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS); + u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED); + + dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", + prefix, port + 1, portsc, + (portsc & XHCI_PORTSC_PP) ? " powered," : "", + (portsc & XHCI_PORTSC_PED) ? " enabled," : "", + pls, speed, speed_name[speed]); +} + +static int +xhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + return (portsc & XHCI_PORTSC_CCS) ? 1 : 0; +} + +// Reset device on port +static int +xhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + int rc; + + switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { + case PLS_U0: + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + case PLS_POLLING: + xhci_print_port_state(3, __func__, port, portsc); + portsc |= XHCI_PORTSC_PR; + writel(&xhci->pr[port].portsc, portsc); + if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) + return -1; + portsc = readl(&xhci->pr[port].portsc); + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + default: + rc = -1; + break; + } + + xhci_print_port_state(1, "XHCI", port, portsc); + return rc; +} + +static void +xhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + // XXX - should turn the port power off. +} + +static struct usbhub_op_s xhci_hub_ops = { + .detect = xhci_hub_detect, + .reset = xhci_hub_reset, + .disconnect = xhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +xhci_check_ports(struct usb_xhci_s *xhci) +{ + // Wait for port power to stabilize. + msleep(XHCI_TIME_POSTPOWER); + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &xhci->usb; + hub.portcount = xhci->ports; + hub.op = &xhci_hub_ops; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +xhci_free_pipes(struct usb_xhci_s *xhci) +{ + // XXX - should walk list of pipes and free unused pipes. +} + +static void +configure_xhci(void *data) +{ + struct usb_xhci_s *xhci = data; + u32 reg; + + xhci->devs = memalign_high(64, sizeof(*xhci->devs) * (xhci->slots + 1)); + xhci->eseg = memalign_high(64, sizeof(*xhci->eseg)); + xhci->cmds = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->cmds)); + xhci->evts = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->evts)); + if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { + warn_noalloc(); + goto fail; + } + memset(xhci->devs, 0, sizeof(*xhci->devs) * (xhci->slots + 1)); + memset(xhci->cmds, 0, sizeof(*xhci->cmds)); + memset(xhci->evts, 0, sizeof(*xhci->evts)); + memset(xhci->eseg, 0, sizeof(*xhci->eseg)); + + reg = readl(&xhci->op->usbcmd); + if (reg & XHCI_CMD_RS) { + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + if (wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32) != 0) + goto fail; + } + + dprintf(3, "%s: resetting\n", __func__); + writel(&xhci->op->usbcmd, XHCI_CMD_HCRST); + if (wait_bit(&xhci->op->usbcmd, XHCI_CMD_HCRST, 0, 100) != 0) + goto fail; + if (wait_bit(&xhci->op->usbsts, XHCI_STS_CNR, 0, 100) != 0) + goto fail; + + writel(&xhci->op->config, xhci->slots); + writel(&xhci->op->dcbaap_low, (u32)xhci->devs); + writel(&xhci->op->dcbaap_high, 0); + writel(&xhci->op->crcr_low, (u32)xhci->cmds | 1); + writel(&xhci->op->crcr_high, 0); + xhci->cmds->cs = 1; + + xhci->eseg->ptr_low = (u32)xhci->evts; + xhci->eseg->ptr_high = 0; + xhci->eseg->size = XHCI_RING_ITEMS; + writel(&xhci->ir->erstsz, 1); + writel(&xhci->ir->erdp_low, (u32)xhci->evts); + writel(&xhci->ir->erdp_high, 0); + writel(&xhci->ir->erstba_low, (u32)xhci->eseg); + writel(&xhci->ir->erstba_high, 0); + xhci->evts->cs = 1; + + reg = readl(&xhci->caps->hcsparams2); + u32 spb = reg >> 27; + if (spb) { + dprintf(3, "%s: setup %d scratch pad buffers\n", __func__, spb); + u64 *spba = memalign_high(64, sizeof(*spba) * spb); + void *pad = memalign_high(PAGE_SIZE, PAGE_SIZE * spb); + if (!spba || !pad) { + warn_noalloc(); + free(spba); + free(pad); + goto fail; + } + int i; + for (i = 0; i < spb; i++) + spba[i] = (u32)pad + (i * PAGE_SIZE); + xhci->devs[0].ptr_low = (u32)spba; + xhci->devs[0].ptr_high = 0; + } + + reg = readl(&xhci->op->usbcmd); + reg |= XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + + // Find devices + int count = xhci_check_ports(xhci); + xhci_free_pipes(xhci); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + dprintf(1, "XHCI no devices found\n"); + reg = readl(&xhci->op->usbcmd); + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32); + +fail: + free(xhci->eseg); + free(xhci->evts); + free(xhci->cmds); + free(xhci->devs); + free(xhci); +} + +static void +xhci_controller_setup(struct pci_device *pci) +{ + struct usb_xhci_s *xhci = malloc_high(sizeof(*xhci)); + if (!xhci) { + warn_noalloc(); + return; + } + memset(xhci, 0, sizeof(*xhci)); + + wait_preempt(); // Avoid pci_config_readl when preempting + xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK; + xhci->caps = (void*)(xhci->baseaddr); + xhci->op = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength)); + xhci->pr = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400); + xhci->db = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff)); + xhci->ir = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20); + + u32 hcs1 = readl(&xhci->caps->hcsparams1); + u32 hcc = readl(&xhci->caps->hccparams); + xhci->ports = (hcs1 >> 24) & 0xff; + xhci->slots = hcs1 & 0xff; + xhci->xcap = ((hcc >> 16) & 0xffff) << 2; + xhci->context64 = (hcc & 0x04) ? 1 : 0; + + xhci->usb.pci = pci; + xhci->usb.type = USB_TYPE_XHCI; + + dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots" + ", %d byte contexts\n" + , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) + , pci_bdf_to_fn(pci->bdf), xhci->caps + , xhci->ports, xhci->slots, xhci->context64 ? 64 : 32); + + if (xhci->xcap) { + u32 off, addr = xhci->baseaddr + xhci->xcap; + do { + struct xhci_xcap *xcap = (void*)addr; + u32 ports, name, cap = readl(&xcap->cap); + switch (cap & 0xff) { + case 0x02: + name = readl(&xcap->data[0]); + ports = readl(&xcap->data[1]); + dprintf(1, "XHCI protocol %c%c%c%c %x.%02x" + ", %d ports (offset %d), def %x\n" + , (name >> 0) & 0xff + , (name >> 8) & 0xff + , (name >> 16) & 0xff + , (name >> 24) & 0xff + , (cap >> 24) & 0xff + , (cap >> 16) & 0xff + , (ports >> 8) & 0xff + , (ports >> 0) & 0xff + , ports >> 16); + break; + default: + dprintf(1, "XHCI extcap 0x%x @ %x\n", cap & 0xff, addr); + break; + } + off = (cap >> 8) & 0xff; + addr += off << 2; + } while (off > 0); + } + + u32 pagesize = readl(&xhci->op->pagesize); + if (PAGE_SIZE != (pagesize<<12)) { + dprintf(1, "XHCI driver does not support page size code %d\n" + , pagesize<<12); + free(xhci); + return; + } + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + run_thread(configure_xhci, xhci); +} + +void +xhci_setup(void) +{ + if (! CONFIG_USB_XHCI) + return; + struct pci_device *pci; + foreachpci(pci) { + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI) + xhci_controller_setup(pci); + } +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value) +{ + struct xhci_db *db = xhci->db; + void *addr = &db[slotid].doorbell; + writel(addr, value); +} + +static void xhci_process_events(struct usb_xhci_s *xhci) +{ + struct xhci_ring *evts = xhci->evts; + + for (;;) { + /* check for event */ + u32 nidx = evts->nidx; + u32 cs = evts->cs; + struct xhci_trb *etrb = evts->ring + nidx; + u32 control = etrb->control; + if ((control & TRB_C) != (cs ? 1 : 0)) + return; + + /* process event */ + u32 evt_type = TRB_TYPE(control); + u32 evt_cc = (etrb->status >> 24) & 0xff; + switch (evt_type) { + case ER_TRANSFER: + case ER_COMMAND_COMPLETE: + { + struct xhci_trb *rtrb = (void*)etrb->ptr_low; + struct xhci_ring *ring = XHCI_RING(rtrb); + struct xhci_trb *evt = &ring->evt; + u32 eidx = rtrb - ring->ring + 1; + dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n", + __func__, ring, rtrb, evt, evt_type, eidx, evt_cc); + memcpy(evt, etrb, sizeof(*etrb)); + ring->eidx = eidx; + break; + } + case ER_PORT_STATUS_CHANGE: + { + u32 portid = (etrb->ptr_low >> 24) & 0xff; + dprintf(3, "%s: status change port #%d\n", + __func__, portid); + break; + } + default: + dprintf(1, "%s: unknown event, type %d, cc %d\n", + __func__, evt_type, evt_cc); + break; + } + + /* move ring index, notify xhci */ + nidx++; + if (nidx == XHCI_RING_ITEMS) { + nidx = 0; + cs = cs ? 0 : 1; + evts->cs = cs; + } + evts->nidx = nidx; + struct xhci_ir *ir = xhci->ir; + u32 erdp = (u32)(evts->ring + nidx); + writel(&ir->erdp_low, erdp); + writel(&ir->erdp_high, 0); + } +} + +static int xhci_ring_busy(struct xhci_ring *ring) +{ + u32 eidx = ring->eidx; + u32 nidx = ring->nidx; + return (eidx != nidx); +} + +static int xhci_event_wait(struct usb_xhci_s *xhci, + struct xhci_ring *ring, + u32 timeout) +{ + u32 end = timer_calc(timeout); + + for (;;) { + xhci_process_events(xhci); + if (!xhci_ring_busy(ring)) { + u32 status = ring->evt.status; + return (status >> 24) & 0xff; + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +static void xhci_trb_queue(struct xhci_ring *ring, + struct xhci_trb *trb) +{ + u32 nidx = ring->nidx; + u32 cs = ring->cs; + struct xhci_trb *dst; + u32 control; + + if (nidx == XHCI_RING_ITEMS-1) { + dst = ring->ring + nidx; + control = (TR_LINK << 10); // trb type + control |= TRB_LK_TC; + control |= (cs ? TRB_C : 0); + dst->ptr_low = (u32)&ring[0]; + dst->ptr_high = 0; + dst->status = 0; + dst->control = control; + nidx = 0; + cs = cs ? 0 : 1; + ring->nidx = nidx; + ring->cs = cs; + + dprintf(5, "%s: ring %p [linked]\n", __func__, ring); + } + + dst = ring->ring + nidx; + control = trb->control | (cs ? TRB_C : 0); + + dst->ptr_low = trb->ptr_low; + dst->ptr_high = trb->ptr_high; + dst->status = trb->status; + dst->control = control; + nidx++; + ring->nidx = nidx; + + dprintf(5, "%s: ring %p [nidx %d, len %d]\n", + __func__, ring, nidx, + trb->status & 0xffff); +} + +static int xhci_cmd_submit(struct usb_xhci_s *xhci, + struct xhci_trb *cmd) +{ + int rc; + + mutex_lock(&xhci->cmds->lock); + xhci_trb_queue(xhci->cmds, cmd); + xhci_doorbell(xhci, 0, 0); + rc = xhci_event_wait(xhci, xhci->cmds, 1000); + mutex_unlock(&xhci->cmds->lock); + return rc; +} + +static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) +{ + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (CR_ENABLE_SLOT << 10) + }; + dprintf(3, "%s:\n", __func__); + int cc = xhci_cmd_submit(xhci, &cmd); + if (cc != CC_SUCCESS) + return -1; + return (xhci->cmds->evt.control >> 24) & 0xff; +} + +#if 0 +static int xhci_cmd_disable_slot(struct usb_xhci_s *xhci, u32 slotid) +{ + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_DISABLE_SLOT << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} +#endif + +static int xhci_cmd_address_device(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_ADDRESS_DEVICE << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, slotid); + return xhci_cmd_submit(xhci, &cmd); +} + +static int xhci_cmd_configure_endpoint(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} + +static int xhci_cmd_evaluate_context(struct usb_xhci_s *xhci, u32 slotid + , struct xhci_inctx *inctx) +{ + struct xhci_trb cmd = { + .ptr_low = (u32)inctx, + .ptr_high = 0, + .status = 0, + .control = (slotid << 24) | (CR_EVALUATE_CONTEXT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + slotid, inctx->add, inctx->del); + return xhci_cmd_submit(xhci, &cmd); +} + +static struct xhci_inctx * +xhci_alloc_inctx(struct usbdevice_s *usbdev, int maxepid) +{ + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + int size = (sizeof(struct xhci_inctx) * 33) << xhci->context64; + struct xhci_inctx *in = memalign_tmphigh(2048 << xhci->context64, size); + if (!in) { + warn_noalloc(); + return NULL; + } + memset(in, 0, size); + + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; + slot->ctx[0] |= maxepid << 27; // context entries + slot->ctx[0] |= speed_to_xhci[usbdev->speed] << 20; + + // Set high-speed hub flags. + struct usbdevice_s *hubdev = usbdev->hub->usbdev; + if (hubdev) { + if (usbdev->speed == USB_LOWSPEED || usbdev->speed == USB_FULLSPEED) { + struct xhci_pipe *hpipe = container_of( + hubdev->defpipe, struct xhci_pipe, pipe); + if (hubdev->speed == USB_HIGHSPEED) { + slot->ctx[2] |= hpipe->slotid; + slot->ctx[2] |= (usbdev->port+1) << 8; + } else { + struct xhci_slotctx *hslot = (void*)xhci->devs[hpipe->slotid].ptr_low; + slot->ctx[2] = hslot->ctx[2]; + } + } + u32 route = 0; + while (usbdev->hub->usbdev) { + route <<= 4; + route |= (usbdev->port+1) & 0xf; + usbdev = usbdev->hub->usbdev; + } + slot->ctx[0] |= route; + } + + slot->ctx[1] |= (usbdev->port+1) << 16; + + return in; +} + +static int xhci_config_hub(struct usbhub_s *hub) +{ + struct usb_xhci_s *xhci = container_of( + hub->cntl, struct usb_xhci_s, usb); + struct xhci_pipe *pipe = container_of( + hub->usbdev->defpipe, struct xhci_pipe, pipe); + struct xhci_slotctx *hdslot = (void*)xhci->devs[pipe->slotid].ptr_low; + if ((hdslot->ctx[3] >> 27) == 3) + // Already configured + return 0; + struct xhci_inctx *in = xhci_alloc_inctx(hub->usbdev, 1); + if (!in) + return -1; + in->add = 0x01; + struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64]; + slot->ctx[0] |= 1 << 26; + slot->ctx[1] |= hub->portcount << 24; + + int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); + free(in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: configure hub: failed (cc %d)\n", __func__, cc); + return -1; + } + return 0; +} + +static struct usb_pipe * +xhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + struct xhci_pipe *pipe; + u32 epid; + + if (epdesc->bEndpointAddress == 0) { + epid = 1; + } else { + epid = (epdesc->bEndpointAddress & 0x0f) * 2; + epid += (epdesc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + } + + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_high(XHCI_RING_SIZE, sizeof(*pipe)); + else + pipe = memalign_low(XHCI_RING_SIZE, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->epid = epid; + pipe->reqs.cs = 1; + if (eptype == USB_ENDPOINT_XFER_INT) + pipe->buf = malloc_high(pipe->pipe.maxpacket); + + // Allocate input context and initialize endpoint info. + struct xhci_inctx *in = xhci_alloc_inctx(usbdev, epid); + if (!in) + goto fail; + in->add = 0x01 | (1 << epid); + struct xhci_epctx *ep = (void*)&in[(pipe->epid+1) << xhci->context64]; + if (eptype == USB_ENDPOINT_XFER_INT) + ep->ctx[0] = (usb_get_period(usbdev, epdesc) + 3) << 16; + ep->ctx[1] |= eptype << 3; + if (epdesc->bEndpointAddress & USB_DIR_IN + || eptype == USB_ENDPOINT_XFER_CONTROL) + ep->ctx[1] |= 1 << 5; + ep->ctx[1] |= pipe->pipe.maxpacket << 16; + ep->deq_low = (u32)&pipe->reqs.ring[0]; + ep->deq_low |= 1; // dcs + ep->length = pipe->pipe.maxpacket; + + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); + if (pipe->epid == 1) { + if (usbdev->hub->usbdev) { + // Make sure parent hub is configured. + int ret = xhci_config_hub(usbdev->hub); + if (ret) + goto fail; + } + // Enable slot. + u32 size = (sizeof(struct xhci_slotctx) * 32) << xhci->context64; + struct xhci_slotctx *dev = memalign_high(1024 << xhci->context64, size); + if (!dev) { + warn_noalloc(); + goto fail; + } + int slotid = xhci_cmd_enable_slot(xhci); + if (slotid < 0) { + dprintf(1, "%s: enable slot: failed\n", __func__); + free(dev); + goto fail; + } + dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); + memset(dev, 0, size); + pipe->slotid = usbdev->slotid = slotid; + xhci->devs[slotid].ptr_low = (u32)dev; + xhci->devs[slotid].ptr_high = 0; + + // Send set_address command. + int cc = xhci_cmd_address_device(xhci, slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); + goto fail; + } + } else { + pipe->slotid = usbdev->slotid; + // Send configure command. + int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc); + goto fail; + } + } + free(in); + return &pipe->pipe; + +fail: + free(pipe); + free(in); + return NULL; +} + +struct usb_pipe * +xhci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + if (!CONFIG_USB_XHCI) + return NULL; + if (!epdesc) { + usb_add_freelist(upipe); + return NULL; + } + if (!upipe) + return xhci_alloc_pipe(usbdev, epdesc); + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + int oldmaxpacket = upipe->maxpacket; + usb_desc2pipe(upipe, usbdev, epdesc); + struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->slotid, pipe->epid); + if (eptype != USB_ENDPOINT_XFER_CONTROL || upipe->maxpacket == oldmaxpacket) + return upipe; + + // maxpacket has changed on control endpoint - update controller. + dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", + __func__, oldmaxpacket, pipe->pipe.maxpacket); + struct xhci_inctx *in = xhci_alloc_inctx(usbdev, 1); + if (!in) + return upipe; + in->add = (1 << 1); + struct xhci_epctx *ep = (void*)&in[2 << xhci->context64]; + ep->ctx[1] |= (pipe->pipe.maxpacket << 16); + int cc = xhci_cmd_evaluate_context(xhci, pipe->slotid, in); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", + __func__, cc); + } + free(in); + + return upipe; +} + +static void xhci_xfer_queue(struct xhci_pipe *pipe, + void *data, int datalen, u32 flags) +{ + struct xhci_trb trb; + memset(&trb, 0, sizeof(trb)); + if (flags & TRB_TR_IDT) + memcpy(&trb.ptr_low, data, datalen); + else + trb.ptr_low = (u32)data; + trb.status = datalen; + trb.control = flags; + xhci_trb_queue(&pipe->reqs, &trb); +} + +static void xhci_xfer_kick(struct xhci_pipe *pipe) +{ + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 slotid = pipe->slotid; + u32 epid = pipe->epid; + + dprintf(5, "%s: ring %p, slotid %d, epid %d\n", + __func__, &pipe->reqs, slotid, epid); + xhci_doorbell(xhci, slotid, epid); +} + +static void xhci_xfer_normal(struct xhci_pipe *pipe, + void *data, int datalen) +{ + xhci_xfer_queue(pipe, data, datalen, (TR_NORMAL << 10) | TRB_TR_IOC); + xhci_xfer_kick(pipe); +} + +int +xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datalen) +{ + if (!CONFIG_USB_XHCI) + return -1; + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + + if (cmd) { + const struct usb_ctrlrequest *req = cmd; + if (req->bRequest == USB_REQ_SET_ADDRESS) + // Set address command sent during xhci_alloc_pipe. + return 0; + + xhci_xfer_queue(pipe, (void*)req, USB_CONTROL_SETUP_SIZE + , (TR_SETUP << 10) | TRB_TR_IDT + | ((datalen ? (dir ? 3 : 2) : 0) << 16)); + if (datalen) + xhci_xfer_queue(pipe, data, datalen, (TR_DATA << 10) + | ((dir ? 1 : 0) << 16)); + xhci_xfer_queue(pipe, NULL, 0, (TR_STATUS << 10) | TRB_TR_IOC + | ((dir ? 0 : 1) << 16)); + xhci_xfer_kick(pipe); + } else { + xhci_xfer_normal(pipe, data, datalen); + } + + int cc = xhci_event_wait(xhci, &pipe->reqs, usb_xfer_time(p, datalen)); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: xfer failed (cc %d)\n", __func__, cc); + return -1; + } + + return 0; +} + +int VISIBLE32FLAT +xhci_poll_intr(struct usb_pipe *p, void *data) +{ + if (!CONFIG_USB_XHCI) + return -1; + + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = container_of( + pipe->pipe.cntl, struct usb_xhci_s, usb); + u32 len = pipe->pipe.maxpacket; + void *buf = pipe->buf; + int bufused = pipe->bufused; + + if (!bufused) { + xhci_xfer_normal(pipe, buf, len); + bufused = 1; + pipe->bufused = bufused; + return -1; + } + + xhci_process_events(xhci); + if (xhci_ring_busy(&pipe->reqs)) + return -1; + dprintf(5, "%s: st %x ct %x [ %p <= %p / %d ]\n", __func__, + pipe->reqs.evt.status, + pipe->reqs.evt.control, + data, buf, len); + memcpy(data, buf, len); + xhci_xfer_normal(pipe, buf, len); + return 0; +} diff --git a/qemu/roms/seabios/src/hw/usb-xhci.h b/qemu/roms/seabios/src/hw/usb-xhci.h new file mode 100644 index 000000000..c768c5b58 --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb-xhci.h @@ -0,0 +1,133 @@ +#ifndef __USB_XHCI_H +#define __USB_XHCI_H + +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; + +// -------------------------------------------------------------- + +// usb-xhci.c +void xhci_setup(void); +struct usb_pipe *xhci_realloc_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc); +int xhci_send_pipe(struct usb_pipe *p, int dir, const void *cmd + , void *data, int datasize); +int xhci_poll_intr(struct usb_pipe *p, void *data); + +// -------------------------------------------------------------- +// register interface + +// capabilities +struct xhci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams1; + u32 hcsparams2; + u32 hcsparams3; + u32 hccparams; + u32 dboff; + u32 rtsoff; +} PACKED; + +// extended capabilities +struct xhci_xcap { + u32 cap; + u32 data[]; +} PACKED; + +// operational registers +struct xhci_op { + u32 usbcmd; + u32 usbsts; + u32 pagesize; + u32 reserved_01[2]; + u32 dnctl; + u32 crcr_low; + u32 crcr_high; + u32 reserved_02[4]; + u32 dcbaap_low; + u32 dcbaap_high; + u32 config; +} PACKED; + +// port registers +struct xhci_pr { + u32 portsc; + u32 portpmsc; + u32 portli; + u32 reserved_01; +} PACKED; + +// doorbell registers +struct xhci_db { + u32 doorbell; +} PACKED; + +// runtime registers +struct xhci_rts { + u32 mfindex; +} PACKED; + +// interrupter registers +struct xhci_ir { + u32 iman; + u32 imod; + u32 erstsz; + u32 reserved_01; + u32 erstba_low; + u32 erstba_high; + u32 erdp_low; + u32 erdp_high; +} PACKED; + +// -------------------------------------------------------------- +// memory data structs + +// slot context +struct xhci_slotctx { + u32 ctx[4]; + u32 reserved_01[4]; +} PACKED; + +// endpoint context +struct xhci_epctx { + u32 ctx[2]; + u32 deq_low; + u32 deq_high; + u32 length; + u32 reserved_01[3]; +} PACKED; + +// device context array element +struct xhci_devlist { + u32 ptr_low; + u32 ptr_high; +} PACKED; + +// input context +struct xhci_inctx { + u32 del; + u32 add; + u32 reserved_01[6]; +} PACKED; + +// transfer block (ring element) +struct xhci_trb { + u32 ptr_low; + u32 ptr_high; + u32 status; + u32 control; +} PACKED; + +// event ring segment +struct xhci_er_seg { + u32 ptr_low; + u32 ptr_high; + u32 size; + u32 reserved_01; +} PACKED; + +#endif // usb-xhci.h diff --git a/qemu/roms/seabios/src/hw/usb.c b/qemu/roms/seabios/src/hw/usb.c new file mode 100644 index 000000000..1b4ea8bed --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb.c @@ -0,0 +1,500 @@ +// Main code for handling USB controllers and devices. +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // ehci_setup +#include "usb-xhci.h" // xhci_setup +#include "usb-hid.h" // usb_keyboard_setup +#include "usb-hub.h" // usb_hub_setup +#include "usb-msc.h" // usb_msc_setup +#include "usb-ohci.h" // ohci_setup +#include "usb-uas.h" // usb_uas_setup +#include "usb-uhci.h" // uhci_setup +#include "util.h" // msleep +#include "x86.h" // __fls + + +/**************************************************************** + * Controller function wrappers + ****************************************************************/ + +// Allocate, update, or free a usb pipe. +static struct usb_pipe * +usb_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe + , struct usb_endpoint_descriptor *epdesc) +{ + switch (usbdev->hub->cntl->type) { + default: + case USB_TYPE_UHCI: + return uhci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_OHCI: + return ohci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_EHCI: + return ehci_realloc_pipe(usbdev, pipe, epdesc); + case USB_TYPE_XHCI: + return xhci_realloc_pipe(usbdev, pipe, epdesc); + } +} + +// Send a message on a control pipe using the default control descriptor. +static int +usb_send_pipe(struct usb_pipe *pipe_fl, int dir, const void *cmd + , void *data, int datasize) +{ + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_OHCI: + if (MODESEGMENT) + return -1; + return ohci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_EHCI: + return ehci_send_pipe(pipe_fl, dir, cmd, data, datasize); + case USB_TYPE_XHCI: + if (MODESEGMENT) + return -1; + return xhci_send_pipe(pipe_fl, dir, cmd, data, datasize); + } +} + +int +usb_poll_intr(struct usb_pipe *pipe_fl, void *data) +{ + ASSERT16(); + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_poll_intr(pipe_fl, data); + case USB_TYPE_OHCI: + return ohci_poll_intr(pipe_fl, data); + case USB_TYPE_EHCI: + return ehci_poll_intr(pipe_fl, data); + case USB_TYPE_XHCI: ; + extern void _cfunc32flat_xhci_poll_intr(void); + return call32_params(_cfunc32flat_xhci_poll_intr, (u32)pipe_fl + , (u32)MAKE_FLATPTR(GET_SEG(SS), (u32)data), 0, -1); + } +} + +int usb_32bit_pipe(struct usb_pipe *pipe_fl) +{ + return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI) + || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI); +} + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Allocate a usb pipe. +struct usb_pipe * +usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + return usb_realloc_pipe(usbdev, NULL, epdesc); +} + +// Free an allocated control or bulk pipe. +void +usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe) +{ + if (!pipe) + return; + usb_realloc_pipe(usbdev, pipe, NULL); +} + +// Send a message to the default control pipe of a device. +int +usb_send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data) +{ + return usb_send_pipe(pipe, req->bRequestType & USB_DIR_IN, req + , data, req->wLength); +} + +// Send a message to a bulk endpoint +int +usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +{ + return usb_send_pipe(pipe_fl, dir, NULL, data, datasize); +} + +// Check if a pipe for a given controller is on the freelist +int +usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe) +{ + return pipe->cntl != cntl; +} + +// Add a pipe to the controller's freelist +void +usb_add_freelist(struct usb_pipe *pipe) +{ + if (!pipe) + return; + struct usb_s *cntl = pipe->cntl; + pipe->freenext = cntl->freelist; + cntl->freelist = pipe; +} + +// Check for an available pipe on the freelist. +struct usb_pipe * +usb_get_freelist(struct usb_s *cntl, u8 eptype) +{ + struct usb_pipe **pfree = &cntl->freelist; + for (;;) { + struct usb_pipe *pipe = *pfree; + if (!pipe) + return NULL; + if (pipe->eptype == eptype) { + *pfree = pipe->freenext; + return pipe; + } + pfree = &pipe->freenext; + } +} + +// Fill "pipe" endpoint info from an endpoint descriptor. +void +usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + pipe->cntl = usbdev->hub->cntl; + pipe->type = usbdev->hub->cntl->type; + pipe->ep = epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + pipe->devaddr = usbdev->devaddr; + pipe->speed = usbdev->speed; + pipe->maxpacket = epdesc->wMaxPacketSize; + pipe->eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +} + +// Find the exponential period of the requested interrupt end point. +int +usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + int period = epdesc->bInterval; + if (usbdev->speed != USB_HIGHSPEED) + return (period <= 0) ? 0 : __fls(period); + return (period <= 4) ? 0 : period - 4; +} + +// Maximum time (in ms) a data transfer should take +int +usb_xfer_time(struct usb_pipe *pipe, int datalen) +{ + // Use the maximum command time (5 seconds), except for + // set_address commands where we don't want to stall the boot if + // the device doesn't actually exist. Add 100ms to account for + // any controller delays. + if (!GET_LOWFLAT(pipe->devaddr)) + return USB_TIME_STATUS + 100; + return USB_TIME_COMMAND + 100; +} + +// Find the first endpoint of a given type in an interface description. +struct usb_endpoint_descriptor * +usb_find_desc(struct usbdevice_s *usbdev, int type, int dir) +{ + struct usb_endpoint_descriptor *epdesc = (void*)&usbdev->iface[1]; + for (;;) { + if ((void*)epdesc >= (void*)usbdev->iface + usbdev->imax + || epdesc->bDescriptorType == USB_DT_INTERFACE) { + return NULL; + } + if (epdesc->bDescriptorType == USB_DT_ENDPOINT + && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir + && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type) + return epdesc; + epdesc = (void*)epdesc + epdesc->bLength; + } +} + +// Get the first 8 bytes of the device descriptor. +static int +get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_DEVICE<<8; + req.wIndex = 0; + req.wLength = 8; + return usb_send_default_control(pipe, &req, dinfo); +} + +static struct usb_config_descriptor * +get_device_config(struct usb_pipe *pipe) +{ + struct usb_config_descriptor cfg; + + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_CONFIG<<8; + req.wIndex = 0; + req.wLength = sizeof(cfg); + int ret = usb_send_default_control(pipe, &req, &cfg); + if (ret) + return NULL; + + void *config = malloc_tmphigh(cfg.wTotalLength); + if (!config) + return NULL; + req.wLength = cfg.wTotalLength; + ret = usb_send_default_control(pipe, &req, config); + if (ret) { + free(config); + return NULL; + } + //hexdump(config, cfg.wTotalLength); + return config; +} + +static int +set_configuration(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_CONFIGURATION; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return usb_send_default_control(pipe, &req, NULL); +} + + +/**************************************************************** + * Initialization and enumeration + ****************************************************************/ + +static const int speed_to_ctlsize[] = { + [ USB_FULLSPEED ] = 8, + [ USB_LOWSPEED ] = 8, + [ USB_HIGHSPEED ] = 64, + [ USB_SUPERSPEED ] = 512, +}; + +// Assign an address to a device in the default state on the given +// controller. +static int +usb_set_address(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + struct usb_s *cntl = usbdev->hub->cntl; + dprintf(3, "set_address %p\n", cntl); + if (cntl->maxaddr >= USB_MAXADDR) + return -1; + + msleep(USB_TIME_RSTRCY); + + // Create a pipe for the default address. + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = speed_to_ctlsize[usbdev->speed], + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc); + if (!usbdev->defpipe) + return -1; + + // Send set_address command. + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_ADDRESS; + req.wValue = cntl->maxaddr + 1; + req.wIndex = 0; + req.wLength = 0; + int ret = usb_send_default_control(usbdev->defpipe, &req, NULL); + if (ret) { + usb_free_pipe(usbdev, usbdev->defpipe); + return -1; + } + + msleep(USB_TIME_SETADDR_RECOVERY); + + cntl->maxaddr++; + usbdev->devaddr = cntl->maxaddr; + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + return 0; +} + +// Called for every found device - see if a driver is available for +// this device and do setup if so. +static int +configure_usb_device(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + dprintf(3, "config_usb: %p\n", usbdev->defpipe); + + // Set the max packet size for endpoint 0 of this device. + struct usb_device_descriptor dinfo; + int ret = get_device_info8(usbdev->defpipe, &dinfo); + if (ret) + return 0; + u16 maxpacket = dinfo.bMaxPacketSize0; + if (dinfo.bcdUSB >= 0x0300) + maxpacket = 1 << dinfo.bMaxPacketSize0; + dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%d\n" + , dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass + , dinfo.bDeviceProtocol, maxpacket); + if (maxpacket < 8) + return 0; + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = maxpacket, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_realloc_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + + // Get configuration + struct usb_config_descriptor *config = get_device_config(usbdev->defpipe); + if (!config) + return 0; + + // Determine if a driver exists for this device - only look at the + // first interface of the first configuration. + struct usb_interface_descriptor *iface = (void*)(&config[1]); + if (iface->bInterfaceClass != USB_CLASS_HID + && iface->bInterfaceClass != USB_CLASS_MASS_STORAGE + && iface->bInterfaceClass != USB_CLASS_HUB) + // Not a supported device. + goto fail; + + // Set the configuration. + ret = set_configuration(usbdev->defpipe, config->bConfigurationValue); + if (ret) + goto fail; + + // Configure driver. + usbdev->config = config; + usbdev->iface = iface; + usbdev->imax = (void*)config + config->wTotalLength - (void*)iface; + if (iface->bInterfaceClass == USB_CLASS_HUB) + ret = usb_hub_setup(usbdev); + else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) { + if (iface->bInterfaceProtocol == US_PR_BULK) + ret = usb_msc_setup(usbdev); + if (iface->bInterfaceProtocol == US_PR_UAS) + ret = usb_uas_setup(usbdev); + } else + ret = usb_hid_setup(usbdev); + if (ret) + goto fail; + + free(config); + return 1; +fail: + free(config); + return 0; +} + +static void +usb_hub_port_setup(void *data) +{ + struct usbdevice_s *usbdev = data; + struct usbhub_s *hub = usbdev->hub; + u32 port = usbdev->port; + + for (;;) { + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret > 0) + // Device connected. + break; + if (ret < 0 || timer_check(hub->detectend)) + // No device found. + goto done; + msleep(5); + } + + // XXX - wait USB_TIME_ATTDB time? + + // Reset port and determine device speed + mutex_lock(&hub->cntl->resetlock); + int ret = hub->op->reset(hub, port); + if (ret < 0) + // Reset failed + goto resetfail; + usbdev->speed = ret; + + // Set address of port + ret = usb_set_address(usbdev); + if (ret) { + hub->op->disconnect(hub, port); + goto resetfail; + } + mutex_unlock(&hub->cntl->resetlock); + + // Configure the device + int count = configure_usb_device(usbdev); + usb_free_pipe(usbdev, usbdev->defpipe); + if (!count) + hub->op->disconnect(hub, port); + hub->devcount += count; +done: + hub->threads--; + free(usbdev); + return; + +resetfail: + mutex_unlock(&hub->cntl->resetlock); + goto done; +} + +void +usb_enumerate(struct usbhub_s *hub) +{ + u32 portcount = hub->portcount; + hub->threads = portcount; + hub->detectend = timer_calc(USB_TIME_SIGATT); + + // Launch a thread for every port. + int i; + for (i=0; i<portcount; i++) { + struct usbdevice_s *usbdev = malloc_tmphigh(sizeof(*usbdev)); + if (!usbdev) { + warn_noalloc(); + continue; + } + memset(usbdev, 0, sizeof(*usbdev)); + usbdev->hub = hub; + usbdev->port = i; + run_thread(usb_hub_port_setup, usbdev); + } + + // Wait for threads to complete. + while (hub->threads) + yield(); +} + +void +__usb_setup(void *data) +{ + dprintf(3, "init usb\n"); + xhci_setup(); + ehci_setup(); + uhci_setup(); + ohci_setup(); +} + +void +usb_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_USB) + return; + run_thread(__usb_setup, NULL); +} diff --git a/qemu/roms/seabios/src/hw/usb.h b/qemu/roms/seabios/src/hw/usb.h new file mode 100644 index 000000000..efb5e6f9c --- /dev/null +++ b/qemu/roms/seabios/src/hw/usb.h @@ -0,0 +1,254 @@ +// USB functions and data. +#ifndef __USB_H +#define __USB_H + +#include "stacks.h" // struct mutex_s + +// Information on a USB end point. +struct usb_pipe { + union { + struct usb_s *cntl; + struct usb_pipe *freenext; + }; + u8 type; + u8 ep; + u8 devaddr; + u8 speed; + u16 maxpacket; + u8 eptype; +}; + +// Common information for usb devices. +struct usbdevice_s { + struct usbhub_s *hub; + struct usb_pipe *defpipe; + u32 slotid; + u32 port; + struct usb_config_descriptor *config; + struct usb_interface_descriptor *iface; + int imax; + u8 speed; + u8 devaddr; +}; + +// Common information for usb controllers. +struct usb_s { + struct usb_pipe *freelist; + struct mutex_s resetlock; + struct pci_device *pci; + u8 type; + u8 maxaddr; +}; + +// Information for enumerating USB hubs +struct usbhub_s { + struct usbhub_op_s *op; + struct usbdevice_s *usbdev; + struct usb_s *cntl; + struct mutex_s lock; + u32 detectend; + u32 port; + u32 threads; + u32 portcount; + u32 devcount; +}; + +// Hub callback (32bit) info +struct usbhub_op_s { + int (*detect)(struct usbhub_s *hub, u32 port); + int (*reset)(struct usbhub_s *hub, u32 port); + void (*disconnect)(struct usbhub_s *hub, u32 port); +}; + +#define USB_TYPE_UHCI 1 +#define USB_TYPE_OHCI 2 +#define USB_TYPE_EHCI 3 +#define USB_TYPE_XHCI 4 + +#define USB_FULLSPEED 0 +#define USB_LOWSPEED 1 +#define USB_HIGHSPEED 2 +#define USB_SUPERSPEED 3 + +#define USB_MAXADDR 127 + + +/**************************************************************** + * usb structs and flags + ****************************************************************/ + +// USB mandated timings (in ms) +#define USB_TIME_SIGATT 100 +#define USB_TIME_ATTDB 100 +#define USB_TIME_DRST 10 +#define USB_TIME_DRSTR 50 +#define USB_TIME_RSTRCY 10 + +#define USB_TIME_STATUS 50 +#define USB_TIME_DATAIN 500 +#define USB_TIME_COMMAND 5000 + +#define USB_TIME_SETADDR_RECOVERY 2 + +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SETUP 0x2d + +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} PACKED; + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_ENDPOINT_COMPANION 0x30 + +struct usb_device_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} PACKED; + +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 + +struct usb_config_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; +} PACKED; + +struct usb_interface_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +} PACKED; + +struct usb_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +} PACKED; + +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + +#define USB_CONTROL_SETUP_SIZE 8 + + +/**************************************************************** + * usb mass storage flags + ****************************************************************/ + +#define US_SC_ATAPI_8020 0x02 +#define US_SC_ATAPI_8070 0x05 +#define US_SC_SCSI 0x06 + +#define US_PR_BULK 0x50 /* bulk-only transport */ +#define US_PR_UAS 0x62 /* usb attached scsi */ + +/**************************************************************** + * function defs + ****************************************************************/ + +// usb.c +int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); +int usb_poll_intr(struct usb_pipe *pipe, void *data); +int usb_32bit_pipe(struct usb_pipe *pipe_fl); +struct usb_pipe *usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +void usb_free_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe); +int usb_send_default_control(struct usb_pipe *pipe + , const struct usb_ctrlrequest *req, void *data); +int usb_is_freelist(struct usb_s *cntl, struct usb_pipe *pipe); +void usb_add_freelist(struct usb_pipe *pipe); +struct usb_pipe *usb_get_freelist(struct usb_s *cntl, u8 eptype); +void usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_get_period(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_xfer_time(struct usb_pipe *pipe, int datalen); +struct usb_endpoint_descriptor *usb_find_desc(struct usbdevice_s *usbdev + , int type, int dir); +void usb_enumerate(struct usbhub_s *hub); +void usb_setup(void); + +#endif // usb.h diff --git a/qemu/roms/seabios/src/hw/virtio-blk.c b/qemu/roms/seabios/src/hw/virtio-blk.c new file mode 100644 index 000000000..e2dbd3c94 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-blk.c @@ -0,0 +1,177 @@ +// Virtio block boot support. +// +// Copyright (C) 2010 Red Hat Inc. +// +// Authors: +// Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "config.h" // CONFIG_* +#include "block.h" // struct drive_s +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-blk.h" + +struct virtiodrive_s { + struct drive_s drive; + struct vring_virtqueue *vq; + u16 ioaddr; +}; + +static int +virtio_blk_op(struct disk_op_s *op, int write) +{ + struct virtiodrive_s *vdrive_gf = + container_of(op->drive_gf, struct virtiodrive_s, drive); + struct vring_virtqueue *vq = GET_GLOBALFLAT(vdrive_gf->vq); + struct virtio_blk_outhdr hdr = { + .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN, + .ioprio = 0, + .sector = op->lba, + }; + u8 status = VIRTIO_BLK_S_UNSUPP; + struct vring_list sg[] = { + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr), + .length = sizeof(hdr), + }, + { + .addr = op->buf_fl, + .length = GET_GLOBALFLAT(vdrive_gf->drive.blksize) * op->count, + }, + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &status), + .length = sizeof(status), + }, + }; + + /* Add to virtqueue and kick host */ + if (write) + vring_add_buf(vq, sg, 2, 1, 0, 0); + else + vring_add_buf(vq, sg, 1, 2, 0, 0); + vring_kick(GET_GLOBALFLAT(vdrive_gf->ioaddr), vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(GET_GLOBALFLAT(vdrive_gf->ioaddr)); + + return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +process_virtio_blk_op(struct disk_op_s *op) +{ + if (! CONFIG_VIRTIO_BLK) + return 0; + switch (op->command) { + case CMD_READ: + return virtio_blk_op(op, 0); + case CMD_WRITE: + return virtio_blk_op(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + return DISK_RET_EPARAM; + } +} + +static void +init_virtio_blk(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive)); + if (!vdrive) { + warn_noalloc(); + return; + } + memset(vdrive, 0, sizeof(*vdrive)); + vdrive->drive.type = DTYPE_VIRTIO_BLK; + vdrive->drive.cntl_id = bdf; + + u16 ioaddr = vp_init_simple(bdf); + vdrive->ioaddr = ioaddr; + if (vp_find_vq(ioaddr, 0, &vdrive->vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-blk %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + struct virtio_blk_config cfg; + vp_get(ioaddr, 0, &cfg, sizeof(cfg)); + + u32 f = vp_get_features(ioaddr); + vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? + cfg.blk_size : DISK_SECTOR_SIZE; + + vdrive->drive.sectors = cfg.capacity; + dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize); + goto fail; + } + + vdrive->drive.pchs.cylinder = cfg.cylinders; + vdrive->drive.pchs.head = cfg.heads; + vdrive->drive.pchs.sector = cfg.sectors; + char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + + boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci)); + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + return; + +fail: + vp_reset(ioaddr); + free(vdrive->vq); + free(vdrive); +} + +void +virtio_blk_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_BLK) + return; + + dprintf(3, "init virtio-blk\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_BLK) + continue; + init_virtio_blk(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/virtio-blk.h b/qemu/roms/seabios/src/hw/virtio-blk.h new file mode 100644 index 000000000..b233c744b --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-blk.h @@ -0,0 +1,43 @@ +#ifndef _VIRTIO_BLK_H +#define _VIRTIO_BLK_H + +struct virtio_blk_config +{ + u64 capacity; + u32 size_max; + u32 seg_max; + u16 cylinders; + u8 heads; + u8 sectors; + u32 blk_size; + u8 physical_block_exp; + u8 alignment_offset; + u16 min_io_size; + u32 opt_io_size; +} __attribute__((packed)); + +#define VIRTIO_BLK_F_BLK_SIZE 6 + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr { + /* VIRTIO_BLK_T* */ + u32 type; + /* io priority. */ + u32 ioprio; + /* Sector (ie. 512 byte offset) */ + u64 sector; +}; + +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +struct disk_op_s; +int process_virtio_blk_op(struct disk_op_s *op); +void virtio_blk_setup(void); + +#endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/seabios/src/hw/virtio-pci.c b/qemu/roms/seabios/src/hw/virtio-pci.c new file mode 100644 index 000000000..b9b3ab1e3 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-pci.c @@ -0,0 +1,97 @@ +/* virtio-pci.c - pci interface for virtio interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori <aliguori@us.ibm.com> + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + */ + +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_config_readl +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "virtio-pci.h" +#include "virtio-ring.h" + +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq) +{ + u16 num; + + ASSERT32FLAT(); + struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq)); + if (!vq) { + warn_noalloc(); + goto fail; + } + memset(vq, 0, sizeof(*vq)); + + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* check if the queue is available */ + + num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num) { + dprintf(1, "ERROR: queue size is 0\n"); + goto fail; + } + + if (num > MAX_QUEUE_NUM) { + dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + goto fail; + } + + /* check if the queue is already active */ + + if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + dprintf(1, "ERROR: queue already active\n"); + goto fail; + } + + vq->queue_index = queue_index; + + /* initialize the queue */ + + struct vring * vr = &vq->vring; + vring_init(vr, num, (unsigned char*)&vq->queue); + + /* activate the queue + * + * NOTE: vr->desc is initialized by vring_init() + */ + + outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, + ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return num; + +fail: + free(vq); + *p_vq = NULL; + return -1; +} + +u16 vp_init_simple(u16 bdf) +{ + u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & + PCI_BASE_ADDRESS_IO_MASK; + + vp_reset(ioaddr); + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER ); + return ioaddr; +} diff --git a/qemu/roms/seabios/src/hw/virtio-pci.h b/qemu/roms/seabios/src/hw/virtio-pci.h new file mode 100644 index 000000000..bc04b039e --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-pci.h @@ -0,0 +1,105 @@ +#ifndef _VIRTIO_PCI_H +#define _VIRTIO_PCI_H + +#include "x86.h" // inl + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 + +/* The remaining space is defined by each driver as the per-driver + * configuration space */ +#define VIRTIO_PCI_CONFIG 20 + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +static inline u32 vp_get_features(unsigned int ioaddr) +{ + return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); +} + +static inline void vp_set_features(unsigned int ioaddr, u32 features) +{ + outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES); +} + +static inline void vp_get(unsigned int ioaddr, unsigned offset, + void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i); +} + +static inline u8 vp_get_status(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_STATUS); +} + +static inline void vp_set_status(unsigned int ioaddr, u8 status) +{ + if (status == 0) /* reset */ + return; + outb(status, ioaddr + VIRTIO_PCI_STATUS); +} + +static inline u8 vp_get_isr(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_reset(unsigned int ioaddr) +{ + outb(0, ioaddr + VIRTIO_PCI_STATUS); + (void)inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_notify(unsigned int ioaddr, int queue_index) +{ + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); +} + +static inline void vp_del_vq(unsigned int ioaddr, int queue_index) +{ + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* deactivate the queue */ + + outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); +} + +struct vring_virtqueue; +u16 vp_init_simple(u16 bdf); +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq); +#endif /* _VIRTIO_PCI_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-ring.c b/qemu/roms/seabios/src/hw/virtio-ring.c new file mode 100644 index 000000000..97e0b3487 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-ring.c @@ -0,0 +1,149 @@ +/* virtio-pci.c - virtio ring management + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + * + * + */ + +#include "biosvar.h" // GET_GLOBAL +#include "output.h" // panic +#include "virtio-ring.h" +#include "virtio-pci.h" + +#define BUG() do { \ + panic("BUG: failure at %d/%s()!\n", __LINE__, __func__); \ + } while (0) +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) + +/* + * vring_more_used + * + * is there some used buffers ? + * + */ + +int vring_more_used(struct vring_virtqueue *vq) +{ + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + int more = GET_LOWFLAT(vq->last_used_idx) != GET_LOWFLAT(used->idx); + /* Make sure ring reads are done after idx read above. */ + smp_rmb(); + return more; +} + +/* + * vring_free + * + * put at the begin of the free list the current desc[head] + */ + +void vring_detach(struct vring_virtqueue *vq, unsigned int head) +{ + struct vring *vr = &vq->vring; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + unsigned int i; + + /* find end of given descriptor */ + + i = head; + while (GET_LOWFLAT(desc[i].flags) & VRING_DESC_F_NEXT) + i = GET_LOWFLAT(desc[i].next); + + /* link it with free list and point to it */ + + SET_LOWFLAT(desc[i].next, GET_LOWFLAT(vq->free_head)); + SET_LOWFLAT(vq->free_head, head); +} + +/* + * vring_get_buf + * + * get a buffer from the used list + * + */ + +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) +{ + struct vring *vr = &vq->vring; + struct vring_used_elem *elem; + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + u32 id; + int ret; + +// BUG_ON(!vring_more_used(vq)); + + elem = &used->ring[GET_LOWFLAT(vq->last_used_idx) % GET_LOWFLAT(vr->num)]; + id = GET_LOWFLAT(elem->id); + if (len != NULL) + *len = GET_LOWFLAT(elem->len); + + ret = GET_LOWFLAT(vq->vdata[id]); + + vring_detach(vq, id); + + SET_LOWFLAT(vq->last_used_idx, GET_LOWFLAT(vq->last_used_idx) + 1); + + return ret; +} + +void vring_add_buf(struct vring_virtqueue *vq, + struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added) +{ + struct vring *vr = &vq->vring; + int i, av, head, prev; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + BUG_ON(out + in == 0); + + prev = 0; + head = GET_LOWFLAT(vq->free_head); + for (i = head; out; i = GET_LOWFLAT(desc[i].next), out--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + for ( ; in; i = GET_LOWFLAT(desc[i].next), in--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT|VRING_DESC_F_WRITE); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + SET_LOWFLAT(desc[prev].flags, + GET_LOWFLAT(desc[prev].flags) & ~VRING_DESC_F_NEXT); + + SET_LOWFLAT(vq->free_head, i); + + SET_LOWFLAT(vq->vdata[head], index); + + av = (GET_LOWFLAT(avail->idx) + num_added) % GET_LOWFLAT(vr->num); + SET_LOWFLAT(avail->ring[av], head); +} + +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +{ + struct vring *vr = &vq->vring; + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + /* Make sure idx update is done after ring write. */ + smp_wmb(); + SET_LOWFLAT(avail->idx, GET_LOWFLAT(avail->idx) + num_added); + + vp_notify(ioaddr, GET_LOWFLAT(vq->queue_index)); +} diff --git a/qemu/roms/seabios/src/hw/virtio-ring.h b/qemu/roms/seabios/src/hw/virtio-ring.h new file mode 100644 index 000000000..b7a7aafb2 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-ring.h @@ -0,0 +1,131 @@ +#ifndef _VIRTIO_RING_H +#define _VIRTIO_RING_H + +#include "types.h" // u64 +#include "memmap.h" // PAGE_SIZE + +#define PAGE_SHIFT 12 +#define PAGE_MASK (PAGE_SIZE-1) + +#define virt_to_phys(v) (unsigned long)(v) +#define phys_to_virt(p) (void*)(p) +/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ +#define smp_rmb() barrier() +#define smp_wmb() barrier() + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +#define MAX_QUEUE_NUM (128) + +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +#define VRING_USED_F_NO_NOTIFY 1 + +struct vring_desc +{ + u64 addr; + u32 len; + u16 flags; + u16 next; +}; + +struct vring_avail +{ + u16 flags; + u16 idx; + u16 ring[0]; +}; + +struct vring_used_elem +{ + u32 id; + u32 len; +}; + +struct vring_used +{ + u16 flags; + u16 idx; + struct vring_used_elem ring[]; +}; + +struct vring { + unsigned int num; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +#define vring_size(num) \ + (((((sizeof(struct vring_desc) * num) + \ + (sizeof(struct vring_avail) + sizeof(u16) * num)) \ + + PAGE_MASK) & ~PAGE_MASK) + \ + (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) + +typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)]; + +struct vring_virtqueue { + virtio_queue_t queue; + struct vring vring; + u16 free_head; + u16 last_used_idx; + u16 vdata[MAX_QUEUE_NUM]; + /* PCI */ + int queue_index; +}; + +struct vring_list { + char *addr; + unsigned int length; +}; + +static inline void vring_init(struct vring *vr, + unsigned int num, unsigned char *queue) +{ + unsigned int i; + unsigned long pa; + + ASSERT32FLAT(); + vr->num = num; + + /* physical address of desc must be page aligned */ + + pa = virt_to_phys(queue); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->desc = phys_to_virt(pa); + + vr->avail = (struct vring_avail *)&vr->desc[num]; + /* disable interrupts */ + vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + + /* physical address of used must be page aligned */ + + pa = virt_to_phys(&vr->avail->ring[num]); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->used = phys_to_virt(pa); + + for (i = 0; i < num - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = 0; +} + +int vring_more_used(struct vring_virtqueue *vq); +void vring_detach(struct vring_virtqueue *vq, unsigned int head); +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); +void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added); +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); + +#endif /* _VIRTIO_RING_H_ */ diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.c b/qemu/roms/seabios/src/hw/virtio-scsi.c new file mode 100644 index 000000000..8f966875b --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-scsi.c @@ -0,0 +1,187 @@ +// Virtio SCSI boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-scsi.h" + +struct virtio_lun_s { + struct drive_s drive; + struct pci_device *pci; + struct vring_virtqueue *vq; + u16 ioaddr; + u16 target; + u16 lun; +}; + +static int +virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct virtio_scsi_req_cmd req; + struct virtio_scsi_resp_cmd resp; + struct vring_list sg[3]; + + memset(&req, 0, sizeof(req)); + req.lun[0] = 1; + req.lun[1] = target; + req.lun[2] = (lun >> 8) | 0x40; + req.lun[3] = (lun & 0xff); + memcpy(req.cdb, cdbcmd, 16); + + u32 len = op->count * blocksize; + int datain = cdb_is_read(cdbcmd, blocksize); + int in_num = (datain ? 2 : 1); + int out_num = (len ? 3 : 2) - in_num; + + sg[0].addr = MAKE_FLATPTR(GET_SEG(SS), &req); + sg[0].length = sizeof(req); + + sg[out_num].addr = MAKE_FLATPTR(GET_SEG(SS), &resp); + sg[out_num].length = sizeof(resp); + + if (len) { + int data_idx = (datain ? 2 : 1); + sg[data_idx].addr = op->buf_fl; + sg[data_idx].length = len; + } + + /* Add to virtqueue and kick host */ + vring_add_buf(vq, sg, out_num, in_num, 0, 0); + vring_kick(ioaddr, vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(ioaddr); + + if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) { + return DISK_RET_SUCCESS; + } + return DISK_RET_EBADTRACK; +} + +int +virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct virtio_lun_s *vlun_gf = + container_of(op->drive_gf, struct virtio_lun_s, drive); + + return virtio_scsi_cmd(GET_GLOBALFLAT(vlun_gf->ioaddr), + GET_GLOBALFLAT(vlun_gf->vq), op, cdbcmd, + GET_GLOBALFLAT(vlun_gf->target), + GET_GLOBALFLAT(vlun_gf->lun), + blocksize); +} + +static int +virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target, u16 lun) +{ + struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun)); + if (!vlun) { + warn_noalloc(); + return -1; + } + memset(vlun, 0, sizeof(*vlun)); + vlun->drive.type = DTYPE_VIRTIO_SCSI; + vlun->drive.cntl_id = pci->bdf; + vlun->pci = pci; + vlun->ioaddr = ioaddr; + vlun->vq = vq; + vlun->target = target; + vlun->lun = lun; + + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio); + if (ret) + goto fail; + return 0; + +fail: + free(vlun); + return -1; +} + +static int +virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0); + return ret < 0 ? 0 : 1; +} + +static void +init_virtio_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct vring_virtqueue *vq = NULL; + u16 ioaddr = vp_init_simple(bdf); + if (vp_find_vq(ioaddr, 2, &vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-scsi %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + + int i, tot; + for (tot = 0, i = 0; i < 256; i++) + tot += virtio_scsi_scan_target(pci, ioaddr, vq, i); + + if (!tot) + goto fail; + + return; + +fail: + vp_reset(ioaddr); + free(vq); +} + +void +virtio_scsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_SCSI) + return; + + dprintf(3, "init virtio-scsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI) + continue; + init_virtio_scsi(pci); + } +} diff --git a/qemu/roms/seabios/src/hw/virtio-scsi.h b/qemu/roms/seabios/src/hw/virtio-scsi.h new file mode 100644 index 000000000..96c3701d2 --- /dev/null +++ b/qemu/roms/seabios/src/hw/virtio-scsi.h @@ -0,0 +1,47 @@ +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +struct virtio_scsi_config +{ + u32 num_queues; + u32 seg_max; + u32 max_sectors; + u32 cmd_per_lun; + u32 event_info_size; + u32 sense_size; + u32 cdb_size; + u16 max_channel; + u16 max_target; + u32 max_lun; +} __attribute__((packed)); + +/* This is the first element of the "out" scatter-gather list. */ +struct virtio_scsi_req_cmd { + u8 lun[8]; + u64 id; + u8 task_attr; + u8 prio; + u8 crn; + char cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); + +/* This is the first element of the "in" scatter-gather list. */ +struct virtio_scsi_resp_cmd { + u32 sense_len; + u32 residual; + u16 status_qualifier; + u8 status; + u8 response; + u8 sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); + +#define VIRTIO_SCSI_S_OK 0 + +struct disk_op_s; +int virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void virtio_scsi_setup(void); + +#endif /* _VIRTIO_SCSI_H */ diff --git a/qemu/roms/seabios/src/jpeg.c b/qemu/roms/seabios/src/jpeg.c new file mode 100644 index 000000000..c2138edab --- /dev/null +++ b/qemu/roms/seabios/src/jpeg.c @@ -0,0 +1,1055 @@ +/* + * Copyright (C) 2001, Novell Inc. + * Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Novell nor the names of the contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * a tiny jpeg decoder. + * + * written in August 2001 by Michael Schroeder <mls@suse.de> + * + */ + +#define __LITTLE_ENDIAN +#include "malloc.h" +#include "string.h" +#include "util.h" +#define ISHIFT 11 + +#define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5)) +#define IMULT(a, b) (((a) * (b)) >> ISHIFT) +#define ITOINT(a) ((a) >> ISHIFT) + +#ifndef __P +# define __P(x) x +#endif + +/* special markers */ +#define M_BADHUFF -1 +#define M_EOF 0x80 + +struct in { + unsigned char *p; + unsigned int bits; + int left; + int marker; + + int (*func) __P((void *)); + void *data; +}; + +/*********************************/ +struct dec_hufftbl; +struct enc_hufftbl; + +union hufftblp { + struct dec_hufftbl *dhuff; + struct enc_hufftbl *ehuff; +}; + +struct scan { + int dc; /* old dc value */ + + union hufftblp hudc; + union hufftblp huac; + int next; /* when to switch to next scan */ + + int cid; /* component id */ + int hv; /* horiz/vert, copied from comp */ + int tq; /* quant tbl, copied from comp */ +}; + +/*********************************/ + +#define DECBITS 10 /* seems to be the optimum */ + +struct dec_hufftbl { + int maxcode[17]; + int valptr[16]; + unsigned char vals[256]; + unsigned int llvals[1 << DECBITS]; +}; + +static void decode_mcus __P((struct in *, int *, int, struct scan *, int *)); +static int dec_readmarker __P((struct in *)); +static void dec_makehuff __P((struct dec_hufftbl *, int *, unsigned char *)); + +static void setinput __P((struct in *, unsigned char *)); +/*********************************/ + +#undef PREC +#define PREC int + +static void idctqtab __P((unsigned char *, PREC *)); +static void idct __P((int *, int *, PREC *, PREC, int)); +static void scaleidctqtab __P((PREC *, PREC)); + +/*********************************/ + +static void initcol __P((PREC[][64])); + +static void col221111 __P((int *, unsigned char *, int)); +static void col221111_16 __P((int *, unsigned char *, int)); +static void col221111_32 __P((int *, unsigned char *, int)); + +/*********************************/ + +#define ERR_NO_SOI 1 +#define ERR_NOT_8BIT 2 +#define ERR_HEIGHT_MISMATCH 3 +#define ERR_WIDTH_MISMATCH 4 +#define ERR_BAD_WIDTH_OR_HEIGHT 5 +#define ERR_TOO_MANY_COMPPS 6 +#define ERR_ILLEGAL_HV 7 +#define ERR_QUANT_TABLE_SELECTOR 8 +#define ERR_NOT_YCBCR_221111 9 +#define ERR_UNKNOWN_CID_IN_SCAN 10 +#define ERR_NOT_SEQUENTIAL_DCT 11 +#define ERR_WRONG_MARKER 12 +#define ERR_NO_EOI 13 +#define ERR_BAD_TABLES 14 +#define ERR_DEPTH_MISMATCH 15 + +/*********************************/ + +#define M_SOI 0xd8 +#define M_APP0 0xe0 +#define M_DQT 0xdb +#define M_SOF0 0xc0 +#define M_DHT 0xc4 +#define M_DRI 0xdd +#define M_SOS 0xda +#define M_RST0 0xd0 +#define M_EOI 0xd9 +#define M_COM 0xfe + +struct comp { + int cid; + int hv; + int tq; +}; + +#define MAXCOMP 4 +struct jpginfo { + int nc; /* number of components */ + int ns; /* number of scans */ + int dri; /* restart interval */ + int nm; /* mcus til next marker */ + int rm; /* next restart marker */ +}; + +struct jpeg_decdata { + int dcts[6 * 64 + 16]; + int out[64 * 6]; + int dquant[3][64]; + + unsigned char *datap; + struct jpginfo info; + struct comp comps[MAXCOMP]; + struct scan dscans[MAXCOMP]; + unsigned char quant[4][64]; + struct dec_hufftbl dhuff[4]; + struct in in; + + int height, width; +}; + +static int getbyte(struct jpeg_decdata *jpeg) +{ + return *jpeg->datap++; +} + +static int getword(struct jpeg_decdata *jpeg) +{ + int c1, c2; + c1 = *jpeg->datap++; + c2 = *jpeg->datap++; + return c1 << 8 | c2; +} + +static int readtables(struct jpeg_decdata *jpeg, int till) +{ + int m, l, i, j, lq, pq, tq; + int tc, th, tt; + + for (;;) { + if (getbyte(jpeg) != 0xff) + return -1; + if ((m = getbyte(jpeg)) == till) + break; + + switch (m) { + case 0xc2: + return 0; + + case M_DQT: + lq = getword(jpeg); + while (lq > 2) { + pq = getbyte(jpeg); + tq = pq & 15; + if (tq > 3) + return -1; + pq >>= 4; + if (pq != 0) + return -1; + for (i = 0; i < 64; i++) + jpeg->quant[tq][i] = getbyte(jpeg); + lq -= 64 + 1; + } + break; + + case M_DHT: + l = getword(jpeg); + while (l > 2) { + int hufflen[16], k; + unsigned char huffvals[256]; + + tc = getbyte(jpeg); + th = tc & 15; + tc >>= 4; + tt = tc * 2 + th; + if (tc > 1 || th > 1) + return -1; + for (i = 0; i < 16; i++) + hufflen[i] = getbyte(jpeg); + l -= 1 + 16; + k = 0; + for (i = 0; i < 16; i++) { + for (j = 0; j < hufflen[i]; j++) + huffvals[k++] = getbyte(jpeg); + l -= hufflen[i]; + } + dec_makehuff(jpeg->dhuff + tt, hufflen, huffvals); + } + break; + + case M_DRI: + l = getword(jpeg); + jpeg->info.dri = getword(jpeg); + break; + + default: + l = getword(jpeg); + while (l-- > 2) + getbyte(jpeg); + break; + } + } + return 0; +} + +static void dec_initscans(struct jpeg_decdata *jpeg) +{ + int i; + + jpeg->info.nm = jpeg->info.dri + 1; + jpeg->info.rm = M_RST0; + for (i = 0; i < jpeg->info.ns; i++) + jpeg->dscans[i].dc = 0; +} + +static int dec_checkmarker(struct jpeg_decdata *jpeg) +{ + int i; + + if (dec_readmarker(&jpeg->in) != jpeg->info.rm) + return -1; + jpeg->info.nm = jpeg->info.dri; + jpeg->info.rm = (jpeg->info.rm + 1) & ~0x08; + for (i = 0; i < jpeg->info.ns; i++) + jpeg->dscans[i].dc = 0; + return 0; +} + +struct jpeg_decdata *jpeg_alloc(void) +{ + struct jpeg_decdata *jpeg = malloc_tmphigh(sizeof(*jpeg)); + return jpeg; +} + +int jpeg_decode(struct jpeg_decdata *jpeg, unsigned char *buf) +{ + int i, j, m, tac, tdc; + + if (!jpeg || !buf) + return -1; + jpeg->datap = buf; + if (getbyte(jpeg) != 0xff) + return ERR_NO_SOI; + if (getbyte(jpeg) != M_SOI) + return ERR_NO_SOI; + if (readtables(jpeg, M_SOF0)) + return ERR_BAD_TABLES; + getword(jpeg); + i = getbyte(jpeg); + if (i != 8) + return ERR_NOT_8BIT; + jpeg->height = getword(jpeg); + jpeg->width = getword(jpeg); + if ((jpeg->height & 15) || (jpeg->width & 15)) + return ERR_BAD_WIDTH_OR_HEIGHT; + jpeg->info.nc = getbyte(jpeg); + if (jpeg->info.nc > MAXCOMP) + return ERR_TOO_MANY_COMPPS; + for (i = 0; i < jpeg->info.nc; i++) { + int h, v; + jpeg->comps[i].cid = getbyte(jpeg); + jpeg->comps[i].hv = getbyte(jpeg); + v = jpeg->comps[i].hv & 15; + h = jpeg->comps[i].hv >> 4; + jpeg->comps[i].tq = getbyte(jpeg); + if (h > 3 || v > 3) + return ERR_ILLEGAL_HV; + if (jpeg->comps[i].tq > 3) + return ERR_QUANT_TABLE_SELECTOR; + } + if (readtables(jpeg, M_SOS)) + return ERR_BAD_TABLES; + getword(jpeg); + jpeg->info.ns = getbyte(jpeg); + if (jpeg->info.ns != 3) + return ERR_NOT_YCBCR_221111; + for (i = 0; i < 3; i++) { + jpeg->dscans[i].cid = getbyte(jpeg); + tdc = getbyte(jpeg); + tac = tdc & 15; + tdc >>= 4; + if (tdc > 1 || tac > 1) + return ERR_QUANT_TABLE_SELECTOR; + for (j = 0; j < jpeg->info.nc; j++) + if (jpeg->comps[j].cid == jpeg->dscans[i].cid) + break; + if (j == jpeg->info.nc) + return ERR_UNKNOWN_CID_IN_SCAN; + jpeg->dscans[i].hv = jpeg->comps[j].hv; + jpeg->dscans[i].tq = jpeg->comps[j].tq; + jpeg->dscans[i].hudc.dhuff = &jpeg->dhuff[tdc]; + jpeg->dscans[i].huac.dhuff = &jpeg->dhuff[2 + tac]; + } + + i = getbyte(jpeg); + j = getbyte(jpeg); + m = getbyte(jpeg); + + if (i != 0 || j != 63 || m != 0) + return ERR_NOT_SEQUENTIAL_DCT; + + if (jpeg->dscans[0].cid != 1 || jpeg->dscans[1].cid != 2 + || jpeg->dscans[2].cid != 3) + return ERR_NOT_YCBCR_221111; + + if (jpeg->dscans[0].hv != 0x22 || jpeg->dscans[1].hv != 0x11 + || jpeg->dscans[2].hv != 0x11) + return ERR_NOT_YCBCR_221111; + + idctqtab(jpeg->quant[jpeg->dscans[0].tq], jpeg->dquant[0]); + idctqtab(jpeg->quant[jpeg->dscans[1].tq], jpeg->dquant[1]); + idctqtab(jpeg->quant[jpeg->dscans[2].tq], jpeg->dquant[2]); + initcol(jpeg->dquant); + setinput(&jpeg->in, jpeg->datap); + +#if 0 + /* landing zone */ + img[len] = 0; + img[len + 1] = 0xff; + img[len + 2] = M_EOF; +#endif + + dec_initscans(jpeg); + + return 0; +} + +void jpeg_get_size(struct jpeg_decdata *jpeg, int *width, int *height) +{ + *width = jpeg->width; + *height = jpeg->height; +} + +int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest) +{ + int m, mcusx, mcusy, mx, my, mloffset, jpgbpl; + int max[6]; + + if (jpeg->height != height) + return ERR_HEIGHT_MISMATCH; + if (jpeg->width != width) + return ERR_WIDTH_MISMATCH; + + jpgbpl = width * depth / 8; + mloffset = bytes_per_line_dest > jpgbpl ? bytes_per_line_dest : jpgbpl; + + mcusx = jpeg->width >> 4; + mcusy = jpeg->height >> 4; + + jpeg->dscans[0].next = 6 - 4; + jpeg->dscans[1].next = 6 - 4 - 1; + jpeg->dscans[2].next = 6 - 4 - 1 - 1; /* 411 encoding */ + for (my = 0; my < mcusy; my++) { + for (mx = 0; mx < mcusx; mx++) { + if (jpeg->info.dri && !--jpeg->info.nm) + if (dec_checkmarker(jpeg)) + return ERR_WRONG_MARKER; + + decode_mcus(&jpeg->in, jpeg->dcts, 6, jpeg->dscans, max); + idct(jpeg->dcts, jpeg->out, jpeg->dquant[0], + IFIX(128.5), max[0]); + idct(jpeg->dcts + 64, jpeg->out + 64, jpeg->dquant[0], + IFIX(128.5), max[1]); + idct(jpeg->dcts + 128, jpeg->out + 128, jpeg->dquant[0], + IFIX(128.5), max[2]); + idct(jpeg->dcts + 192, jpeg->out + 192, jpeg->dquant[0], + IFIX(128.5), max[3]); + idct(jpeg->dcts + 256, jpeg->out + 256, jpeg->dquant[1], + IFIX(0.5), max[4]); + idct(jpeg->dcts + 320, jpeg->out + 320, jpeg->dquant[2], + IFIX(0.5), max[5]); + + switch (depth) { + case 32: + col221111_32(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 4), + mloffset); + break; + case 24: + col221111(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 3), + mloffset); + break; + case 16: + col221111_16(jpeg->out, + pic + (my * 16 * mloffset + mx * 16 * 2), + mloffset); + break; + default: + return ERR_DEPTH_MISMATCH; + break; + } + } + } + + m = dec_readmarker(&jpeg->in); + if (m != M_EOI) + return ERR_NO_EOI; + + return 0; +} + +/****************************************************************/ +/************** huffman decoder ***************/ +/****************************************************************/ + +static int fillbits __P((struct in *, int, unsigned int)); +static int dec_rec2 __P((struct in *, struct dec_hufftbl *, int *, int, int)); + +static void setinput(struct in *in, unsigned char *p) +{ + in->p = p; + in->left = 0; + in->bits = 0; + in->marker = 0; +} + +static int fillbits(struct in *in, int le, unsigned int bi) +{ + int b, m; + + if (in->marker) { + if (le <= 16) + in->bits = bi << 16, le += 16; + return le; + } + while (le <= 24) { + b = *in->p++; + if (b == 0xff && (m = *in->p++) != 0) { + if (m == M_EOF) { + if (in->func && (m = in->func(in->data)) == 0) + continue; + } + in->marker = m; + if (le <= 16) + bi = bi << 16, le += 16; + break; + } + bi = bi << 8 | b; + le += 8; + } + in->bits = bi; /* tmp... 2 return values needed */ + return le; +} + +static int dec_readmarker(struct in *in) +{ + int m; + + in->left = fillbits(in, in->left, in->bits); + if ((m = in->marker) == 0) + return 0; + in->left = 0; + in->marker = 0; + return m; +} + +#define LEBI_DCL int le, bi +#define LEBI_GET(in) (le = in->left, bi = in->bits) +#define LEBI_PUT(in) (in->left = le, in->bits = bi) + +#define GETBITS(in, n) ( \ + (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \ + (le -= (n)), \ + bi >> le & ((1 << (n)) - 1) \ +) + +#define UNGETBITS(in, n) ( \ + le += (n) \ +) + + +static int dec_rec2(struct in *in, struct dec_hufftbl *hu, int *runp, + int c, int i) +{ + LEBI_DCL; + + LEBI_GET(in); + if (i) { + UNGETBITS(in, i & 127); + *runp = i >> 8 & 15; + i >>= 16; + } else { + for (i = DECBITS; + (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++); + if (i >= 16) { + in->marker = M_BADHUFF; + return 0; + } + i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2]; + *runp = i >> 4; + i &= 15; + } + if (i == 0) { /* sigh, 0xf0 is 11 bit */ + LEBI_PUT(in); + return 0; + } + /* receive part */ + c = GETBITS(in, i); + if (c < (1 << (i - 1))) + c += (-1 << i) + 1; + LEBI_PUT(in); + return c; +} + +#define DEC_REC(in, hu, r, i) ( \ + r = GETBITS(in, DECBITS), \ + i = hu->llvals[r], \ + i & 128 ? \ + ( \ + UNGETBITS(in, i & 127), \ + r = i >> 8 & 15, \ + i >> 16 \ + ) \ + : \ + ( \ + LEBI_PUT(in), \ + i = dec_rec2(in, hu, &r, r, i), \ + LEBI_GET(in), \ + i \ + ) \ +) + +static void decode_mcus(struct in *in, int *dct, int n, struct scan *sc, + int *maxp) +{ + struct dec_hufftbl *hu; + int i, r, t; + LEBI_DCL; + + memset(dct, 0, n * 64 * sizeof(*dct)); + LEBI_GET(in); + while (n-- > 0) { + hu = sc->hudc.dhuff; + *dct++ = (sc->dc += DEC_REC(in, hu, r, t)); + + hu = sc->huac.dhuff; + i = 63; + while (i > 0) { + t = DEC_REC(in, hu, r, t); + if (t == 0 && r == 0) { + dct += i; + break; + } + dct += r; + *dct++ = t; + i -= r + 1; + } + *maxp++ = 64 - i; + if (n == sc->next) + sc++; + } + LEBI_PUT(in); +} + +static void dec_makehuff(struct dec_hufftbl *hu, int *hufflen, + unsigned char *huffvals) +{ + int code, k, i, j, d, x, c, v; + for (i = 0; i < (1 << DECBITS); i++) + hu->llvals[i] = 0; + + /* + * llvals layout: + * + * value v already known, run r, backup u bits: + * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu + * value unknown, size b bits, run r, backup u bits: + * 000000000000bbbb 0000 rrrr 0 uuuuuuu + * value and size unknown: + * 0000000000000000 0000 0000 0 0000000 + */ + + code = 0; + k = 0; + for (i = 0; i < 16; i++, code <<= 1) { /* sizes */ + hu->valptr[i] = k; + for (j = 0; j < hufflen[i]; j++) { + hu->vals[k] = *huffvals++; + if (i < DECBITS) { + c = code << (DECBITS - 1 - i); + v = hu->vals[k] & 0x0f; /* size */ + for (d = 1 << (DECBITS - 1 - i); --d >= 0;) { + if (v + i < DECBITS) { /* both fit in table */ + x = d >> (DECBITS - 1 - v - i); + if (v && x < (1 << (v - 1))) + x += (-1 << v) + 1; + x = x << 16 | (hu->vals[k] & 0xf0) << 4 | + (DECBITS - (i + 1 + v)) | 128; + } else + x = v << 16 | (hu->vals[k] & 0xf0) << 4 | + (DECBITS - (i + 1)); + hu->llvals[c | d] = x; + } + } + code++; + k++; + } + hu->maxcode[i] = code; + } + hu->maxcode[16] = 0x20000; /* always terminate decode */ +} + +/****************************************************************/ +/************** idct ***************/ +/****************************************************************/ + +#define ONE ((PREC)IFIX(1.)) +#define S2 ((PREC)IFIX(0.382683432)) +#define C2 ((PREC)IFIX(0.923879532)) +#define C4 ((PREC)IFIX(0.707106781)) + +#define S22 ((PREC)IFIX(2 * 0.382683432)) +#define C22 ((PREC)IFIX(2 * 0.923879532)) +#define IC4 ((PREC)IFIX(1 / 0.707106781)) + +#define C3IC1 ((PREC)IFIX(0.847759065)) /* c3/c1 */ +#define C5IC1 ((PREC)IFIX(0.566454497)) /* c5/c1 */ +#define C7IC1 ((PREC)IFIX(0.198912367)) /* c7/c1 */ + +#define XPP(a,b) (t = a + b, b = a - b, a = t) +#define XMP(a,b) (t = a - b, b = a + b, a = t) +#define XPM(a,b) (t = a + b, b = b - a, a = t) + +#define ROT(a,b,s,c) ( t = IMULT(a + b, s), \ + a = IMULT(a, c - s) + t, \ + b = IMULT(b, c + s) - t) + +#define IDCT \ +( \ + XPP(t0, t1), \ + XMP(t2, t3), \ + t2 = IMULT(t2, IC4) - t3, \ + XPP(t0, t3), \ + XPP(t1, t2), \ + XMP(t4, t7), \ + XPP(t5, t6), \ + XMP(t5, t7), \ + t5 = IMULT(t5, IC4), \ + ROT(t4, t6, S22, C22), \ + t6 -= t7, \ + t5 -= t6, \ + t4 -= t5, \ + XPP(t0, t7), \ + XPP(t1, t6), \ + XPP(t2, t5), \ + XPP(t3, t4) \ +) + +static unsigned char zig2[64] = { + 0, 2, 3, 9, 10, 20, 21, 35, + 14, 16, 25, 31, 39, 46, 50, 57, + 5, 7, 12, 18, 23, 33, 37, 48, + 27, 29, 41, 44, 52, 55, 59, 62, + 15, 26, 30, 40, 45, 51, 56, 58, + 1, 4, 8, 11, 19, 22, 34, 36, + 28, 42, 43, 53, 54, 60, 61, 63, + 6, 13, 17, 24, 32, 38, 47, 49 +}; + +static void idct(int *in, int *out, PREC * quant, PREC off, int max) +{ + PREC t0, t1, t2, t3, t4, t5, t6, t7, t; + PREC tmp[64], *tmpp; + int i, j; + unsigned char *zig2p; + + t0 = off; + if (max == 1) { + t0 += in[0] * quant[0]; + for (i = 0; i < 64; i++) + out[i] = ITOINT(t0); + return; + } + zig2p = zig2; + tmpp = tmp; + for (i = 0; i < 8; i++) { + j = *zig2p++; + t0 += in[j] * quant[j]; + j = *zig2p++; + t5 = in[j] * quant[j]; + j = *zig2p++; + t2 = in[j] * quant[j]; + j = *zig2p++; + t7 = in[j] * quant[j]; + j = *zig2p++; + t1 = in[j] * quant[j]; + j = *zig2p++; + t4 = in[j] * quant[j]; + j = *zig2p++; + t3 = in[j] * quant[j]; + j = *zig2p++; + t6 = in[j] * quant[j]; + IDCT; + tmpp[0 * 8] = t0; + tmpp[1 * 8] = t1; + tmpp[2 * 8] = t2; + tmpp[3 * 8] = t3; + tmpp[4 * 8] = t4; + tmpp[5 * 8] = t5; + tmpp[6 * 8] = t6; + tmpp[7 * 8] = t7; + tmpp++; + t0 = 0; + } + for (i = 0; i < 8; i++) { + t0 = tmp[8 * i + 0]; + t1 = tmp[8 * i + 1]; + t2 = tmp[8 * i + 2]; + t3 = tmp[8 * i + 3]; + t4 = tmp[8 * i + 4]; + t5 = tmp[8 * i + 5]; + t6 = tmp[8 * i + 6]; + t7 = tmp[8 * i + 7]; + IDCT; + out[8 * i + 0] = ITOINT(t0); + out[8 * i + 1] = ITOINT(t1); + out[8 * i + 2] = ITOINT(t2); + out[8 * i + 3] = ITOINT(t3); + out[8 * i + 4] = ITOINT(t4); + out[8 * i + 5] = ITOINT(t5); + out[8 * i + 6] = ITOINT(t6); + out[8 * i + 7] = ITOINT(t7); + } +} + +static unsigned char zig[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +static PREC aaidct[8] = { + IFIX(0.3535533906), IFIX(0.4903926402), + IFIX(0.4619397663), IFIX(0.4157348062), + IFIX(0.3535533906), IFIX(0.2777851165), + IFIX(0.1913417162), IFIX(0.0975451610) +}; + + +static void idctqtab(unsigned char *qin, PREC * qout) +{ + int i, j; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] * + IMULT(aaidct[i], aaidct[j]); +} + +static void scaleidctqtab(PREC * q, PREC sc) +{ + int i; + + for (i = 0; i < 64; i++) + q[i] = IMULT(q[i], sc); +} + +/****************************************************************/ +/************** color decoder ***************/ +/****************************************************************/ + +#define ROUND + +/* + * YCbCr Color transformation: + * + * y:0..255 Cb:-128..127 Cr:-128..127 + * + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + * => + * Cr *= 1.40200; + * Cb *= 1.77200; + * Cg = 0.19421 * Cb + .50937 * Cr; + * R = Y + Cr; + * G = Y - Cg; + * B = Y + Cb; + * + * => + * Cg = (50 * Cb + 130 * Cr + 128) >> 8; + */ + +static void initcol(PREC q[][64]) +{ + scaleidctqtab(q[1], IFIX(1.77200)); + scaleidctqtab(q[2], IFIX(1.40200)); +} + +/* This is optimized for the stupid sun SUNWspro compiler. */ +#define STORECLAMP(a,x) \ +( \ + (a) = (x), \ + (unsigned int)(x) >= 256 ? \ + ((a) = (x) < 0 ? 0 : 255) \ + : \ + 0 \ +) + +#define CLAMP(x) ((unsigned int)(x) >= 256 ? ((x) < 0 ? 0 : 255) : (x)) + +#ifdef ROUND + +#define CBCRCG(yin, xin) \ +( \ + cb = outc[0 +yin*8+xin], \ + cr = outc[64+yin*8+xin], \ + cg = (50 * cb + 130 * cr + 128) >> 8 \ +) + +#else + +#define CBCRCG(yin, xin) \ +( \ + cb = outc[0 +yin*8+xin], \ + cr = outc[64+yin*8+xin], \ + cg = (3 * cb + 8 * cr) >> 4 \ +) + +#endif + +#ifdef __LITTLE_ENDIAN +#define PIC(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 3 + 2], y + cr), \ + STORECLAMP(p[(xout) * 3 + 1], y - cg), \ + STORECLAMP(p[(xout) * 3 + 0], y + cb) \ +) +#else +#define PIC(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 3 + 0], y + cr), \ + STORECLAMP(p[(xout) * 3 + 1], y - cg), \ + STORECLAMP(p[(xout) * 3 + 2], y + cb) \ +) +#endif + +#ifdef __LITTLE_ENDIAN +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ + ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y & 0xff, \ + p[(xout) * 2 + 1] = y >> 8 \ +) +#else +#ifdef CONFIG_PPC +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 7) | \ + ((CLAMP(y - cg + add*2+1) & 0xf8) << 2) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y >> 8, \ + p[(xout) * 2 + 1] = y & 0xff \ +) +#else +#define PIC_16(yin, xin, p, xout, add) \ +( \ + y = outy[(yin) * 8 + xin], \ + y = ((CLAMP(y + cr + add*2+1) & 0xf8) << 8) | \ + ((CLAMP(y - cg + add ) & 0xfc) << 3) | \ + ((CLAMP(y + cb + add*2+1) ) >> 3), \ + p[(xout) * 2 + 0] = y >> 8, \ + p[(xout) * 2 + 1] = y & 0xff \ +) +#endif +#endif + +#define PIC_32(yin, xin, p, xout) \ +( \ + y = outy[(yin) * 8 + xin], \ + STORECLAMP(p[(xout) * 4 + 0], y + cr), \ + STORECLAMP(p[(xout) * 4 + 1], y - cg), \ + STORECLAMP(p[(xout) * 4 + 2], y + cb), \ + p[(xout) * 4 + 3] = 0 \ +) + +#define PIC221111(xin) \ +( \ + CBCRCG(0, xin), \ + PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0), \ + PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1), \ + PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0), \ + PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ +) + +#define PIC221111_16(xin) \ +( \ + CBCRCG(0, xin), \ + PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0, 3), \ + PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1, 0), \ + PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0, 1), \ + PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \ +) + +#define PIC221111_32(xin) \ +( \ + CBCRCG(0, xin), \ + PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0), \ + PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1), \ + PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0), \ + PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1) \ +) + +static void col221111(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} + +static void col221111_16(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111_16(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} + +static void col221111_32(int *out, unsigned char *pic, int width) +{ + int i, j, k; + unsigned char *pic0, *pic1; + int *outy, *outc; + int cr, cg, cb, y; + + pic0 = pic; + pic1 = pic + width; + outy = out; + outc = out + 64 * 4; + for (i = 2; i > 0; i--) { + for (j = 4; j > 0; j--) { + for (k = 0; k < 8; k++) { + PIC221111_32(k); + } + outc += 8; + outy += 16; + pic0 += 2 * width; + pic1 += 2 * width; + } + outy += 64 * 2 - 16 * 4; + } +} diff --git a/qemu/roms/seabios/src/kbd.c b/qemu/roms/seabios/src/kbd.c new file mode 100644 index 000000000..a5a1ad9a5 --- /dev/null +++ b/qemu/roms/seabios/src/kbd.c @@ -0,0 +1,576 @@ +// 16bit code to handle keyboard requests. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "hw/ps2port.h" // ps2_kbd_command +#include "hw/usb-hid.h" // usb_kbd_command +#include "output.h" // debug_enter +#include "stacks.h" // yield +#include "string.h" // memset +#include "util.h" // kbd_init + +// Bit definitions for BDA kbd_flag[012] +#define KF0_RSHIFT (1<<0) +#define KF0_LSHIFT (1<<1) +#define KF0_CTRLACTIVE (1<<2) +#define KF0_ALTACTIVE (1<<3) +#define KF0_SCROLLACTIVE (1<<4) +#define KF0_NUMACTIVE (1<<5) +#define KF0_CAPSACTIVE (1<<6) + +#define KF1_LCTRL (1<<0) +#define KF1_LALT (1<<1) +#define KF1_PAUSEACTIVE (1<<3) +#define KF1_SCROLL (1<<4) +#define KF1_NUM (1<<5) +#define KF1_CAPS (1<<6) + +#define KF2_LAST_E1 (1<<0) +#define KF2_LAST_E0 (1<<1) +#define KF2_RCTRL (1<<2) +#define KF2_RALT (1<<3) +#define KF2_101KBD (1<<4) + +void +kbd_init(void) +{ + dprintf(3, "init keyboard\n"); + u16 x = offsetof(struct bios_data_area_s, kbd_buf); + SET_BDA(kbd_flag2, KF2_101KBD); + SET_BDA(kbd_buf_head, x); + SET_BDA(kbd_buf_tail, x); + SET_BDA(kbd_buf_start_offset, x); + + SET_BDA(kbd_buf_end_offset + , x + FIELD_SIZEOF(struct bios_data_area_s, kbd_buf)); +} + +static u8 +enqueue_key(u8 scan_code, u8 ascii_code) +{ + u16 buffer_start = GET_BDA(kbd_buf_start_offset); + u16 buffer_end = GET_BDA(kbd_buf_end_offset); + + u16 buffer_head = GET_BDA(kbd_buf_head); + u16 buffer_tail = GET_BDA(kbd_buf_tail); + + u16 temp_tail = buffer_tail; + buffer_tail += 2; + if (buffer_tail >= buffer_end) + buffer_tail = buffer_start; + + if (buffer_tail == buffer_head) + return 0; + + SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+0), ascii_code); + SET_FARVAR(SEG_BDA, *(u8*)(temp_tail+1), scan_code); + SET_BDA(kbd_buf_tail, buffer_tail); + return 1; +} + +static void +dequeue_key(struct bregs *regs, int incr, int extended) +{ + yield(); + u16 buffer_head; + u16 buffer_tail; + for (;;) { + buffer_head = GET_BDA(kbd_buf_head); + buffer_tail = GET_BDA(kbd_buf_tail); + + if (buffer_head != buffer_tail) + break; + if (!incr) { + regs->flags |= F_ZF; + return; + } + yield_toirq(); + } + + u8 ascii_code = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+0)); + u8 scan_code = GET_FARVAR(SEG_BDA, *(u8*)(buffer_head+1)); + if ((ascii_code == 0xF0 && scan_code != 0) + || (ascii_code == 0xE0 && !extended)) + ascii_code = 0; + regs->ax = (scan_code << 8) | ascii_code; + + if (!incr) { + regs->flags &= ~F_ZF; + return; + } + u16 buffer_start = GET_BDA(kbd_buf_start_offset); + u16 buffer_end = GET_BDA(kbd_buf_end_offset); + + buffer_head += 2; + if (buffer_head >= buffer_end) + buffer_head = buffer_start; + SET_BDA(kbd_buf_head, buffer_head); +} + +static int +kbd_command(int command, u8 *param) +{ + if (usb_kbd_active()) + return usb_kbd_command(command, param); + return ps2_kbd_command(command, param); +} + +// read keyboard input +static void +handle_1600(struct bregs *regs) +{ + dequeue_key(regs, 1, 0); +} + +// check keyboard status +static void +handle_1601(struct bregs *regs) +{ + dequeue_key(regs, 0, 0); +} + +// get shift flag status +static void +handle_1602(struct bregs *regs) +{ + yield(); + regs->al = GET_BDA(kbd_flag0); +} + +// store key-stroke into buffer +static void +handle_1605(struct bregs *regs) +{ + regs->al = !enqueue_key(regs->ch, regs->cl); +} + +// GET KEYBOARD FUNCTIONALITY +static void +handle_1609(struct bregs *regs) +{ + // bit Bochs Description + // 7 0 reserved + // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support) + // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support) + // 4 1 INT 16/AH=0Ah supported + // 3 0 INT 16/AX=0306h supported + // 2 0 INT 16/AX=0305h supported + // 1 0 INT 16/AX=0304h supported + // 0 0 INT 16/AX=0300h supported + // + regs->al = 0x30; +} + +// GET KEYBOARD ID +static void noinline +handle_160a(struct bregs *regs) +{ + u8 param[2]; + int ret = kbd_command(ATKBD_CMD_GETID, param); + if (ret) { + regs->bx = 0; + return; + } + regs->bx = (param[1] << 8) | param[0]; +} + +// read MF-II keyboard input +static void +handle_1610(struct bregs *regs) +{ + dequeue_key(regs, 1, 1); +} + +// check MF-II keyboard status +static void +handle_1611(struct bregs *regs) +{ + dequeue_key(regs, 0, 1); +} + +// get extended keyboard status +static void +handle_1612(struct bregs *regs) +{ + yield(); + regs->al = GET_BDA(kbd_flag0); + regs->ah = ((GET_BDA(kbd_flag1) & ~(KF2_RCTRL|KF2_RALT)) + | (GET_BDA(kbd_flag2) & (KF2_RCTRL|KF2_RALT))); + //BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); +} + +static void +handle_166f(struct bregs *regs) +{ + if (regs->al == 0x08) + // unsupported, aka normal keyboard + regs->ah = 2; +} + +// keyboard capability check called by DOS 5.0+ keyb +static void +handle_1692(struct bregs *regs) +{ + // function int16 ah=0x10-0x12 supported + regs->ah = 0x80; +} + +// 122 keys capability check called by DOS 5.0+ keyb +static void +handle_16a2(struct bregs *regs) +{ + // don't change AH : function int16 ah=0x20-0x22 NOT supported +} + +static void +handle_16XX(struct bregs *regs) +{ + warn_unimplemented(regs); +} + +static void noinline +set_leds(void) +{ + u8 shift_flags = (GET_BDA(kbd_flag0) >> 4) & 0x07; + u8 kbd_led = GET_BDA(kbd_led); + u8 led_flags = kbd_led & 0x07; + if (shift_flags == led_flags) + return; + + int ret = kbd_command(ATKBD_CMD_SETLEDS, &shift_flags); + if (ret) + // Error + return; + kbd_led = (kbd_led & ~0x07) | shift_flags; + SET_BDA(kbd_led, kbd_led); +} + +// INT 16h Keyboard Service Entry Point +void VISIBLE16 +handle_16(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_16); + if (! CONFIG_KEYBOARD) + return; + + // XXX - set_leds should be called from irq handler + set_leds(); + + switch (regs->ah) { + case 0x00: handle_1600(regs); break; + case 0x01: handle_1601(regs); break; + case 0x02: handle_1602(regs); break; + case 0x05: handle_1605(regs); break; + case 0x09: handle_1609(regs); break; + case 0x0a: handle_160a(regs); break; + case 0x10: handle_1610(regs); break; + case 0x11: handle_1611(regs); break; + case 0x12: handle_1612(regs); break; + case 0x92: handle_1692(regs); break; + case 0xa2: handle_16a2(regs); break; + case 0x6f: handle_166f(regs); break; + default: handle_16XX(regs); break; + } +} + +#define none 0 +#define MNUM KF0_NUMACTIVE +#define MCAP KF0_CAPSACTIVE + +static struct scaninfo { + u16 normal; + u16 shift; + u16 control; + u16 alt; + u8 lock_flags; +} scan_to_scanascii[] VAR16 = { + { none, none, none, none, none }, + { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */ + { 0x0231, 0x0221, none, 0x7800, none }, /* 1! */ + { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */ + { 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */ + { 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */ + { 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */ + { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */ + { 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */ + { 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */ + { 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */ + { 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */ + { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */ + { 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */ + { 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */ + { 0x0f09, 0x0f00, none, none, none }, /* tab */ + { 0x1071, 0x1051, 0x1011, 0x1000, MCAP }, /* Q */ + { 0x1177, 0x1157, 0x1117, 0x1100, MCAP }, /* W */ + { 0x1265, 0x1245, 0x1205, 0x1200, MCAP }, /* E */ + { 0x1372, 0x1352, 0x1312, 0x1300, MCAP }, /* R */ + { 0x1474, 0x1454, 0x1414, 0x1400, MCAP }, /* T */ + { 0x1579, 0x1559, 0x1519, 0x1500, MCAP }, /* Y */ + { 0x1675, 0x1655, 0x1615, 0x1600, MCAP }, /* U */ + { 0x1769, 0x1749, 0x1709, 0x1700, MCAP }, /* I */ + { 0x186f, 0x184f, 0x180f, 0x1800, MCAP }, /* O */ + { 0x1970, 0x1950, 0x1910, 0x1900, MCAP }, /* P */ + { 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */ + { 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */ + { 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */ + { none, none, none, none, none }, /* L Ctrl */ + { 0x1e61, 0x1e41, 0x1e01, 0x1e00, MCAP }, /* A */ + { 0x1f73, 0x1f53, 0x1f13, 0x1f00, MCAP }, /* S */ + { 0x2064, 0x2044, 0x2004, 0x2000, MCAP }, /* D */ + { 0x2166, 0x2146, 0x2106, 0x2100, MCAP }, /* F */ + { 0x2267, 0x2247, 0x2207, 0x2200, MCAP }, /* G */ + { 0x2368, 0x2348, 0x2308, 0x2300, MCAP }, /* H */ + { 0x246a, 0x244a, 0x240a, 0x2400, MCAP }, /* J */ + { 0x256b, 0x254b, 0x250b, 0x2500, MCAP }, /* K */ + { 0x266c, 0x264c, 0x260c, 0x2600, MCAP }, /* L */ + { 0x273b, 0x273a, none, none, none }, /* ;: */ + { 0x2827, 0x2822, none, none, none }, /* '" */ + { 0x2960, 0x297e, none, none, none }, /* `~ */ + { none, none, none, none, none }, /* L shift */ + { 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */ + { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, MCAP }, /* Z */ + { 0x2d78, 0x2d58, 0x2d18, 0x2d00, MCAP }, /* X */ + { 0x2e63, 0x2e43, 0x2e03, 0x2e00, MCAP }, /* C */ + { 0x2f76, 0x2f56, 0x2f16, 0x2f00, MCAP }, /* V */ + { 0x3062, 0x3042, 0x3002, 0x3000, MCAP }, /* B */ + { 0x316e, 0x314e, 0x310e, 0x3100, MCAP }, /* N */ + { 0x326d, 0x324d, 0x320d, 0x3200, MCAP }, /* M */ + { 0x332c, 0x333c, none, none, none }, /* ,< */ + { 0x342e, 0x343e, none, none, none }, /* .> */ + { 0x352f, 0x353f, none, none, none }, /* /? */ + { none, none, none, none, none }, /* R Shift */ + { 0x372a, 0x372a, none, none, none }, /* * */ + { none, none, none, none, none }, /* L Alt */ + { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */ + { none, none, none, none, none }, /* caps lock */ + { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */ + { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */ + { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */ + { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */ + { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */ + { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */ + { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */ + { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */ + { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */ + { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */ + { none, none, none, none, none }, /* Num Lock */ + { none, none, none, none, none }, /* Scroll Lock */ + { 0x4700, 0x4737, 0x7700, none, MNUM }, /* 7 Home */ + { 0x4800, 0x4838, none, none, MNUM }, /* 8 UP */ + { 0x4900, 0x4939, 0x8400, none, MNUM }, /* 9 PgUp */ + { 0x4a2d, 0x4a2d, none, none, none }, /* - */ + { 0x4b00, 0x4b34, 0x7300, none, MNUM }, /* 4 Left */ + { 0x4c00, 0x4c35, none, none, MNUM }, /* 5 */ + { 0x4d00, 0x4d36, 0x7400, none, MNUM }, /* 6 Right */ + { 0x4e2b, 0x4e2b, none, none, none }, /* + */ + { 0x4f00, 0x4f31, 0x7500, none, MNUM }, /* 1 End */ + { 0x5000, 0x5032, none, none, MNUM }, /* 2 Down */ + { 0x5100, 0x5133, 0x7600, none, MNUM }, /* 3 PgDn */ + { 0x5200, 0x5230, none, none, MNUM }, /* 0 Ins */ + { 0x5300, 0x532e, none, none, MNUM }, /* Del */ + { none, none, none, none, none }, + { none, none, none, none, none }, + { 0x565c, 0x567c, none, none, none }, /* \| */ + { 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */ + { 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */ +}; + +// Handle a ps2 style scancode read from the keyboard. +static void +__process_key(u8 scancode) +{ + u8 flags0 = GET_BDA(kbd_flag0); + u8 flags1 = GET_BDA(kbd_flag1); + u8 flags2 = GET_BDA(kbd_flag2); + + if (flags2 & KF2_LAST_E1) { + // Part of "pause" key (sequence is e1 1d 45 e1 9d c5) + if ((scancode & ~0x80) == 0x1d) + // Second key of sequence + return; + // Third key of sequence - clear flag. + flags2 &= ~KF2_LAST_E1; + SET_BDA(kbd_flag2, flags2); + + if (scancode == 0xc5) { + // Final key in sequence. + + // XXX - do actual pause. + } + return; + } + + // XXX - PrtScr should cause int 0x05 + // XXX - Ctrl+Break should cause int 0x1B + // XXX - SysReq should cause int 0x15/0x85 + + switch (scancode) { + case 0x00: + dprintf(1, "KBD: int09 handler: AL=0\n"); + return; + + case 0x3a: /* Caps Lock press */ + flags0 ^= KF0_CAPSACTIVE; + flags1 |= KF1_CAPS; + break; + case 0xba: /* Caps Lock release */ + flags1 &= ~KF1_CAPS; + break; + + case 0x2a: /* L Shift press */ + flags0 |= KF0_LSHIFT; + break; + case 0xaa: /* L Shift release */ + flags0 &= ~KF0_LSHIFT; + break; + + case 0x36: /* R Shift press */ + flags0 |= KF0_RSHIFT; + break; + case 0xb6: /* R Shift release */ + flags0 &= ~KF0_RSHIFT; + break; + + case 0x1d: /* Ctrl press */ + flags0 |= KF0_CTRLACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 |= KF2_RCTRL; + else + flags1 |= KF1_LCTRL; + break; + case 0x9d: /* Ctrl release */ + flags0 &= ~KF0_CTRLACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 &= ~KF2_RCTRL; + else + flags1 &= ~KF1_LCTRL; + break; + + case 0x38: /* Alt press */ + flags0 |= KF0_ALTACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 |= KF2_RALT; + else + flags1 |= KF1_LALT; + break; + case 0xb8: /* Alt release */ + flags0 &= ~KF0_ALTACTIVE; + if (flags2 & KF2_LAST_E0) + flags2 &= ~KF2_RALT; + else + flags1 &= ~KF1_LALT; + break; + + case 0x45: /* Num Lock press */ + flags1 |= KF1_NUM; + flags0 ^= KF0_NUMACTIVE; + break; + case 0xc5: /* Num Lock release */ + flags1 &= ~KF1_NUM; + break; + + case 0x46: /* Scroll Lock press */ + flags1 |= KF1_SCROLL; + flags0 ^= KF0_SCROLLACTIVE; + break; + case 0xc6: /* Scroll Lock release */ + flags1 &= ~KF1_SCROLL; + break; + + case 0xe0: + // Extended key + flags2 |= KF2_LAST_E0; + SET_BDA(kbd_flag2, flags2); + return; + case 0xe1: + // Start of pause key sequence + flags2 |= KF2_LAST_E1; + break; + + default: + if (scancode & 0x80) + // toss key releases + break; + if (scancode == 0x53 + && ((flags0 & (KF0_CTRLACTIVE|KF0_ALTACTIVE)) + == (KF0_CTRLACTIVE|KF0_ALTACTIVE))) { + // Ctrl+alt+del - reset machine. + SET_BDA(soft_reset_flag, 0x1234); + reset(); + } + if (scancode >= ARRAY_SIZE(scan_to_scanascii)) { + dprintf(1, "KBD: int09h_handler(): unknown scancode read: 0x%02x!\n" + , scancode); + return; + } + u8 asciicode; + struct scaninfo *info = &scan_to_scanascii[scancode]; + if (flags0 & KF0_ALTACTIVE) { + asciicode = GET_GLOBAL(info->alt); + scancode = GET_GLOBAL(info->alt) >> 8; + } else if (flags0 & KF0_CTRLACTIVE) { + asciicode = GET_GLOBAL(info->control); + scancode = GET_GLOBAL(info->control) >> 8; + } else if (flags2 & KF2_LAST_E0 + && scancode >= 0x47 && scancode <= 0x53) { + /* extended keys handling */ + asciicode = 0xe0; + scancode = GET_GLOBAL(info->normal) >> 8; + } else if (flags0 & (KF0_RSHIFT|KF0_LSHIFT)) { + /* check if lock state should be ignored because a SHIFT + * key is pressed */ + + if (flags0 & GET_GLOBAL(info->lock_flags)) { + asciicode = GET_GLOBAL(info->normal); + scancode = GET_GLOBAL(info->normal) >> 8; + } else { + asciicode = GET_GLOBAL(info->shift); + scancode = GET_GLOBAL(info->shift) >> 8; + } + } else { + /* check if lock is on */ + if (flags0 & GET_GLOBAL(info->lock_flags)) { + asciicode = GET_GLOBAL(info->shift); + scancode = GET_GLOBAL(info->shift) >> 8; + } else { + asciicode = GET_GLOBAL(info->normal); + scancode = GET_GLOBAL(info->normal) >> 8; + } + } + if (scancode==0 && asciicode==0) + dprintf(1, "KBD: scancode & asciicode are zero?\n"); + enqueue_key(scancode, asciicode); + break; + } + flags2 &= ~KF2_LAST_E0; + + SET_BDA(kbd_flag0, flags0); + SET_BDA(kbd_flag1, flags1); + SET_BDA(kbd_flag2, flags2); +} + +void +process_key(u8 key) +{ + if (!CONFIG_KEYBOARD) + return; + + if (CONFIG_KBD_CALL_INT15_4F) { + // allow for keyboard intercept + struct bregs br; + memset(&br, 0, sizeof(br)); + br.eax = (0x4f << 8) | key; + br.flags = F_IF|F_CF; + call16_int(0x15, &br); + if (!(br.flags & F_CF)) + return; + key = br.eax; + } + __process_key(key); +} diff --git a/qemu/roms/seabios/src/list.h b/qemu/roms/seabios/src/list.h new file mode 100644 index 000000000..de656b9d6 --- /dev/null +++ b/qemu/roms/seabios/src/list.h @@ -0,0 +1,81 @@ +#ifndef __LIST_H +#define __LIST_H + +#include "types.h" // container_of + + +/**************************************************************** + * hlist - Double linked lists with a single pointer list head + ****************************************************************/ + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +struct hlist_head { + struct hlist_node *first; +}; + +static inline int +hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void +hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void +hlist_add(struct hlist_node *n, struct hlist_node **pprev) +{ + struct hlist_node *next = *pprev; + n->pprev = pprev; + n->next = next; + if (next) + next->pprev = &n->next; + *pprev = n; +} + +static inline void +hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + hlist_add(n, &h->first); +} + +static inline void +hlist_add_before(struct hlist_node *n, struct hlist_node *next) +{ + hlist_add(n, next->pprev); +} + +static inline void +hlist_add_after(struct hlist_node *n, struct hlist_node *prev) +{ + hlist_add(n, &prev->next); +} + +#define hlist_for_each_entry(pos, head, member) \ + for (pos = container_of((head)->first, typeof(*pos), member) \ + ; pos != container_of(NULL, typeof(*pos), member) \ + ; pos = container_of(pos->member.next, typeof(*pos), member)) + +#define hlist_for_each_entry_safe(pos, n, head, member) \ + for (pos = container_of((head)->first, typeof(*pos), member) \ + ; pos != container_of(NULL, typeof(*pos), member) \ + && ({ n = pos->member.next; 1; }) \ + ; pos = container_of(n, typeof(*pos), member)) + +#define hlist_for_each_entry_pprev(pos, pprev, head, member) \ + for (pprev = &(head)->first \ + ; *pprev && ({ pos=container_of(*pprev, typeof(*pos), member); 1; }) \ + ; pprev = &(*pprev)->next) + + +#endif // list.h diff --git a/qemu/roms/seabios/src/malloc.c b/qemu/roms/seabios/src/malloc.c new file mode 100644 index 000000000..c4cb17149 --- /dev/null +++ b/qemu/roms/seabios/src/malloc.c @@ -0,0 +1,541 @@ +// Internal dynamic memory allocations. +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "config.h" // BUILD_BIOS_ADDR +#include "list.h" // hlist_node +#include "malloc.h" // _malloc +#include "memmap.h" // struct e820entry +#include "output.h" // dprintf +#include "stacks.h" // wait_preempt +#include "std/optionrom.h" // OPTION_ROM_ALIGN +#include "string.h" // memset + +// Information on a reserved area. +struct allocinfo_s { + struct hlist_node node; + void *data, *dataend, *allocend; +}; + +// Information on a tracked memory allocation. +struct allocdetail_s { + struct allocinfo_s detailinfo; + struct allocinfo_s datainfo; + u32 handle; +}; + +// The various memory zones. +struct zone_s { + struct hlist_head head; +}; + +struct zone_s ZoneLow VARVERIFY32INIT, ZoneHigh VARVERIFY32INIT; +struct zone_s ZoneFSeg VARVERIFY32INIT; +struct zone_s ZoneTmpLow VARVERIFY32INIT, ZoneTmpHigh VARVERIFY32INIT; + +static struct zone_s *Zones[] VARVERIFY32INIT = { + &ZoneTmpLow, &ZoneLow, &ZoneFSeg, &ZoneTmpHigh, &ZoneHigh +}; + + +/**************************************************************** + * low-level memory reservations + ****************************************************************/ + +// Find and reserve space from a given zone +static void * +allocSpace(struct zone_s *zone, u32 size, u32 align, struct allocinfo_s *fill) +{ + struct allocinfo_s *info; + hlist_for_each_entry(info, &zone->head, node) { + void *dataend = info->dataend; + void *allocend = info->allocend; + void *newallocend = (void*)ALIGN_DOWN((u32)allocend - size, align); + if (newallocend >= dataend && newallocend <= allocend) { + // Found space - now reserve it. + if (!fill) + fill = newallocend; + fill->data = newallocend; + fill->dataend = newallocend + size; + fill->allocend = allocend; + + info->allocend = newallocend; + hlist_add_before(&fill->node, &info->node); + return newallocend; + } + } + return NULL; +} + +// Release space allocated with allocSpace() +static void +freeSpace(struct allocinfo_s *info) +{ + struct allocinfo_s *next = container_of_or_null( + info->node.next, struct allocinfo_s, node); + if (next && next->allocend == info->data) + next->allocend = info->allocend; + hlist_del(&info->node); +} + +// Add new memory to a zone +static void +addSpace(struct zone_s *zone, void *start, void *end) +{ + // Find position to add space + struct allocinfo_s *info; + struct hlist_node **pprev; + hlist_for_each_entry_pprev(info, pprev, &zone->head, node) { + if (info->data < start) + break; + } + + // Add space using temporary allocation info. + struct allocdetail_s tempdetail; + tempdetail.datainfo.data = tempdetail.datainfo.dataend = start; + tempdetail.datainfo.allocend = end; + hlist_add(&tempdetail.datainfo.node, pprev); + + // Allocate final allocation info. + struct allocdetail_s *detail = allocSpace( + &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); + if (!detail) { + detail = allocSpace(&ZoneTmpLow, sizeof(*detail) + , MALLOC_MIN_ALIGN, NULL); + if (!detail) { + hlist_del(&tempdetail.datainfo.node); + warn_noalloc(); + return; + } + } + + // Replace temp alloc space with final alloc space + pprev = tempdetail.datainfo.node.pprev; + hlist_del(&tempdetail.datainfo.node); + memcpy(&detail->datainfo, &tempdetail.datainfo, sizeof(detail->datainfo)); + detail->handle = MALLOC_DEFAULT_HANDLE; + hlist_add(&detail->datainfo.node, pprev); +} + +// Search all zones for an allocation obtained from allocSpace() +static struct allocinfo_s * +findAlloc(void *data) +{ + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct allocinfo_s *info; + hlist_for_each_entry(info, &Zones[i]->head, node) { + if (info->data == data) + return info; + } + } + return NULL; +} + +// Return the last sentinal node of a zone +static struct allocinfo_s * +findLast(struct zone_s *zone) +{ + struct allocinfo_s *info, *last = NULL; + hlist_for_each_entry(info, &zone->head, node) { + last = info; + } + return last; +} + + +/**************************************************************** + * ebda movement + ****************************************************************/ + +// Move ebda +static int +relocate_ebda(u32 newebda, u32 oldebda, u8 ebda_size) +{ + u32 lowram = GET_BDA(mem_size_kb) * 1024; + if (oldebda != lowram) + // EBDA isn't at end of ram - give up. + return -1; + + // Do copy + memmove((void*)newebda, (void*)oldebda, ebda_size * 1024); + + // Update indexes + dprintf(1, "ebda moved from %x to %x\n", oldebda, newebda); + SET_BDA(mem_size_kb, newebda / 1024); + SET_BDA(ebda_seg, FLATPTR_TO_SEG(newebda)); + return 0; +} + +// Support expanding the ZoneLow dynamically. +static void * +zonelow_expand(u32 size, u32 align, struct allocinfo_s *fill) +{ + // Make sure to not move ebda while an optionrom is running. + if (unlikely(wait_preempt())) { + void *data = allocSpace(&ZoneLow, size, align, fill); + if (data) + return data; + } + + struct allocinfo_s *info = findLast(&ZoneLow); + if (!info) + return NULL; + u32 oldpos = (u32)info->allocend; + u32 newpos = ALIGN_DOWN(oldpos - size, align); + u32 bottom = (u32)info->dataend; + if (newpos >= bottom && newpos <= oldpos) + // Space already present. + return allocSpace(&ZoneLow, size, align, fill); + u16 ebda_seg = get_ebda_seg(); + u32 ebda_pos = (u32)MAKE_FLATPTR(ebda_seg, 0); + u8 ebda_size = GET_EBDA(ebda_seg, size); + u32 ebda_end = ebda_pos + ebda_size * 1024; + if (ebda_end != bottom) + // Something else is after ebda - can't use any existing space. + newpos = ALIGN_DOWN(ebda_end - size, align); + u32 newbottom = ALIGN_DOWN(newpos, 1024); + u32 newebda = ALIGN_DOWN(newbottom - ebda_size * 1024, 1024); + if (newebda < BUILD_EBDA_MINIMUM) + // Not enough space. + return NULL; + + // Move ebda + int ret = relocate_ebda(newebda, ebda_pos, ebda_size); + if (ret) + return NULL; + + // Update zone + if (ebda_end == bottom) { + info->data = (void*)newbottom; + info->dataend = (void*)newbottom; + } else + addSpace(&ZoneLow, (void*)newbottom, (void*)ebda_end); + + return allocSpace(&ZoneLow, size, align, fill); +} + + +/**************************************************************** + * tracked memory allocations + ****************************************************************/ + +// Allocate memory from the given zone and track it as a PMM allocation +void * __malloc +_malloc(struct zone_s *zone, u32 size, u32 align) +{ + ASSERT32FLAT(); + if (!size) + return NULL; + + // Find and reserve space for bookkeeping. + struct allocdetail_s *detail = allocSpace( + &ZoneTmpHigh, sizeof(*detail), MALLOC_MIN_ALIGN, NULL); + if (!detail) { + detail = allocSpace(&ZoneTmpLow, sizeof(*detail) + , MALLOC_MIN_ALIGN, NULL); + if (!detail) + return NULL; + } + detail->handle = MALLOC_DEFAULT_HANDLE; + + // Find and reserve space for main allocation + void *data = allocSpace(zone, size, align, &detail->datainfo); + if (!CONFIG_MALLOC_UPPERMEMORY && !data && zone == &ZoneLow) + data = zonelow_expand(size, align, &detail->datainfo); + if (!data) { + freeSpace(&detail->detailinfo); + return NULL; + } + + dprintf(8, "_malloc zone=%p size=%d align=%x ret=%p (detail=%p)\n" + , zone, size, align, data, detail); + + return data; +} + +// Free a data block allocated with _malloc +int +_free(void *data) +{ + ASSERT32FLAT(); + struct allocinfo_s *info = findAlloc(data); + if (!info || data == (void*)info || data == info->dataend) + return -1; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, datainfo); + dprintf(8, "_free %p (detail=%p)\n", data, detail); + freeSpace(info); + freeSpace(&detail->detailinfo); + return 0; +} + +// Find the amount of free space in a given zone. +u32 +malloc_getspace(struct zone_s *zone) +{ + // XXX - doesn't account for ZoneLow being able to grow. + // XXX - results not reliable when CONFIG_THREAD_OPTIONROMS + u32 maxspace = 0; + struct allocinfo_s *info; + hlist_for_each_entry(info, &zone->head, node) { + u32 space = info->allocend - info->dataend; + if (space > maxspace) + maxspace = space; + } + + if (zone != &ZoneTmpHigh && zone != &ZoneTmpLow) + return maxspace; + // Account for space needed for PMM tracking. + u32 reserve = ALIGN(sizeof(struct allocdetail_s), MALLOC_MIN_ALIGN); + if (maxspace <= reserve) + return 0; + return maxspace - reserve; +} + +// Set a handle associated with an allocation. +void +malloc_sethandle(void *data, u32 handle) +{ + ASSERT32FLAT(); + struct allocinfo_s *info = findAlloc(data); + if (!info || data == (void*)info || data == info->dataend) + return; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, datainfo); + detail->handle = handle; +} + +// Find the data block allocated with _malloc with a given handle. +void * +malloc_findhandle(u32 handle) +{ + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct allocinfo_s *info; + hlist_for_each_entry(info, &Zones[i]->head, node) { + if (info->data != (void*)info) + continue; + struct allocdetail_s *detail = container_of( + info, struct allocdetail_s, detailinfo); + if (detail->handle == handle) + return detail->datainfo.data; + } + } + return NULL; +} + + +/**************************************************************** + * 0xc0000-0xf0000 management + ****************************************************************/ + +static u32 RomEnd = BUILD_ROM_START; +static struct allocinfo_s *RomBase; + +#define OPROM_HEADER_RESERVE 16 + +// Return the maximum memory position option roms may use. +u32 +rom_get_max(void) +{ + if (CONFIG_MALLOC_UPPERMEMORY) + return ALIGN_DOWN((u32)RomBase->allocend - OPROM_HEADER_RESERVE + , OPTION_ROM_ALIGN); + extern u8 final_readonly_start[]; + return (u32)final_readonly_start; +} + +// Return the end of the last deployed option rom. +u32 +rom_get_last(void) +{ + return RomEnd; +} + +// Request space for an optionrom in 0xc0000-0xf0000 area. +struct rom_header * +rom_reserve(u32 size) +{ + u32 newend = ALIGN(RomEnd + size, OPTION_ROM_ALIGN); + if (newend > rom_get_max()) + return NULL; + if (CONFIG_MALLOC_UPPERMEMORY) { + if (newend < (u32)zonelow_base) + newend = (u32)zonelow_base; + RomBase->data = RomBase->dataend = (void*)newend + OPROM_HEADER_RESERVE; + } + return (void*)RomEnd; +} + +// Confirm space as in use by an optionrom. +int +rom_confirm(u32 size) +{ + void *new = rom_reserve(size); + if (!new) { + warn_noalloc(); + return -1; + } + RomEnd = ALIGN(RomEnd + size, OPTION_ROM_ALIGN); + return 0; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +void +malloc_preinit(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc preinit\n"); + + // Don't declare any memory between 0xa0000 and 0x100000 + add_e820(BUILD_LOWRAM_END, BUILD_BIOS_ADDR-BUILD_LOWRAM_END, E820_HOLE); + + // Mark known areas as reserved. + add_e820(BUILD_BIOS_ADDR, BUILD_BIOS_SIZE, E820_RESERVED); + + // Populate temp high ram + u32 highram = 0; + int i; + for (i=e820_count-1; i>=0; i--) { + struct e820entry *en = &e820_list[i]; + u64 end = en->start + en->size; + if (end < 1024*1024) + break; + if (en->type != E820_RAM || end > 0xffffffff) + continue; + u32 s = en->start, e = end; + if (!highram) { + u32 newe = ALIGN_DOWN(e - BUILD_MAX_HIGHTABLE, MALLOC_MIN_ALIGN); + if (newe <= e && newe >= s) { + highram = newe; + e = newe; + } + } + addSpace(&ZoneTmpHigh, (void*)s, (void*)e); + } + + // Populate regions + addSpace(&ZoneTmpLow, (void*)BUILD_STACK_ADDR, (void*)BUILD_EBDA_MINIMUM); + if (highram) { + addSpace(&ZoneHigh, (void*)highram + , (void*)highram + BUILD_MAX_HIGHTABLE); + add_e820(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED); + } +} + +void +csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, u32 hi_pmm_size) +{ + ASSERT32FLAT(); + + if (hi_pmm_size > BUILD_MAX_HIGHTABLE) { + void *hi_pmm_end = (void *)hi_pmm + hi_pmm_size; + addSpace(&ZoneTmpHigh, (void *)hi_pmm, hi_pmm_end - BUILD_MAX_HIGHTABLE); + addSpace(&ZoneHigh, hi_pmm_end - BUILD_MAX_HIGHTABLE, hi_pmm_end); + } else { + addSpace(&ZoneTmpHigh, (void *)hi_pmm, (void *)hi_pmm + hi_pmm_size); + } + addSpace(&ZoneTmpLow, (void *)low_pmm, (void *)low_pmm + low_pmm_size); +} + +u32 LegacyRamSize VARFSEG; + +// Calculate the maximum ramsize (less than 4gig) from e820 map. +static void +calcRamSize(void) +{ + u32 rs = 0; + int i; + for (i=e820_count-1; i>=0; i--) { + struct e820entry *en = &e820_list[i]; + u64 end = en->start + en->size; + u32 type = en->type; + if (end <= 0xffffffff && (type == E820_ACPI || type == E820_RAM)) { + rs = end; + break; + } + } + LegacyRamSize = rs >= 1024*1024 ? rs : 1024*1024; +} + +// Update pointers after code relocation. +void +malloc_init(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc init\n"); + + if (CONFIG_RELOCATE_INIT) { + // Fixup malloc pointers after relocation + int i; + for (i=0; i<ARRAY_SIZE(Zones); i++) { + struct zone_s *zone = Zones[i]; + if (zone->head.first) + zone->head.first->pprev = &zone->head.first; + } + } + + // Initialize low-memory region + extern u8 varlow_start[], varlow_end[], final_varlow_start[]; + memmove(final_varlow_start, varlow_start, varlow_end - varlow_start); + if (CONFIG_MALLOC_UPPERMEMORY) { + addSpace(&ZoneLow, zonelow_base + OPROM_HEADER_RESERVE + , final_varlow_start); + RomBase = findLast(&ZoneLow); + } else { + addSpace(&ZoneLow, (void*)ALIGN_DOWN((u32)final_varlow_start, 1024) + , final_varlow_start); + } + + // Add space available in f-segment to ZoneFSeg + extern u8 zonefseg_start[], zonefseg_end[]; + memset(zonefseg_start, 0, zonefseg_end - zonefseg_start); + addSpace(&ZoneFSeg, zonefseg_start, zonefseg_end); + + calcRamSize(); +} + +void +malloc_prepboot(void) +{ + ASSERT32FLAT(); + dprintf(3, "malloc finalize\n"); + + u32 base = rom_get_max(); + memset((void*)RomEnd, 0, base-RomEnd); + if (CONFIG_MALLOC_UPPERMEMORY) { + // Place an optionrom signature around used low mem area. + struct rom_header *dummyrom = (void*)base; + dummyrom->signature = OPTION_ROM_SIGNATURE; + int size = (BUILD_BIOS_ADDR - base) / 512; + dummyrom->size = (size > 255) ? 255 : size; + } + + // Reserve more low-mem if needed. + u32 endlow = GET_BDA(mem_size_kb)*1024; + add_e820(endlow, BUILD_LOWRAM_END-endlow, E820_RESERVED); + + // Clear unused f-seg ram. + struct allocinfo_s *info = findLast(&ZoneFSeg); + memset(info->dataend, 0, info->allocend - info->dataend); + dprintf(1, "Space available for UMB: %x-%x, %x-%x\n" + , RomEnd, base, (u32)info->dataend, (u32)info->allocend); + + // Give back unused high ram. + info = findLast(&ZoneHigh); + if (info) { + u32 giveback = ALIGN_DOWN(info->allocend - info->dataend, PAGE_SIZE); + add_e820((u32)info->dataend, giveback, E820_RAM); + dprintf(1, "Returned %d bytes of ZoneHigh\n", giveback); + } + + calcRamSize(); +} diff --git a/qemu/roms/seabios/src/malloc.h b/qemu/roms/seabios/src/malloc.h new file mode 100644 index 000000000..2bcb5bf6d --- /dev/null +++ b/qemu/roms/seabios/src/malloc.h @@ -0,0 +1,71 @@ +#ifndef __MALLOC_H +#define __MALLOC_H + +#include "types.h" // u32 + +// malloc.c +extern struct zone_s ZoneLow, ZoneHigh, ZoneFSeg, ZoneTmpLow, ZoneTmpHigh; +u32 rom_get_max(void); +u32 rom_get_last(void); +struct rom_header *rom_reserve(u32 size); +int rom_confirm(u32 size); +void csm_malloc_preinit(u32 low_pmm, u32 low_pmm_size, u32 hi_pmm, + u32 hi_pmm_size); +void malloc_preinit(void); +extern u32 LegacyRamSize; +void malloc_init(void); +void malloc_prepboot(void); +void *_malloc(struct zone_s *zone, u32 size, u32 align); +int _free(void *data); +u32 malloc_getspace(struct zone_s *zone); +void malloc_sethandle(void *data, u32 handle); +void *malloc_findhandle(u32 handle); + +#define MALLOC_DEFAULT_HANDLE 0xFFFFFFFF +// Minimum alignment of malloc'd memory +#define MALLOC_MIN_ALIGN 16 +// Helper functions for memory allocation. +static inline void *malloc_low(u32 size) { + return _malloc(&ZoneLow, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_high(u32 size) { + return _malloc(&ZoneHigh, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_fseg(u32 size) { + return _malloc(&ZoneFSeg, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmplow(u32 size) { + return _malloc(&ZoneTmpLow, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmphigh(u32 size) { + return _malloc(&ZoneTmpHigh, size, MALLOC_MIN_ALIGN); +} +static inline void *malloc_tmp(u32 size) { + void *ret = malloc_tmphigh(size); + if (ret) + return ret; + return malloc_tmplow(size); +} +static inline void *memalign_low(u32 align, u32 size) { + return _malloc(&ZoneLow, size, align); +} +static inline void *memalign_high(u32 align, u32 size) { + return _malloc(&ZoneHigh, size, align); +} +static inline void *memalign_tmplow(u32 align, u32 size) { + return _malloc(&ZoneTmpLow, size, align); +} +static inline void *memalign_tmphigh(u32 align, u32 size) { + return _malloc(&ZoneTmpHigh, size, align); +} +static inline void *memalign_tmp(u32 align, u32 size) { + void *ret = memalign_tmphigh(align, size); + if (ret) + return ret; + return memalign_tmplow(align, size); +} +static inline void free(void *data) { + _free(data); +} + +#endif // malloc.h diff --git a/qemu/roms/seabios/src/memmap.c b/qemu/roms/seabios/src/memmap.c new file mode 100644 index 000000000..e03f8d0bf --- /dev/null +++ b/qemu/roms/seabios/src/memmap.c @@ -0,0 +1,144 @@ +// Support for building memory maps suitable for int 15 e820 calls. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // BUILD_MAX_E820 +#include "memmap.h" // struct e820entry +#include "output.h" // dprintf +#include "string.h" // memmove + + +/**************************************************************** + * e820 memory map + ****************************************************************/ + +// Info on e820 map location and size. +struct e820entry e820_list[BUILD_MAX_E820] VARFSEG; +int e820_count VARFSEG; + +// Remove an entry from the e820_list. +static void +remove_e820(int i) +{ + e820_count--; + memmove(&e820_list[i], &e820_list[i+1] + , sizeof(e820_list[0]) * (e820_count - i)); +} + +// Insert an entry in the e820_list at the given position. +static void +insert_e820(int i, u64 start, u64 size, u32 type) +{ + if (e820_count >= BUILD_MAX_E820) { + warn_noalloc(); + return; + } + + memmove(&e820_list[i+1], &e820_list[i] + , sizeof(e820_list[0]) * (e820_count - i)); + e820_count++; + struct e820entry *e = &e820_list[i]; + e->start = start; + e->size = size; + e->type = type; +} + +static const char * +e820_type_name(u32 type) +{ + switch (type) { + case E820_RAM: return "RAM"; + case E820_RESERVED: return "RESERVED"; + case E820_ACPI: return "ACPI"; + case E820_NVS: return "NVS"; + case E820_UNUSABLE: return "UNUSABLE"; + case E820_HOLE: return "HOLE"; + default: return "UNKNOWN"; + } +} + +// Show the current e820_list. +static void +dump_map(void) +{ + dprintf(1, "e820 map has %d items:\n", e820_count); + int i; + for (i=0; i<e820_count; i++) { + struct e820entry *e = &e820_list[i]; + u64 e_end = e->start + e->size; + dprintf(1, " %d: %016llx - %016llx = %d %s\n", i + , e->start, e_end, e->type, e820_type_name(e->type)); + } +} + +// Add a new entry to the list. This scans for overlaps and keeps the +// list sorted. +void +add_e820(u64 start, u64 size, u32 type) +{ + dprintf(8, "Add to e820 map: %08x %08x %d\n", (u32)start, (u32)size, type); + + if (! size) + // Huh? Nothing to do. + return; + + // Find position of new item (splitting existing item if needed). + u64 end = start + size; + int i; + for (i=0; i<e820_count; i++) { + struct e820entry *e = &e820_list[i]; + u64 e_end = e->start + e->size; + if (start > e_end) + continue; + // Found position - check if an existing item needs to be split. + if (start > e->start) { + if (type == e->type) { + // Same type - merge them. + size += start - e->start; + start = e->start; + } else { + // Split existing item. + e->size = start - e->start; + i++; + if (e_end > end) + insert_e820(i, end, e_end - end, e->type); + } + } + break; + } + // Remove/adjust existing items that are overlapping. + while (i<e820_count) { + struct e820entry *e = &e820_list[i]; + if (end < e->start) + // No overlap - done. + break; + u64 e_end = e->start + e->size; + if (end >= e_end) { + // Existing item completely overlapped - remove it. + remove_e820(i); + continue; + } + // Not completely overlapped - adjust its start. + e->start = end; + e->size = e_end - end; + if (type == e->type) { + // Same type - merge them. + size += e->size; + remove_e820(i); + } + break; + } + // Insert new item. + if (type != E820_HOLE) + insert_e820(i, start, size, type); + //dump_map(); +} + +// Report on final memory locations. +void +memmap_prepboot(void) +{ + dump_map(); +} diff --git a/qemu/roms/seabios/src/memmap.h b/qemu/roms/seabios/src/memmap.h new file mode 100644 index 000000000..7bda56e2b --- /dev/null +++ b/qemu/roms/seabios/src/memmap.h @@ -0,0 +1,29 @@ +#ifndef __E820MAP_H +#define __E820MAP_H + +#include "types.h" // u64 + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 +#define E820_HOLE ((u32)-1) // Useful for removing entries + +struct e820entry { + u64 start; + u64 size; + u32 type; +}; + +void add_e820(u64 start, u64 size, u32 type); +void memmap_prepboot(void); + +// A typical OS page size +#define PAGE_SIZE 4096 + +// e820 map storage +extern struct e820entry e820_list[]; +extern int e820_count; + +#endif // e820map.h diff --git a/qemu/roms/seabios/src/misc.c b/qemu/roms/seabios/src/misc.c new file mode 100644 index 000000000..8caaf31d8 --- /dev/null +++ b/qemu/roms/seabios/src/misc.c @@ -0,0 +1,203 @@ +// Code for misc 16bit handlers and variables. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_BDA +#include "bregs.h" // struct bregs +#include "hw/pic.h" // enable_hwirq +#include "output.h" // debug_enter +#include "stacks.h" // call16_int +#include "string.h" // memset + +#define PORT_MATH_CLEAR 0x00f0 + +// Indicator if POST phase has been started (and if it has completed). +int HaveRunPost VARFSEG; + +int +in_post(void) +{ + return GET_GLOBAL(HaveRunPost) == 1; +} + + +/**************************************************************** + * Misc 16bit ISRs + ****************************************************************/ + +// INT 12h Memory Size Service Entry Point +void VISIBLE16 +handle_12(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_12); + regs->ax = GET_BDA(mem_size_kb); +} + +// INT 11h Equipment List Service Entry Point +void VISIBLE16 +handle_11(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_11); + regs->ax = GET_BDA(equipment_list_flags); +} + +// INT 05h Print Screen Service Entry Point +void VISIBLE16 +handle_05(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_05); +} + +// INT 10h Video Support Service Entry Point +void VISIBLE16 +handle_10(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_10); + // dont do anything, since the VGA BIOS handles int10h requests +} + +// NMI handler +void VISIBLE16 +handle_02(void) +{ + debug_isr(DEBUG_ISR_02); +} + +void +mathcp_setup(void) +{ + dprintf(3, "math cp init\n"); + // 80x87 coprocessor installed + set_equipment_flags(0x02, 0x02); + enable_hwirq(13, FUNC16(entry_75)); +} + +// INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION +void VISIBLE16 +handle_75(void) +{ + debug_isr(DEBUG_ISR_75); + + // clear irq13 + outb(0, PORT_MATH_CLEAR); + // clear interrupt + pic_eoi2(); + // legacy nmi call + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x02, &br); +} + + +/**************************************************************** + * BIOS_CONFIG_TABLE + ****************************************************************/ + +// DMA channel 3 used by hard disk BIOS +#define CBT_F1_DMA3USED (1<<7) +// 2nd interrupt controller (8259) installed +#define CBT_F1_2NDPIC (1<<6) +// Real-Time Clock installed +#define CBT_F1_RTC (1<<5) +// INT 15/AH=4Fh called upon INT 09h +#define CBT_F1_INT154F (1<<4) +// wait for external event (INT 15/AH=41h) supported +#define CBT_F1_WAITEXT (1<<3) +// extended BIOS area allocated (usually at top of RAM) +#define CBT_F1_EBDA (1<<2) +// bus is Micro Channel instead of ISA +#define CBT_F1_MCA (1<<1) +// system has dual bus (Micro Channel + ISA) +#define CBT_F1_MCAISA (1<<0) + +// INT 16/AH=09h (keyboard functionality) supported +#define CBT_F2_INT1609 (1<<6) + +struct bios_config_table_s BIOS_CONFIG_TABLE VARFSEGFIXED(0xe6f5) = { + .size = sizeof(BIOS_CONFIG_TABLE) - 2, + .model = BUILD_MODEL_ID, + .submodel = BUILD_SUBMODEL_ID, + .biosrev = BUILD_BIOS_REVISION, + .feature1 = ( + CBT_F1_2NDPIC | CBT_F1_RTC | CBT_F1_EBDA + | (CONFIG_KBD_CALL_INT15_4F ? CBT_F1_INT154F : 0)), + .feature2 = CBT_F2_INT1609, + .feature3 = 0, + .feature4 = 0, + .feature5 = 0, +}; + + +/**************************************************************** + * GDT and IDT tables + ****************************************************************/ + +// Real mode IDT descriptor +struct descloc_s rmode_IDT_info VARFSEG = { + .length = sizeof(struct rmode_IVT) - 1, + .addr = (u32)MAKE_FLATPTR(SEG_IVT, 0), +}; + +// Dummy IDT that forces a machine shutdown if an irq happens in +// protected mode. +u8 dummy_IDT VARFSEG; + +// Protected mode IDT descriptor +struct descloc_s pmode_IDT_info VARFSEG = { + .length = sizeof(dummy_IDT) - 1, + .addr = (u32)&dummy_IDT, +}; + +// GDT +u64 rombios32_gdt[] VARFSEG __aligned(8) = { + // First entry can't be used. + 0x0000000000000000LL, + // 32 bit flat code segment (SEG32_MODE32_CS) + GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_B, + // 32 bit flat data segment (SEG32_MODE32_DS) + GDT_GRANLIMIT(0xffffffff) | GDT_DATA | GDT_B, + // 16 bit code segment base=0xf0000 limit=0xffff (SEG32_MODE16_CS) + GDT_LIMIT(BUILD_BIOS_SIZE-1) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR), + // 16 bit data segment base=0x0 limit=0xffff (SEG32_MODE16_DS) + GDT_LIMIT(0x0ffff) | GDT_DATA, + // 16 bit code segment base=0xf0000 limit=0xffffffff (SEG32_MODE16BIG_CS) + GDT_GRANLIMIT(0xffffffff) | GDT_CODE | GDT_BASE(BUILD_BIOS_ADDR), + // 16 bit data segment base=0 limit=0xffffffff (SEG32_MODE16BIG_DS) + GDT_GRANLIMIT(0xffffffff) | GDT_DATA, +}; + +// GDT descriptor +struct descloc_s rombios32_gdt_48 VARFSEG = { + .length = sizeof(rombios32_gdt) - 1, + .addr = (u32)rombios32_gdt, +}; + + +/**************************************************************** + * Misc fixed vars + ****************************************************************/ + +// BIOS build date +char BiosDate[] VARFSEGFIXED(0xfff5) = "06/23/99"; + +u8 BiosModelId VARFSEGFIXED(0xfffe) = BUILD_MODEL_ID; + +u8 BiosChecksum VARFSEGFIXED(0xffff); + +struct floppy_dbt_s diskette_param_table VARFSEGFIXED(0xefc7); + +// Old Fixed Disk Parameter Table (newer tables are in the ebda). +struct fdpt_s OldFDPT VARFSEGFIXED(0xe401); + +// XXX - Baud Rate Generator Table +u8 BaudTable[16] VARFSEGFIXED(0xe729); + +// XXX - Initial Interrupt Vector Offsets Loaded by POST +u8 InitVectors[13] VARFSEGFIXED(0xfef3); + +// XXX - INT 1D - SYSTEM DATA - VIDEO PARAMETER TABLES +u8 VideoParams[88] VARFSEGFIXED(0xf0a4); diff --git a/qemu/roms/seabios/src/mouse.c b/qemu/roms/seabios/src/mouse.c new file mode 100644 index 000000000..6d1f5b77e --- /dev/null +++ b/qemu/roms/seabios/src/mouse.c @@ -0,0 +1,343 @@ +// 16bit code to handle mouse events. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_EBDA +#include "bregs.h" // struct bregs +#include "hw/ps2port.h" // ps2_mouse_command +#include "hw/usb-hid.h" // usb_mouse_command +#include "output.h" // dprintf +#include "stacks.h" // stack_hop_back +#include "util.h" // mouse_init + +void +mouse_init(void) +{ + if (! CONFIG_MOUSE) + return; + dprintf(3, "init mouse\n"); + // pointing device installed + set_equipment_flags(0x04, 0x04); +} + +static int +mouse_command(int command, u8 *param) +{ + if (usb_mouse_active()) + return usb_mouse_command(command, param); + return ps2_mouse_command(command, param); +} + +#define RET_SUCCESS 0x00 +#define RET_EINVFUNCTION 0x01 +#define RET_EINVINPUT 0x02 +#define RET_EINTERFACE 0x03 +#define RET_ENEEDRESEND 0x04 +#define RET_ENOHANDLER 0x05 + +// Disable Mouse +static void +mouse_15c20000(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_DISABLE, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Enable Mouse +static void +mouse_15c20001(struct bregs *regs) +{ + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + if ((mouse_flags_2 & 0x80) == 0) { + set_code_invalid(regs, RET_ENOHANDLER); + return; + } + + int ret = mouse_command(PSMOUSE_CMD_ENABLE, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +static void +mouse_15c200XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +// Disable/Enable Mouse +static void +mouse_15c200(struct bregs *regs) +{ + switch (regs->bh) { + case 0x00: mouse_15c20000(regs); break; + case 0x01: mouse_15c20001(regs); break; + default: mouse_15c200XX(regs); break; + } +} + +// Reset Mouse +static void +mouse_15c201(struct bregs *regs) +{ + u8 param[2]; + int ret = mouse_command(PSMOUSE_CMD_RESET_BAT, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bl = param[0]; + regs->bh = param[1]; + set_code_success(regs); +} + +// Set Sample Rate +static void +mouse_15c202(struct bregs *regs) +{ + static u8 VAR16 sample_rates[7] = {10, 20, 40, 60, 80, 100, 200}; + if (regs->bh >= ARRAY_SIZE(sample_rates)) { + set_code_invalid(regs, RET_EINVINPUT); + return; + } + u8 mouse_data1 = GET_GLOBAL(sample_rates[regs->bh]); + int ret = mouse_command(PSMOUSE_CMD_SETRATE, &mouse_data1); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Set Resolution +static void +mouse_15c203(struct bregs *regs) +{ + // BH: + // 0 = 25 dpi, 1 count per millimeter + // 1 = 50 dpi, 2 counts per millimeter + // 2 = 100 dpi, 4 counts per millimeter + // 3 = 200 dpi, 8 counts per millimeter + if (regs->bh >= 4) { + set_code_invalid(regs, RET_EINVINPUT); + return; + } + u8 param = regs->bh; + int ret = mouse_command(PSMOUSE_CMD_SETRES, ¶m); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Get Device ID +static void +mouse_15c204(struct bregs *regs) +{ + u8 param[2]; + int ret = mouse_command(PSMOUSE_CMD_GETID, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bh = param[0]; + set_code_success(regs); +} + +// Initialize Mouse +static void +mouse_15c205(struct bregs *regs) +{ + if (regs->bh != 3) { + set_code_invalid(regs, RET_EINTERFACE); + return; + } + u16 ebda_seg = get_ebda_seg(); + SET_EBDA(ebda_seg, mouse_flag1, 0x00); + SET_EBDA(ebda_seg, mouse_flag2, regs->bh); + + // Reset Mouse + mouse_15c201(regs); +} + +// Return Status +static void +mouse_15c20600(struct bregs *regs) +{ + u8 param[3]; + int ret = mouse_command(PSMOUSE_CMD_GETINFO, param); + if (ret) { + set_code_invalid(regs, RET_ENEEDRESEND); + return; + } + regs->bl = param[0]; + regs->cl = param[1]; + regs->dl = param[2]; + set_code_success(regs); +} + +// Set Scaling Factor to 1:1 +static void +mouse_15c20601(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_SETSCALE11, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +// Set Scaling Factor to 2:1 +static void +mouse_15c20602(struct bregs *regs) +{ + int ret = mouse_command(PSMOUSE_CMD_SETSCALE21, NULL); + if (ret) + set_code_invalid(regs, RET_ENEEDRESEND); + else + set_code_success(regs); +} + +static void +mouse_15c206XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +// Return Status & Set Scaling Factor... +static void +mouse_15c206(struct bregs *regs) +{ + switch (regs->bh) { + case 0x00: mouse_15c20600(regs); break; + case 0x01: mouse_15c20601(regs); break; + case 0x02: mouse_15c20602(regs); break; + default: mouse_15c206XX(regs); break; + } +} + +// Set Mouse Handler Address +static void +mouse_15c207(struct bregs *regs) +{ + struct segoff_s farptr = SEGOFF(regs->es, regs->bx); + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + if (! farptr.segoff) { + /* remove handler */ + if ((mouse_flags_2 & 0x80) != 0) { + mouse_flags_2 &= ~0x80; + mouse_command(PSMOUSE_CMD_DISABLE, NULL); + } + } else { + /* install handler */ + mouse_flags_2 |= 0x80; + } + SET_EBDA(ebda_seg, mouse_flag2, mouse_flags_2); + SET_EBDA(ebda_seg, far_call_pointer, farptr); + set_code_success(regs); +} + +static void +mouse_15c2XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EINVFUNCTION); +} + +void +handle_15c2(struct bregs *regs) +{ + //debug_stub(regs); + + if (! CONFIG_MOUSE) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + switch (regs->al) { + case 0x00: mouse_15c200(regs); break; + case 0x01: mouse_15c201(regs); break; + case 0x02: mouse_15c202(regs); break; + case 0x03: mouse_15c203(regs); break; + case 0x04: mouse_15c204(regs); break; + case 0x05: mouse_15c205(regs); break; + case 0x06: mouse_15c206(regs); break; + case 0x07: mouse_15c207(regs); break; + default: mouse_15c2XX(regs); break; + } +} + +void VISIBLE16 +invoke_mouse_handler(void) +{ + if (!CONFIG_MOUSE) + return; + if (need_hop_back()) { + extern void _cfunc16_invoke_mouse_handler(void); + stack_hop_back(0, 0, _cfunc16_invoke_mouse_handler); + return; + } + ASSERT16(); + u16 ebda_seg = get_ebda_seg(); + u16 status = GET_EBDA(ebda_seg, mouse_data[0]); + u16 X = GET_EBDA(ebda_seg, mouse_data[1]); + u16 Y = GET_EBDA(ebda_seg, mouse_data[2]); + + struct segoff_s func = GET_EBDA(ebda_seg, far_call_pointer); + dprintf(16, "mouse farcall s=%04x x=%04x y=%04x func=%04x:%04x\n" + , status, X, Y, func.seg, func.offset); + + asm volatile( + "pushl %%ebp\n" + "sti\n" + + "pushl %0\n" + "pushw %w1\n" // status + "pushw %w2\n" // X + "pushw %w3\n" // Y + "pushw $0\n" // Z + "lcallw *8(%%esp)\n" + "addl $12, %%esp\n" + + "cli\n" + "cld\n" + "popl %%ebp" + : "+a"(func.segoff), "+c"(status), "+d"(X), "+b"(Y) + : + : "edi", "esi", "cc", "memory"); +} + +void +process_mouse(u8 data) +{ + if (!CONFIG_MOUSE) + return; + + u16 ebda_seg = get_ebda_seg(); + u8 mouse_flags_1 = GET_EBDA(ebda_seg, mouse_flag1); + u8 mouse_flags_2 = GET_EBDA(ebda_seg, mouse_flag2); + + if (! (mouse_flags_2 & 0x80)) + // far call handler not installed + return; + + u8 package_count = mouse_flags_2 & 0x07; + u8 index = mouse_flags_1 & 0x07; + SET_EBDA(ebda_seg, mouse_data[index], data); + + if ((index+1) < package_count) { + mouse_flags_1++; + SET_EBDA(ebda_seg, mouse_flag1, mouse_flags_1); + return; + } + + SET_EBDA(ebda_seg, mouse_flag1, 0); + invoke_mouse_handler(); +} diff --git a/qemu/roms/seabios/src/optionroms.c b/qemu/roms/seabios/src/optionroms.c new file mode 100644 index 000000000..93d9d2fe6 --- /dev/null +++ b/qemu/roms/seabios/src/optionroms.c @@ -0,0 +1,475 @@ +// Option rom scanning code. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEG +#include "hw/pci.h" // foreachpci +#include "hw/pci_ids.h" // PCI_CLASS_DISPLAY_VGA +#include "hw/pci_regs.h" // PCI_ROM_ADDRESS +#include "malloc.h" // rom_confirm +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // farcall16big +#include "std/optionrom.h" // struct rom_header +#include "std/pnpbios.h" // PNP_SIGNATURE +#include "string.h" // memset +#include "util.h" // get_pnp_offset + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Execute a given option rom. +static void +__callrom(struct rom_header *rom, u16 offset, u16 bdf) +{ + u16 seg = FLATPTR_TO_SEG(rom); + dprintf(1, "Running option rom at %04x:%04x\n", seg, offset); + + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.ax = bdf; + br.bx = 0xffff; + br.dx = 0xffff; + br.es = SEG_BIOS; + br.di = get_pnp_offset(); + br.code = SEGOFF(seg, offset); + start_preempt(); + farcall16big(&br); + finish_preempt(); +} + +// Execute a given option rom at the standard entry vector. +void +callrom(struct rom_header *rom, u16 bdf) +{ + __callrom(rom, OPTION_ROM_INITVECTOR, bdf); +} + +// Execute a BCV option rom registered via add_bcv(). +void +call_bcv(u16 seg, u16 ip) +{ + __callrom(MAKE_FLATPTR(seg, 0), ip, 0); +} + +static int EnforceChecksum; + +// Verify that an option rom looks valid +static int +is_valid_rom(struct rom_header *rom) +{ + dprintf(6, "Checking rom %p (sig %x size %d)\n" + , rom, rom->signature, rom->size); + if (rom->signature != OPTION_ROM_SIGNATURE) + return 0; + if (! rom->size) + return 0; + u32 len = rom->size * 512; + u8 sum = checksum(rom, len); + if (sum != 0) { + dprintf(1, "Found option rom with bad checksum: loc=%p len=%d sum=%x\n" + , rom, len, sum); + if (EnforceChecksum) + return 0; + } + return 1; +} + +// Check if a valid option rom has a pnp struct; return it if so. +static struct pnp_data * +get_pnp_rom(struct rom_header *rom) +{ + struct pnp_data *pnp = (void*)((u8*)rom + rom->pnpoffset); + if (pnp->signature != PNP_SIGNATURE) + return NULL; + return pnp; +} + +// Check for multiple pnp option rom headers. +static struct pnp_data * +get_pnp_next(struct rom_header *rom, struct pnp_data *pnp) +{ + if (! pnp->nextoffset) + return NULL; + pnp = (void*)((u8*)rom + pnp->nextoffset); + if (pnp->signature != PNP_SIGNATURE) + return NULL; + return pnp; +} + +// Check if a valid option rom has a pci struct; return it if so. +static struct pci_data * +get_pci_rom(struct rom_header *rom) +{ + struct pci_data *pd = (void*)((u32)rom + rom->pcioffset); + if (pd->signature != PCI_ROM_SIGNATURE) + return NULL; + if (rom->pcioffset & 3) + dprintf(1, "WARNING! Found unaligned PCI rom (vd=%04x:%04x)\n" + , pd->vendor, pd->device); + return pd; +} + +// Run rom init code and note rom size. +static int +init_optionrom(struct rom_header *rom, u16 bdf, int isvga) +{ + if (! is_valid_rom(rom)) + return -1; + struct rom_header *newrom = rom_reserve(rom->size * 512); + if (!newrom) { + warn_noalloc(); + return -1; + } + if (newrom != rom) + memmove(newrom, rom, rom->size * 512); + + if (isvga || get_pnp_rom(newrom)) + // Only init vga and PnP roms here. + callrom(newrom, bdf); + + return rom_confirm(newrom->size * 512); +} + +#define RS_PCIROM (1LL<<33) + +static void +setRomSource(u64 *sources, struct rom_header *rom, u64 source) +{ + if (sources) + sources[((u32)rom - BUILD_ROM_START) / OPTION_ROM_ALIGN] = source; +} + +static int +getRomPriority(u64 *sources, struct rom_header *rom, int instance) +{ + u64 source = sources[((u32)rom - BUILD_ROM_START) / OPTION_ROM_ALIGN]; + if (!source) + return -1; + if (source & RS_PCIROM) + return bootprio_find_pci_rom((void*)(u32)source, instance); + struct romfile_s *file = (void*)(u32)source; + return bootprio_find_named_rom(file->name, instance); +} + + +/**************************************************************** + * Roms in CBFS + ****************************************************************/ + +static struct rom_header * +deploy_romfile(struct romfile_s *file) +{ + u32 size = file->size; + struct rom_header *rom = rom_reserve(size); + if (!rom) { + warn_noalloc(); + return NULL; + } + int ret = file->copy(file, rom, size); + if (ret <= 0) + return NULL; + return rom; +} + +// Check if an option rom is at a hardcoded location or in CBFS. +static struct rom_header * +lookup_hardcode(struct pci_device *pci) +{ + char fname[17]; + snprintf(fname, sizeof(fname), "pci%04x,%04x.rom" + , pci->vendor, pci->device); + struct romfile_s *file = romfile_find(fname); + if (file) + return deploy_romfile(file); + return NULL; +} + +// Run all roms in a given CBFS directory. +static void +run_file_roms(const char *prefix, int isvga, u64 *sources) +{ + struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix(prefix, file); + if (!file) + break; + struct rom_header *rom = deploy_romfile(file); + if (rom) { + setRomSource(sources, rom, (u32)file); + init_optionrom(rom, 0, isvga); + } + } +} + + +/**************************************************************** + * PCI roms + ****************************************************************/ + +// Verify device is a vga device with legacy address decoding enabled. +int +is_pci_vga(struct pci_device *pci) +{ + if (pci->class != PCI_CLASS_DISPLAY_VGA) + return 0; + u16 cmd = pci_config_readw(pci->bdf, PCI_COMMAND); + if (!(cmd & PCI_COMMAND_IO && cmd & PCI_COMMAND_MEMORY)) + return 0; + while (pci->parent) { + pci = pci->parent; + u32 ctrl = pci_config_readb(pci->bdf, PCI_BRIDGE_CONTROL); + if (!(ctrl & PCI_BRIDGE_CTL_VGA)) + return 0; + } + return 1; +} + +// Copy a rom to its permanent location below 1MiB +static struct rom_header * +copy_rom(struct rom_header *rom) +{ + u32 romsize = rom->size * 512; + struct rom_header *newrom = rom_reserve(romsize); + if (!newrom) { + warn_noalloc(); + return NULL; + } + dprintf(4, "Copying option rom (size %d) from %p to %p\n" + , romsize, rom, newrom); + iomemcpy(newrom, rom, romsize); + return newrom; +} + +// Map the option rom of a given PCI device. +static struct rom_header * +map_pcirom(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(6, "Attempting to map option rom on dev %02x:%02x.%x\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + + if ((pci->header_type & 0x7f) != PCI_HEADER_TYPE_NORMAL) { + dprintf(6, "Skipping non-normal pci device (type=%x)\n" + , pci->header_type); + return NULL; + } + + u32 orig = pci_config_readl(bdf, PCI_ROM_ADDRESS); + pci_config_writel(bdf, PCI_ROM_ADDRESS, ~PCI_ROM_ADDRESS_ENABLE); + u32 sz = pci_config_readl(bdf, PCI_ROM_ADDRESS); + + dprintf(6, "Option rom sizing returned %x %x\n", orig, sz); + orig &= ~PCI_ROM_ADDRESS_ENABLE; + if (!sz || sz == 0xffffffff) + goto fail; + + if (orig == sz || (u32)(orig + 4*1024*1024) < 20*1024*1024) { + // Don't try to map to a pci addresses at its max, in the last + // 4MiB of ram, or the first 16MiB of ram. + dprintf(6, "Preset rom address doesn't look valid\n"); + goto fail; + } + + // Looks like a rom - enable it. + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig | PCI_ROM_ADDRESS_ENABLE); + + struct rom_header *rom = (void*)orig; + for (;;) { + dprintf(5, "Inspecting possible rom at %p (vd=%04x:%04x" + " bdf=%02x:%02x.%x)\n" + , rom, pci->vendor, pci->device + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + if (rom->signature != OPTION_ROM_SIGNATURE) { + dprintf(6, "No option rom signature (got %x)\n", rom->signature); + goto fail; + } + struct pci_data *pd = get_pci_rom(rom); + if (! pd) { + dprintf(6, "No valid pci signature found\n"); + goto fail; + } + + if (pd->vendor == pci->vendor && pd->device == pci->device + && pd->type == PCIROM_CODETYPE_X86) + // A match + break; + dprintf(6, "Didn't match dev/ven (got %04x:%04x) or type (got %d)\n" + , pd->vendor, pd->device, pd->type); + if (pd->indicator & 0x80) { + dprintf(6, "No more images left\n"); + goto fail; + } + rom = (void*)((u32)rom + pd->ilen * 512); + } + + rom = copy_rom(rom); + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig); + return rom; +fail: + // Not valid - restore original and exit. + pci_config_writel(bdf, PCI_ROM_ADDRESS, orig); + return NULL; +} + +// Attempt to map and initialize the option rom on a given PCI device. +static int +init_pcirom(struct pci_device *pci, int isvga, u64 *sources) +{ + u16 bdf = pci->bdf; + dprintf(4, "Attempting to init PCI bdf %02x:%02x.%x (vd %04x:%04x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf) + , pci->vendor, pci->device); + struct rom_header *rom = lookup_hardcode(pci); + if (! rom) + rom = map_pcirom(pci); + if (! rom) + // No ROM present. + return -1; + setRomSource(sources, rom, RS_PCIROM | (u32)pci); + return init_optionrom(rom, bdf, isvga); +} + + +/**************************************************************** + * Non-VGA option rom init + ****************************************************************/ + +void +optionrom_setup(void) +{ + if (! CONFIG_OPTIONROMS) + return; + + dprintf(1, "Scan for option roms\n"); + u64 sources[(BUILD_BIOS_ADDR - BUILD_ROM_START) / OPTION_ROM_ALIGN]; + memset(sources, 0, sizeof(sources)); + u32 post_vga = rom_get_last(); + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + u32 pos = post_vga; + while (pos < rom_get_max()) { + int ret = init_optionrom((void*)pos, 0, 0); + if (ret) + pos += OPTION_ROM_ALIGN; + else + pos = rom_get_last(); + } + } else { + // Find and deploy PCI roms. + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == PCI_CLASS_DISPLAY_VGA || pci->have_driver) + continue; + init_pcirom(pci, 0, sources); + } + + // Find and deploy CBFS roms not associated with a device. + run_file_roms("genroms/", 0, sources); + } + rom_reserve(0); + + // All option roms found and deployed - now build BEV/BCV vectors. + + u32 pos = post_vga; + while (pos < rom_get_last()) { + struct rom_header *rom = (void*)pos; + if (! is_valid_rom(rom)) { + pos += OPTION_ROM_ALIGN; + continue; + } + pos += ALIGN(rom->size * 512, OPTION_ROM_ALIGN); + struct pnp_data *pnp = get_pnp_rom(rom); + if (! pnp) { + // Legacy rom. + boot_add_bcv(FLATPTR_TO_SEG(rom), OPTION_ROM_INITVECTOR, 0 + , getRomPriority(sources, rom, 0)); + continue; + } + // PnP rom - check for BEV and BCV boot capabilities. + int instance = 0; + while (pnp) { + if (pnp->bev) + boot_add_bev(FLATPTR_TO_SEG(rom), pnp->bev, pnp->productname + , getRomPriority(sources, rom, instance++)); + else if (pnp->bcv) + boot_add_bcv(FLATPTR_TO_SEG(rom), pnp->bcv, pnp->productname + , getRomPriority(sources, rom, instance++)); + else + break; + pnp = get_pnp_next(rom, pnp); + } + } +} + + +/**************************************************************** + * VGA init + ****************************************************************/ + +static int S3ResumeVga; +int ScreenAndDebug; +struct rom_header *VgaROM; + +// Call into vga code to turn on console. +void +vgarom_setup(void) +{ + if (! CONFIG_OPTIONROMS) + return; + + dprintf(1, "Scan for VGA option rom\n"); + + // Load some config settings that impact VGA. + EnforceChecksum = romfile_loadint("etc/optionroms-checksum", 1); + S3ResumeVga = romfile_loadint("etc/s3-resume-vga-init", CONFIG_QEMU); + ScreenAndDebug = romfile_loadint("etc/screen-and-debug", 1); + + if (CONFIG_OPTIONROMS_DEPLOYED) { + // Option roms are already deployed on the system. + init_optionrom((void*)BUILD_ROM_START, 0, 1); + } else { + // Clear option rom memory + memset((void*)BUILD_ROM_START, 0, rom_get_max() - BUILD_ROM_START); + + // Find and deploy PCI VGA rom. + struct pci_device *pci; + foreachpci(pci) { + if (!is_pci_vga(pci)) + continue; + vgahook_setup(pci); + init_pcirom(pci, 1, NULL); + break; + } + + // Find and deploy CBFS vga-style roms not associated with a device. + run_file_roms("vgaroms/", 1, NULL); + } + rom_reserve(0); + + if (rom_get_last() == BUILD_ROM_START) + // No VGA rom found + return; + + VgaROM = (void*)BUILD_ROM_START; + enable_vga_console(); +} + +void +s3_resume_vga(void) +{ + if (!S3ResumeVga) + return; + if (!VgaROM || ! is_valid_rom(VgaROM)) + return; + callrom(VgaROM, 0); +} diff --git a/qemu/roms/seabios/src/output.c b/qemu/roms/seabios/src/output.c new file mode 100644 index 000000000..45397b3f6 --- /dev/null +++ b/qemu/roms/seabios/src/output.c @@ -0,0 +1,564 @@ +// Raw screen writing and debug output code. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include <stdarg.h> // va_list + +#include "farptr.h" // GET_VAR +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "biosvar.h" // GET_GLOBAL +#include "hw/serialio.h" // serial_debug_putc +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "stacks.h" // call16_int +#include "string.h" // memset +#include "util.h" // ScreenAndDebug + +struct putcinfo { + void (*func)(struct putcinfo *info, char c); +}; + + +/**************************************************************** + * Debug output + ****************************************************************/ + +void +debug_banner(void) +{ + dprintf(1, "SeaBIOS (version %s)\n", VERSION); +} + +// Write a character to debug port(s). +static void +debug_putc(struct putcinfo *action, char c) +{ + if (! CONFIG_DEBUG_LEVEL) + return; + qemu_debug_putc(c); + if (!MODESEGMENT) + coreboot_debug_putc(c); + serial_debug_putc(c); +} + +// Flush any pending output to debug port(s). +static void +debug_flush(void) +{ + serial_debug_flush(); +} + +// In segmented mode just need a dummy variable (debug_putc is always +// used anyway), and in 32bit flat mode need a pointer to the 32bit +// instance of debug_putc(). +#if MODE16 +static struct putcinfo debuginfo VAR16; +#elif MODESEGMENT +static struct putcinfo debuginfo VAR32SEG; +#else +static struct putcinfo debuginfo = { debug_putc }; +#endif + + +/**************************************************************** + * Screen writing + ****************************************************************/ + +// Show a character on the screen. +static void +screenc(char c) +{ + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + br.ah = 0x0e; + br.al = c; + br.bl = 0x07; + call16_int(0x10, &br); +} + +// Handle a character from a printf request. +static void +screen_putc(struct putcinfo *action, char c) +{ + if (ScreenAndDebug) + debug_putc(&debuginfo, c); + if (c == '\n') + screenc('\r'); + screenc(c); +} + +static struct putcinfo screeninfo = { screen_putc }; + + +/**************************************************************** + * Xprintf code + ****************************************************************/ + +// Output a character. +static void +putc(struct putcinfo *action, char c) +{ + if (MODESEGMENT) { + // Only debugging output supported in segmented mode. + debug_putc(action, c); + return; + } + + void (*func)(struct putcinfo *info, char c) = GET_GLOBAL(action->func); + func(action, c); +} + +// Ouptut a string. +static void +puts(struct putcinfo *action, const char *s) +{ + if (!MODESEGMENT && !s) + s = "(NULL)"; + for (; *s; s++) + putc(action, *s); +} + +// Output a string that is in the CS segment. +static void +puts_cs(struct putcinfo *action, const char *s) +{ + char *vs = (char*)s; + for (;; vs++) { + char c = GET_GLOBAL(*vs); + if (!c) + break; + putc(action, c); + } +} + +// Output an unsigned integer. +static void +putuint(struct putcinfo *action, u32 val) +{ + char buf[12]; + char *d = &buf[sizeof(buf) - 1]; + *d-- = '\0'; + for (;;) { + *d = (val % 10) + '0'; + val /= 10; + if (!val) + break; + d--; + } + puts(action, d); +} + +// Output a single digit hex character. +static inline void +putsinglehex(struct putcinfo *action, u32 val) +{ + if (val <= 9) + val = '0' + val; + else + val = 'a' + val - 10; + putc(action, val); +} + +// Output an integer in hexadecimal with a specified width. +static void +puthex(struct putcinfo *action, u32 val, int width) +{ + switch (width) { + default: putsinglehex(action, (val >> 28) & 0xf); + case 7: putsinglehex(action, (val >> 24) & 0xf); + case 6: putsinglehex(action, (val >> 20) & 0xf); + case 5: putsinglehex(action, (val >> 16) & 0xf); + case 4: putsinglehex(action, (val >> 12) & 0xf); + case 3: putsinglehex(action, (val >> 8) & 0xf); + case 2: putsinglehex(action, (val >> 4) & 0xf); + case 1: putsinglehex(action, (val >> 0) & 0xf); + } +} + +// Output an integer in hexadecimal with a minimum width. +static void +putprettyhex(struct putcinfo *action, u32 val, int width, char padchar) +{ + u32 tmp = val; + int count = 1; + while (tmp >>= 4) + count++; + width -= count; + while (width-- > 0) + putc(action, padchar); + puthex(action, val, count); +} + +static inline int +isdigit(u8 c) +{ + return ((u8)(c - '0')) < 10; +} + +static void +bvprintf(struct putcinfo *action, const char *fmt, va_list args) +{ + const char *s = fmt; + for (;; s++) { + char c = GET_GLOBAL(*(u8*)s); + if (!c) + break; + if (c != '%') { + putc(action, c); + continue; + } + const char *n = s+1; + int field_width = 0; + char padchar = ' '; + u8 is64 = 0; + for (;;) { + c = GET_GLOBAL(*(u8*)n); + if (!isdigit(c)) + break; + if (!field_width && (c == '0')) + padchar = '0'; + else + field_width = field_width * 10 + c - '0'; + n++; + } + if (c == 'l') { + // Ignore long format indicator + n++; + c = GET_GLOBAL(*(u8*)n); + } + if (c == 'l') { + is64 = 1; + n++; + c = GET_GLOBAL(*(u8*)n); + } + s32 val; + const char *sarg; + switch (c) { + case '%': + putc(action, '%'); + break; + case 'd': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + if (val < 0) { + putc(action, '-'); + val = -val; + } + putuint(action, val); + break; + case 'u': + val = va_arg(args, s32); + if (is64) + va_arg(args, s32); + putuint(action, val); + break; + case 'p': + val = va_arg(args, s32); + putc(action, '0'); + putc(action, 'x'); + puthex(action, val, 8); + break; + case 'x': + val = va_arg(args, s32); + if (is64) { + u32 upper = va_arg(args, s32); + if (upper) { + putprettyhex(action, upper, field_width - 8, padchar); + puthex(action, val, 8); + break; + } + } + putprettyhex(action, val, field_width, padchar); + break; + case 'c': + val = va_arg(args, int); + putc(action, val); + break; + case '.': + // Hack to support "%.s" - meaning string on stack. + if (GET_GLOBAL(*(u8*)(n+1)) != 's') + break; + n++; + sarg = va_arg(args, const char *); + puts(action, sarg); + break; + case 's': + sarg = va_arg(args, const char *); + puts_cs(action, sarg); + break; + default: + putc(action, '%'); + n = s; + } + s = n; + } +} + +void +panic(const char *fmt, ...) +{ + if (CONFIG_DEBUG_LEVEL) { + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_flush(); + } + + // XXX - use PANIC PORT. + irq_disable(); + for (;;) + hlt(); +} + +void +__dprintf(const char *fmt, ...) +{ + if (!MODESEGMENT && CONFIG_THREADS && CONFIG_DEBUG_LEVEL >= DEBUG_thread + && *fmt != '\\' && *fmt != '/') { + struct thread_info *cur = getCurThread(); + if (cur != &MainThread) { + // Show "thread id" for this debug message. + debug_putc(&debuginfo, '|'); + puthex(&debuginfo, (u32)cur, 8); + debug_putc(&debuginfo, '|'); + debug_putc(&debuginfo, ' '); + } + } + + va_list args; + va_start(args, fmt); + bvprintf(&debuginfo, fmt, args); + va_end(args); + debug_flush(); +} + +void +printf(const char *fmt, ...) +{ + ASSERT32FLAT(); + va_list args; + va_start(args, fmt); + bvprintf(&screeninfo, fmt, args); + va_end(args); + if (ScreenAndDebug) + debug_flush(); +} + + +/**************************************************************** + * snprintf + ****************************************************************/ + +struct snprintfinfo { + struct putcinfo info; + char *str, *end; +}; + +static void +putc_str(struct putcinfo *info, char c) +{ + struct snprintfinfo *sinfo = container_of(info, struct snprintfinfo, info); + if (sinfo->str >= sinfo->end) + return; + *sinfo->str = c; + sinfo->str++; +} + +// Build a formatted string. Note, this function returns the actual +// number of bytes used (not including null) even in the overflow +// case. +int +snprintf(char *str, size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return 0; + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); + va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return end - str; +} + +// Build a formatted string - malloc'ing the memory. +char * +znprintf(size_t size, const char *fmt, ...) +{ + ASSERT32FLAT(); + if (!size) + return NULL; + char *str = malloc_tmp(size); + if (!str) { + warn_noalloc(); + return NULL; + } + struct snprintfinfo sinfo = { { putc_str }, str, str + size }; + va_list args; + va_start(args, fmt); + bvprintf(&sinfo.info, fmt, args); + va_end(args); + char *end = sinfo.str; + if (end >= sinfo.end) + end = sinfo.end - 1; + *end = '\0'; + return str; +} + + +/**************************************************************** + * Misc helpers + ****************************************************************/ + +void +hexdump(const void *d, int len) +{ + int count=0; + while (len > 0) { + if (count % 8 == 0) { + putc(&debuginfo, '\n'); + puthex(&debuginfo, count*4, 8); + putc(&debuginfo, ':'); + } else { + putc(&debuginfo, ' '); + } + puthex(&debuginfo, *(u32*)d, 8); + count++; + len-=4; + d+=4; + } + putc(&debuginfo, '\n'); + debug_flush(); +} + +static void +dump_regs(struct bregs *regs) +{ + if (!regs) { + dprintf(1, " NULL\n"); + return; + } + dprintf(1, " a=%08x b=%08x c=%08x d=%08x ds=%04x es=%04x ss=%04x\n" + , regs->eax, regs->ebx, regs->ecx, regs->edx + , regs->ds, regs->es, GET_SEG(SS)); + dprintf(1, " si=%08x di=%08x bp=%08x sp=%08x cs=%04x ip=%04x f=%04x\n" + , regs->esi, regs->edi, regs->ebp, (u32)®s[1] + , regs->code.seg, regs->code.offset, regs->flags); +} + +// Report entry to an Interrupt Service Routine (ISR). +void +__debug_isr(const char *fname) +{ + puts_cs(&debuginfo, fname); + putc(&debuginfo, '\n'); + debug_flush(); +} + +// Function called on handler startup. +void +__debug_enter(struct bregs *regs, const char *fname) +{ + dprintf(1, "enter %s:\n", fname); + dump_regs(regs); +} + +// Send debugging output info. +void +__debug_stub(struct bregs *regs, int lineno, const char *fname) +{ + dprintf(1, "stub %s:%d:\n", fname, lineno); + dump_regs(regs); +} + +// Report on an invalid parameter. +void +__warn_invalid(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_invalid) { + dprintf(1, "invalid %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report on an unimplemented feature. +void +__warn_unimplemented(struct bregs *regs, int lineno, const char *fname) +{ + if (CONFIG_DEBUG_LEVEL >= DEBUG_unimplemented) { + dprintf(1, "unimplemented %s:%d:\n", fname, lineno); + dump_regs(regs); + } +} + +// Report a detected internal inconsistency. +void +__warn_internalerror(int lineno, const char *fname) +{ + dprintf(1, "WARNING - internal error detected at %s:%d!\n" + , fname, lineno); +} + +// Report on an allocation failure. +void +__warn_noalloc(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Unable to allocate resource at %s:%d!\n" + , fname, lineno); +} + +// Report on a timeout exceeded. +void +__warn_timeout(int lineno, const char *fname) +{ + dprintf(1, "WARNING - Timeout at %s:%d!\n", fname, lineno); +} + +// Report a handler reporting an invalid parameter to the caller. +void +__set_invalid(struct bregs *regs, int lineno, const char *fname) +{ + __warn_invalid(regs, lineno, fname); + set_invalid_silent(regs); +} + +// Report a call of an unimplemented function. +void +__set_unimplemented(struct bregs *regs, int lineno, const char *fname) +{ + __warn_unimplemented(regs, lineno, fname); + set_invalid_silent(regs); +} + +// Report a handler reporting an invalid parameter code to the +// caller. Note, the lineno and return code are encoded in the same +// parameter as gcc does a better job of scheduling function calls +// when there are 3 or less parameters. +void +__set_code_invalid(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_invalid(regs, lineno, fname); + set_code_invalid_silent(regs, code); +} + +// Report a call of an unimplemented function. +void +__set_code_unimplemented(struct bregs *regs, u32 linecode, const char *fname) +{ + u8 code = linecode; + u32 lineno = linecode >> 8; + __warn_unimplemented(regs, lineno, fname); + set_code_invalid_silent(regs, code); +} diff --git a/qemu/roms/seabios/src/output.h b/qemu/roms/seabios/src/output.h new file mode 100644 index 000000000..14288cf50 --- /dev/null +++ b/qemu/roms/seabios/src/output.h @@ -0,0 +1,68 @@ +#ifndef __OUTPUT_H +#define __OUTPUT_H + +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "types.h" // u32 + +// output.c +void debug_banner(void); +void panic(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))) __noreturn; +void printf(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +int snprintf(char *str, size_t size, const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); +char * znprintf(size_t size, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +void __dprintf(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +struct bregs; +void __debug_enter(struct bregs *regs, const char *fname); +void __debug_isr(const char *fname); +void __debug_stub(struct bregs *regs, int lineno, const char *fname); +void __warn_invalid(struct bregs *regs, int lineno, const char *fname); +void __warn_unimplemented(struct bregs *regs, int lineno, const char *fname); +void __warn_internalerror(int lineno, const char *fname); +void __warn_noalloc(int lineno, const char *fname); +void __warn_timeout(int lineno, const char *fname); +void __set_invalid(struct bregs *regs, int lineno, const char *fname); +void __set_unimplemented(struct bregs *regs, int lineno, const char *fname); +void __set_code_invalid(struct bregs *regs, u32 linecode, const char *fname); +void __set_code_unimplemented(struct bregs *regs, u32 linecode + , const char *fname); +void hexdump(const void *d, int len); + +#define dprintf(lvl, fmt, args...) do { \ + if (CONFIG_DEBUG_LEVEL && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __dprintf((fmt) , ##args ); \ + } while (0) +#define debug_enter(regs, lvl) do { \ + if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __debug_enter((regs), __func__); \ + } while (0) +#define debug_isr(lvl) do { \ + if ((lvl) && (lvl) <= CONFIG_DEBUG_LEVEL) \ + __debug_isr(__func__); \ + } while (0) +#define debug_stub(regs) \ + __debug_stub((regs), __LINE__, __func__) +#define warn_invalid(regs) \ + __warn_invalid((regs), __LINE__, __func__) +#define warn_unimplemented(regs) \ + __warn_unimplemented((regs), __LINE__, __func__) +#define warn_internalerror() \ + __warn_internalerror(__LINE__, __func__) +#define warn_noalloc() \ + __warn_noalloc(__LINE__, __func__) +#define warn_timeout() \ + __warn_timeout(__LINE__, __func__) +#define set_invalid(regs) \ + __set_invalid((regs), __LINE__, __func__) +#define set_code_invalid(regs, code) \ + __set_code_invalid((regs), (code) | (__LINE__ << 8), __func__) +#define set_unimplemented(regs) \ + __set_unimplemented((regs), __LINE__, __func__) +#define set_code_unimplemented(regs, code) \ + __set_code_unimplemented((regs), (code) | (__LINE__ << 8), __func__) + +#endif // output.h diff --git a/qemu/roms/seabios/src/pcibios.c b/qemu/roms/seabios/src/pcibios.c new file mode 100644 index 000000000..7e5d972d9 --- /dev/null +++ b/qemu/roms/seabios/src/pcibios.c @@ -0,0 +1,240 @@ +// PCI BIOS (int 1a/b1) calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "hw/pci.h" // pci_config_readl +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "output.h" // dprintf +#include "std/pirtable.h" // struct pir_header +#include "string.h" // checksum +#include "util.h" // handle_1ab1 + +// romlayout.S +extern void entry_bios32(void); +extern void entry_pcibios32(void); + +#define RET_FUNC_NOT_SUPPORTED 0x81 +#define RET_BAD_VENDOR_ID 0x83 +#define RET_DEVICE_NOT_FOUND 0x86 +#define RET_BUFFER_TOO_SMALL 0x89 + +// installation check +static void +handle_1ab101(struct bregs *regs) +{ + regs->al = 0x01; // Flags - "Config Mechanism #1" supported. + regs->bx = 0x0210; // PCI version 2.10 + regs->cl = GET_GLOBAL(MaxPCIBus); + regs->edx = 0x20494350; // "PCI " + regs->edi = (u32)entry_pcibios32 + BUILD_BIOS_ADDR; + set_code_success(regs); +} + +// find pci device +static void +handle_1ab102(struct bregs *regs) +{ + u32 id = (regs->cx << 16) | regs->dx; + int count = regs->si; + int bus = -1; + while (bus < GET_GLOBAL(MaxPCIBus)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + u32 v = pci_config_readl(bdf, PCI_VENDOR_ID); + if (v != id) + continue; + if (count--) + continue; + regs->bx = bdf; + set_code_success(regs); + return; + } + } + set_code_invalid(regs, RET_DEVICE_NOT_FOUND); +} + +// find class code +static void +handle_1ab103(struct bregs *regs) +{ + int count = regs->si; + u32 classprog = regs->ecx; + int bus = -1; + while (bus < GET_GLOBAL(MaxPCIBus)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + u32 v = pci_config_readl(bdf, PCI_CLASS_REVISION); + if ((v>>8) != classprog) + continue; + if (count--) + continue; + regs->bx = bdf; + set_code_success(regs); + return; + } + } + set_code_invalid(regs, RET_DEVICE_NOT_FOUND); +} + +// read configuration byte +static void +handle_1ab108(struct bregs *regs) +{ + regs->cl = pci_config_readb(regs->bx, regs->di); + set_code_success(regs); +} + +// read configuration word +static void +handle_1ab109(struct bregs *regs) +{ + regs->cx = pci_config_readw(regs->bx, regs->di); + set_code_success(regs); +} + +// read configuration dword +static void +handle_1ab10a(struct bregs *regs) +{ + regs->ecx = pci_config_readl(regs->bx, regs->di); + set_code_success(regs); +} + +// write configuration byte +static void +handle_1ab10b(struct bregs *regs) +{ + pci_config_writeb(regs->bx, regs->di, regs->cl); + set_code_success(regs); +} + +// write configuration word +static void +handle_1ab10c(struct bregs *regs) +{ + pci_config_writew(regs->bx, regs->di, regs->cx); + set_code_success(regs); +} + +// write configuration dword +static void +handle_1ab10d(struct bregs *regs) +{ + pci_config_writel(regs->bx, regs->di, regs->ecx); + set_code_success(regs); +} + +// get irq routing options +static void +handle_1ab10e(struct bregs *regs) +{ + struct pir_header *pirtable_gf = GET_GLOBAL(PirAddr); + if (! pirtable_gf) { + set_code_invalid(regs, RET_FUNC_NOT_SUPPORTED); + return; + } + struct pir_header *pirtable_g = GLOBALFLAT2GLOBAL(pirtable_gf); + + struct param_s { + u16 size; + u16 buf_off; + u16 buf_seg; + } *param_far = (void*)(regs->di+0); + + // Validate and update size. + u16 bufsize = GET_FARVAR(regs->es, param_far->size); + u16 pirsize = GET_GLOBAL(pirtable_g->size) - sizeof(struct pir_header); + SET_FARVAR(regs->es, param_far->size, pirsize); + if (bufsize < pirsize) { + set_code_invalid(regs, RET_BUFFER_TOO_SMALL); + return; + } + + // Get dest buffer. + void *buf_far = (void*)(GET_FARVAR(regs->es, param_far->buf_off)+0); + u16 buf_seg = GET_FARVAR(regs->es, param_far->buf_seg); + + // Memcpy pir table slots to dest buffer. + memcpy_far(buf_seg, buf_far + , get_global_seg() + , (void*)(pirtable_g->slots) + get_global_offset() + , pirsize); + + // XXX - bochs bios sets bx to (1 << 9) | (1 << 11) + regs->bx = GET_GLOBAL(pirtable_g->exclusive_irqs); + set_code_success(regs); +} + +static void +handle_1ab1XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_FUNC_NOT_SUPPORTED); +} + +void +handle_1ab1(struct bregs *regs) +{ + //debug_stub(regs); + + if (! CONFIG_PCIBIOS) { + set_invalid(regs); + return; + } + + switch (regs->al) { + case 0x01: handle_1ab101(regs); break; + case 0x02: handle_1ab102(regs); break; + case 0x03: handle_1ab103(regs); break; + case 0x08: handle_1ab108(regs); break; + case 0x09: handle_1ab109(regs); break; + case 0x0a: handle_1ab10a(regs); break; + case 0x0b: handle_1ab10b(regs); break; + case 0x0c: handle_1ab10c(regs); break; + case 0x0d: handle_1ab10d(regs); break; + case 0x0e: handle_1ab10e(regs); break; + default: handle_1ab1XX(regs); break; + } +} + +// Entry point for pci bios functions. +void VISIBLE16 VISIBLE32SEG +handle_pcibios(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_pcibios); + handle_1ab1(regs); +} + + +/**************************************************************** + * 32bit interface + ****************************************************************/ + +struct bios32_s { + u32 signature; + u32 entry; + u8 version; + u8 length; + u8 checksum; + u8 reserved[5]; +} PACKED; + +struct bios32_s BIOS32HEADER __aligned(16) VARFSEG = { + .signature = 0x5f32335f, // _32_ + .length = sizeof(BIOS32HEADER) / 16, +}; + +void +bios32_init(void) +{ + dprintf(3, "init bios32\n"); + + BIOS32HEADER.entry = (u32)entry_bios32; + BIOS32HEADER.checksum -= checksum(&BIOS32HEADER, sizeof(BIOS32HEADER)); +} diff --git a/qemu/roms/seabios/src/pmm.c b/qemu/roms/seabios/src/pmm.c new file mode 100644 index 000000000..304faab2c --- /dev/null +++ b/qemu/roms/seabios/src/pmm.c @@ -0,0 +1,163 @@ +// Post memory manager (PMM) calls +// +// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // FUNC16 +#include "config.h" // CONFIG_* +#include "malloc.h" // _malloc +#include "output.h" // dprintf +#include "std/pmm.h" // PMM_SIGNATURE +#include "string.h" // checksum +#include "util.h" // pmm_init +#include "x86.h" // __ffs + +extern struct pmmheader PMMHEADER; + +#if CONFIG_PMM +struct pmmheader PMMHEADER __aligned(16) VARFSEG = { + .signature = PMM_SIGNATURE, + .version = 0x01, + .length = sizeof(PMMHEADER), +}; +#endif + +// PMM - allocate +static u32 +handle_pmm00(u16 *args) +{ + u32 length = *(u32*)&args[1], handle = *(u32*)&args[3]; + u16 flags = args[5]; + dprintf(3, "pmm00: length=%x handle=%x flags=%x\n" + , length, handle, flags); + struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh; + if (flags & 8) { + // Permanent memory request. + lowzone = &ZoneLow; + highzone = &ZoneHigh; + } + if (!length) { + // Memory size request + switch (flags & 3) { + default: + case 0: + return 0; + case 1: + return malloc_getspace(lowzone); + case 2: + return malloc_getspace(highzone); + case 3: { + u32 spacelow = malloc_getspace(lowzone); + u32 spacehigh = malloc_getspace(highzone); + if (spacelow > spacehigh) + return spacelow; + return spacehigh; + } + } + } + u32 size = length * 16; + if ((s32)size <= 0) + return 0; + u32 align = MALLOC_MIN_ALIGN; + if (flags & 4) { + align = 1<<__ffs(size); + if (align < MALLOC_MIN_ALIGN) + align = MALLOC_MIN_ALIGN; + } + void *data; + switch (flags & 3) { + default: + case 0: + return 0; + case 1: + data = _malloc(lowzone, size, align); + break; + case 2: + data = _malloc(highzone, size, align); + break; + case 3: { + data = _malloc(lowzone, size, align); + if (!data) + data = _malloc(highzone, size, align); + } + } + if (data && handle != MALLOC_DEFAULT_HANDLE) + malloc_sethandle(data, handle); + return (u32)data; +} + +// PMM - find +static u32 +handle_pmm01(u16 *args) +{ + u32 handle = *(u32*)&args[1]; + dprintf(3, "pmm01: handle=%x\n", handle); + if (handle == MALLOC_DEFAULT_HANDLE) + return 0; + return (u32)malloc_findhandle(handle); +} + +// PMM - deallocate +static u32 +handle_pmm02(u16 *args) +{ + u32 buffer = *(u32*)&args[1]; + dprintf(3, "pmm02: buffer=%x\n", buffer); + int ret = _free((void*)buffer); + if (ret) + // Error + return 1; + return 0; +} + +static u32 +handle_pmmXX(u16 *args) +{ + return PMM_FUNCTION_NOT_SUPPORTED; +} + +u32 VISIBLE32INIT +handle_pmm(u16 *args) +{ + ASSERT32FLAT(); + if (! CONFIG_PMM) + return PMM_FUNCTION_NOT_SUPPORTED; + + u16 arg1 = args[0]; + dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1); + + u32 ret; + switch (arg1) { + case 0x00: ret = handle_pmm00(args); break; + case 0x01: ret = handle_pmm01(args); break; + case 0x02: ret = handle_pmm02(args); break; + default: ret = handle_pmmXX(args); break; + } + + return ret; +} + +void +pmm_init(void) +{ + if (! CONFIG_PMM) + return; + + dprintf(3, "init PMM\n"); + + PMMHEADER.entry = FUNC16(entry_pmm); + PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER)); +} + +void +pmm_prepboot(void) +{ + if (! CONFIG_PMM) + return; + + dprintf(3, "finalize PMM\n"); + + PMMHEADER.signature = 0; + PMMHEADER.entry.segoff = 0; +} diff --git a/qemu/roms/seabios/src/pnpbios.c b/qemu/roms/seabios/src/pnpbios.c new file mode 100644 index 000000000..95ce21f8f --- /dev/null +++ b/qemu/roms/seabios/src/pnpbios.c @@ -0,0 +1,88 @@ +// PNP BIOS calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // BUILD_BIOS_ADDR +#include "farptr.h" // SET_FARVAR +#include "output.h" // dprintf +#include "std/pnpbios.h" // PNP_SIGNATURE +#include "string.h" // checksum +#include "util.h" // pnp_init + +extern struct pnpheader PNPHEADER; +extern char pnp_string[]; + +#if CONFIG_PNPBIOS +struct pnpheader PNPHEADER __aligned(16) VARFSEG = { + .signature = PNP_SIGNATURE, + .version = 0x10, + .length = sizeof(PNPHEADER), + .real_cs = SEG_BIOS, + .prot_base = BUILD_BIOS_ADDR, + .real_ds = SEG_BIOS, + .prot_database = BUILD_BIOS_ADDR, +}; +#else +// We need a copy of this string in the 0xf000 segment, but we are not +// actually a PnP BIOS, so make sure it is *not* aligned, so OSes will +// not see it if they scan. +char pnp_string[] __aligned(2) VARFSEG = " $PnP"; +#endif + +// BBS - Get Version and Installation Check +static u16 +handle_pnp60(u16 *args) +{ + u16 version_ptr = args[1]; + u16 version_seg = args[2]; + SET_FARVAR(version_seg, *(u16*)(version_ptr+0), 0x0101); + return 0; +} + +static u16 +handle_pnpXX(u16 *args) +{ + return FUNCTION_NOT_SUPPORTED; +} + +u16 VISIBLE16 +handle_pnp(u16 *args) +{ + if (! CONFIG_PNPBIOS) + return FUNCTION_NOT_SUPPORTED; + + u16 arg1 = args[0]; + dprintf(DEBUG_HDL_pnp, "pnp call arg1=%x\n", arg1); + + switch (arg1) { + case 0x60: return handle_pnp60(args); + default: return handle_pnpXX(args); + } +} + +u16 +get_pnp_offset(void) +{ + if (! CONFIG_PNPBIOS) + return (u32)pnp_string + 1 - BUILD_BIOS_ADDR; + return (u32)&PNPHEADER - BUILD_BIOS_ADDR; +} + +// romlayout.S +extern void entry_pnp_real(void); +extern void entry_pnp_prot(void); + +void +pnp_init(void) +{ + if (! CONFIG_PNPBIOS) + return; + + dprintf(3, "init PNPBIOS table\n"); + + PNPHEADER.real_ip = (u32)entry_pnp_real - BUILD_BIOS_ADDR; + PNPHEADER.prot_ip = (u32)entry_pnp_prot - BUILD_BIOS_ADDR; + PNPHEADER.checksum -= checksum(&PNPHEADER, sizeof(PNPHEADER)); +} diff --git a/qemu/roms/seabios/src/post.c b/qemu/roms/seabios/src/post.c new file mode 100644 index 000000000..9ea5620c9 --- /dev/null +++ b/qemu/roms/seabios/src/post.c @@ -0,0 +1,336 @@ +// 32bit code to Power On Self Test (POST) a machine. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // qemu_cfg_preinit +#include "fw/xen.h" // xen_preinit +#include "hw/ahci.h" // ahci_setup +#include "hw/ata.h" // ata_setup +#include "hw/esp-scsi.h" // esp_scsi_setup +#include "hw/lsi-scsi.h" // lsi_scsi_setup +#include "hw/megasas.h" // megasas_setup +#include "hw/pvscsi.h" // pvscsi_setup +#include "hw/pic.h" // pic_setup +#include "hw/ps2port.h" // ps2port_setup +#include "hw/rtc.h" // rtc_write +#include "hw/serialio.h" // serial_debug_preinit +#include "hw/usb.h" // usb_setup +#include "hw/virtio-blk.h" // virtio_blk_setup +#include "hw/virtio-scsi.h" // virtio_scsi_setup +#include "malloc.h" // malloc_init +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "string.h" // memset +#include "util.h" // kbd_init + + +/**************************************************************** + * BIOS initialization and hardware setup + ****************************************************************/ + +static void +ivt_init(void) +{ + dprintf(3, "init ivt\n"); + + // Setup reset-vector entry point (controls legacy reboots). + HaveRunPost = 1; + rtc_write(CMOS_RESET_CODE, 0); + + // Initialize all vectors to the default handler. + int i; + for (i=0; i<256; i++) + SET_IVT(i, FUNC16(entry_iret_official)); + + // Initialize all hw vectors to a default hw handler. + for (i=BIOS_HWIRQ0_VECTOR; i<BIOS_HWIRQ0_VECTOR+8; i++) + SET_IVT(i, FUNC16(entry_hwpic1)); + for (i=BIOS_HWIRQ8_VECTOR; i<BIOS_HWIRQ8_VECTOR+8; i++) + SET_IVT(i, FUNC16(entry_hwpic2)); + + // Initialize software handlers. + SET_IVT(0x02, FUNC16(entry_02)); + SET_IVT(0x10, FUNC16(entry_10)); + SET_IVT(0x11, FUNC16(entry_11)); + SET_IVT(0x12, FUNC16(entry_12)); + SET_IVT(0x13, FUNC16(entry_13_official)); + SET_IVT(0x14, FUNC16(entry_14)); + SET_IVT(0x15, FUNC16(entry_15_official)); + SET_IVT(0x16, FUNC16(entry_16)); + SET_IVT(0x17, FUNC16(entry_17)); + SET_IVT(0x18, FUNC16(entry_18)); + SET_IVT(0x19, FUNC16(entry_19_official)); + SET_IVT(0x1a, FUNC16(entry_1a_official)); + SET_IVT(0x40, FUNC16(entry_40)); + + // INT 60h-66h reserved for user interrupt + for (i=0x60; i<=0x66; i++) + SET_IVT(i, SEGOFF(0, 0)); + + // set vector 0x79 to zero + // this is used by 'gardian angel' protection system + SET_IVT(0x79, SEGOFF(0, 0)); +} + +static void +bda_init(void) +{ + dprintf(3, "init bda\n"); + + struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0); + memset(bda, 0, sizeof(*bda)); + + int esize = EBDA_SIZE_START; + u16 ebda_seg = EBDA_SEGMENT_START; + extern u8 final_varlow_start[]; + if (!CONFIG_MALLOC_UPPERMEMORY) + ebda_seg = FLATPTR_TO_SEG(ALIGN_DOWN((u32)final_varlow_start, 1024) + - EBDA_SIZE_START*1024); + SET_BDA(ebda_seg, ebda_seg); + + SET_BDA(mem_size_kb, ebda_seg / (1024/16)); + + // Init ebda + struct extended_bios_data_area_s *ebda = get_ebda_ptr(); + memset(ebda, 0, sizeof(*ebda)); + ebda->size = esize; + + add_e820((u32)ebda, BUILD_LOWRAM_END-(u32)ebda, E820_RESERVED); + + // Init extra stack + StackPos = (void*)(&ExtraStack[BUILD_EXTRA_STACK_SIZE] - zonelow_base); +} + +void +interface_init(void) +{ + // Running at new code address - do code relocation fixups + malloc_init(); + + // Setup romfile items. + qemu_cfg_init(); + coreboot_cbfs_init(); + + // Setup ivt/bda/ebda + ivt_init(); + bda_init(); + + // Other interfaces + thread_init(); + boot_init(); + bios32_init(); + pmm_init(); + pnp_init(); + kbd_init(); + mouse_init(); +} + +// Initialize hardware devices +void +device_hardware_setup(void) +{ + usb_setup(); + ps2port_setup(); + lpt_setup(); + serial_setup(); + + floppy_setup(); + ata_setup(); + ahci_setup(); + sdcard_setup(); + cbfs_payload_setup(); + ramdisk_setup(); + virtio_blk_setup(); + virtio_scsi_setup(); + lsi_scsi_setup(); + esp_scsi_setup(); + megasas_setup(); + pvscsi_setup(); +} + +static void +platform_hardware_setup(void) +{ + // Enable CPU caching + setcr0(getcr0() & ~(CR0_CD|CR0_NW)); + + // Make sure legacy DMA isn't running. + dma_setup(); + + // Init base pc hardware. + pic_setup(); + mathcp_setup(); + timer_setup(); + clock_setup(); + + // Platform specific setup + qemu_platform_setup(); + coreboot_platform_setup(); +} + +void +prepareboot(void) +{ + // Run BCVs + bcv_prepboot(); + + // Finalize data structures before boot + cdrom_prepboot(); + pmm_prepboot(); + malloc_prepboot(); + memmap_prepboot(); + + HaveRunPost = 2; + + // Setup bios checksum. + BiosChecksum -= checksum((u8*)BUILD_BIOS_ADDR, BUILD_BIOS_SIZE); +} + +// Begin the boot process by invoking an int0x19 in 16bit mode. +void VISIBLE32FLAT +startBoot(void) +{ + // Clear low-memory allocations (required by PMM spec). + memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR); + + dprintf(3, "Jump to int19\n"); + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_IF; + call16_int(0x19, &br); +} + +// Main setup code. +static void +maininit(void) +{ + // Initialize internal interfaces. + interface_init(); + + // Setup platform devices. + platform_hardware_setup(); + + // Start hardware initialization (if threads allowed during optionroms) + if (threads_during_optionroms()) + device_hardware_setup(); + + // Run vga option rom + vgarom_setup(); + + // Do hardware initialization (if running synchronously) + if (!threads_during_optionroms()) { + device_hardware_setup(); + wait_threads(); + } + + // Run option roms + optionrom_setup(); + + // Allow user to modify overall boot order. + interactive_bootmenu(); + wait_threads(); + + // Prepare for boot. + prepareboot(); + + // Write protect bios memory. + make_bios_readonly(); + + // Invoke int 19 to start boot process. + startBoot(); +} + + +/**************************************************************** + * POST entry and code relocation + ****************************************************************/ + +// Update given relocs for the code at 'dest' with a given 'delta' +static void +updateRelocs(void *dest, u32 *rstart, u32 *rend, u32 delta) +{ + u32 *reloc; + for (reloc = rstart; reloc < rend; reloc++) + *((u32*)(dest + *reloc)) += delta; +} + +// Relocate init code and then call a function at its new address. +// The passed function should be in the "init" section and must not +// return. +void __noreturn +reloc_preinit(void *f, void *arg) +{ + void (*func)(void *) __noreturn = f; + if (!CONFIG_RELOCATE_INIT) + func(arg); + // Symbols populated by the build. + extern u8 code32flat_start[]; + extern u8 _reloc_min_align; + extern u32 _reloc_abs_start[], _reloc_abs_end[]; + extern u32 _reloc_rel_start[], _reloc_rel_end[]; + extern u32 _reloc_init_start[], _reloc_init_end[]; + extern u8 code32init_start[], code32init_end[]; + + // Allocate space for init code. + u32 initsize = code32init_end - code32init_start; + u32 codealign = (u32)&_reloc_min_align; + void *codedest = memalign_tmp(codealign, initsize); + if (!codedest) + panic("No space for init relocation.\n"); + + // Copy code and update relocs (init absolute, init relative, and runtime) + dprintf(1, "Relocating init from %p to %p (size %d)\n" + , code32init_start, codedest, initsize); + s32 delta = codedest - (void*)code32init_start; + memcpy(codedest, code32init_start, initsize); + updateRelocs(codedest, _reloc_abs_start, _reloc_abs_end, delta); + updateRelocs(codedest, _reloc_rel_start, _reloc_rel_end, -delta); + updateRelocs(code32flat_start, _reloc_init_start, _reloc_init_end, delta); + if (f >= (void*)code32init_start && f < (void*)code32init_end) + func = f + delta; + + // Call function in relocated code. + barrier(); + func(arg); +} + +// Setup for code relocation and then relocate. +void VISIBLE32INIT +dopost(void) +{ + // Detect ram and setup internal malloc. + qemu_preinit(); + coreboot_preinit(); + malloc_preinit(); + + // Relocate initialization code and call maininit(). + reloc_preinit(maininit, NULL); +} + +// Entry point for Power On Self Test (POST) - the BIOS initilization +// phase. This function makes the memory at 0xc0000-0xfffff +// read/writable and then calls dopost(). +void VISIBLE32FLAT +handle_post(void) +{ + if (!CONFIG_QEMU && !CONFIG_COREBOOT) + return; + + serial_debug_preinit(); + debug_banner(); + + // Check if we are running under Xen. + xen_preinit(); + + // Allow writes to modify bios area (0xf0000) + make_bios_writable(); + + // Now that memory is read/writable - start post process. + dopost(); +} diff --git a/qemu/roms/seabios/src/resume.c b/qemu/roms/seabios/src/resume.c new file mode 100644 index 000000000..19031747c --- /dev/null +++ b/qemu/roms/seabios/src/resume.c @@ -0,0 +1,158 @@ +// Code for handling calls to "post" that are resume related. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_* +#include "farptr.h" // FLATPTR_TO_SEGOFF +#include "hw/pci.h" // pci_reboot +#include "hw/pic.h" // pic_eoi2 +#include "hw/ps2port.h" // i8042_reboot +#include "hw/rtc.h" // rtc_read +#include "output.h" // dprintf +#include "stacks.h" // farcall16big +#include "std/bda.h" // struct bios_data_area_s +#include "string.h" // memset +#include "util.h" // dma_setup + +// Handler for post calls that look like a resume. +void VISIBLE16 +handle_resume(void) +{ + ASSERT16(); + int status = rtc_read(CMOS_RESET_CODE); + rtc_write(CMOS_RESET_CODE, 0); + dprintf(1, "In resume (status=%d)\n", status); + + dma_setup(); + + switch (status) { + case 0x01 ... 0x04: + case 0x06 ... 0x09: + panic("Unimplemented shutdown status: %02x\n", status); + + case 0x05: + // flush keyboard (issue EOI) and jump via 40h:0067h + pic_eoi2(); + // NO BREAK + case 0x0a: +#define BDA_JUMP (((struct bios_data_area_s *)0)->jump) + // resume execution by jump via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "ljmpw *%0\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + case 0x0b: + // resume execution via IRET via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "lssw %0, %%sp\n" + "iretw\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + case 0x0c: + // resume execution via RETF via 40h:0067h + asm volatile( + "movw %w1, %%ds\n" + "lssw %0, %%sp\n" + "lretw\n" + : : "m"(BDA_JUMP), "r"(SEG_BDA) + ); + break; + + default: + break; + } + + // Not a 16bit resume - do remaining checks in 32bit mode + asm volatile( + "movw %w1, %%ss\n" + "movl %0, %%esp\n" + "movl $_cfunc32flat_handle_resume32, %%edx\n" + "jmp transition32\n" + : : "i"(BUILD_S3RESUME_STACK_ADDR), "r"(0), "a"(status) + ); +} + +// Handle an S3 resume event +static void +s3_resume(void) +{ + if (!CONFIG_S3_RESUME) + return; + + u32 s3_resume_vector = find_resume_vector(); + if (!s3_resume_vector) { + dprintf(1, "No resume vector set!\n"); + return; + } + + pic_setup(); + smm_setup(); + + pci_resume(); + + s3_resume_vga(); + + make_bios_readonly(); + + // Invoke the resume vector. + struct bregs br; + memset(&br, 0, sizeof(br)); + dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector); + br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector); + farcall16big(&br); +} + +u8 HaveAttemptedReboot VARLOW; + +// Attempt to invoke a hard-reboot. +static void +tryReboot(void) +{ + if (HaveAttemptedReboot) { + // Hard reboot has failed - try to shutdown machine. + dprintf(1, "Unable to hard-reboot machine - attempting shutdown.\n"); + apm_shutdown(); + } + HaveAttemptedReboot = 1; + + dprintf(1, "Attempting a hard reboot\n"); + + // Setup for reset on qemu. + qemu_prep_reset(); + + // Reboot using ACPI RESET_REG + acpi_reboot(); + + // Try keyboard controller reboot. + i8042_reboot(); + + // Try PCI 0xcf9 reboot + pci_reboot(); + + // Try triple fault + asm volatile("int3"); + + panic("Could not reboot"); +} + +void VISIBLE32FLAT +handle_resume32(int status) +{ + ASSERT32FLAT(); + dprintf(1, "In 32bit resume\n"); + + if (status == 0xfe) + s3_resume(); + + // Must be a soft reboot - invoke a hard reboot. + tryReboot(); +} diff --git a/qemu/roms/seabios/src/romfile.c b/qemu/roms/seabios/src/romfile.c new file mode 100644 index 000000000..42261a624 --- /dev/null +++ b/qemu/roms/seabios/src/romfile.c @@ -0,0 +1,100 @@ +// Access to pseudo "file" interface for configuration information. +// +// Copyright (C) 2012 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // struct romfile_s +#include "string.h" // memcmp + +static struct romfile_s *RomfileRoot VARVERIFY32INIT; + +void +romfile_add(struct romfile_s *file) +{ + dprintf(3, "Add romfile: %s (size=%d)\n", file->name, file->size); + file->next = RomfileRoot; + RomfileRoot = file; +} + +// Search for the specified file. +static struct romfile_s * +__romfile_findprefix(const char *prefix, int prefixlen, struct romfile_s *prev) +{ + struct romfile_s *cur = RomfileRoot; + if (prev) + cur = prev->next; + while (cur) { + if (memcmp(prefix, cur->name, prefixlen) == 0) + return cur; + cur = cur->next; + } + return NULL; +} + +struct romfile_s * +romfile_findprefix(const char *prefix, struct romfile_s *prev) +{ + return __romfile_findprefix(prefix, strlen(prefix), prev); +} + +struct romfile_s * +romfile_find(const char *name) +{ + return __romfile_findprefix(name, strlen(name) + 1, NULL); +} + +// Helper function to find, malloc_tmphigh, and copy a romfile. This +// function adds a trailing zero to the malloc'd copy. +void * +romfile_loadfile(const char *name, int *psize) +{ + struct romfile_s *file = romfile_find(name); + if (!file) + return NULL; + + int filesize = file->size; + if (!filesize) + return NULL; + + char *data = malloc_tmphigh(filesize+1); + if (!data) { + warn_noalloc(); + return NULL; + } + + dprintf(5, "Copying romfile '%s' (len %d)\n", name, filesize); + int ret = file->copy(file, data, filesize); + if (ret < 0) { + free(data); + return NULL; + } + if (psize) + *psize = filesize; + data[filesize] = '\0'; + return data; +} + +// Attempt to load an integer from the given file - return 'defval' +// if unsuccessful. +u64 +romfile_loadint(const char *name, u64 defval) +{ + struct romfile_s *file = romfile_find(name); + if (!file) + return defval; + + int filesize = file->size; + if (!filesize || filesize > sizeof(u64) || (filesize & (filesize-1))) + // Doesn't look like a valid integer. + return defval; + + u64 val = 0; + int ret = file->copy(file, &val, sizeof(val)); + if (ret < 0) + return defval; + return val; +} diff --git a/qemu/roms/seabios/src/romfile.h b/qemu/roms/seabios/src/romfile.h new file mode 100644 index 000000000..c6d62a1dd --- /dev/null +++ b/qemu/roms/seabios/src/romfile.h @@ -0,0 +1,19 @@ +#ifndef __ROMFILE_H +#define __ROMFILE_H + +#include "types.h" // u32 + +// romfile.c +struct romfile_s { + struct romfile_s *next; + char name[128]; + u32 size; + int (*copy)(struct romfile_s *file, void *dest, u32 maxlen); +}; +void romfile_add(struct romfile_s *file); +struct romfile_s *romfile_findprefix(const char *prefix, struct romfile_s *prev); +struct romfile_s *romfile_find(const char *name); +void *romfile_loadfile(const char *name, int *psize); +u64 romfile_loadint(const char *name, u64 defval); + +#endif // romfile.h diff --git a/qemu/roms/seabios/src/romlayout.S b/qemu/roms/seabios/src/romlayout.S new file mode 100644 index 000000000..93b6874e7 --- /dev/null +++ b/qemu/roms/seabios/src/romlayout.S @@ -0,0 +1,659 @@ +// Rom layout and bios assembler to C interface. +// +// Copyright (C) 2008-2012 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "asm-offsets.h" // BREGS_* +#include "config.h" // CONFIG_* +#include "entryfuncs.S" // ENTRY_* +#include "hw/rtc.h" // CMOS_RESET_CODE +#include "x86.h" // CR0_* + + .code16 + + +/**************************************************************** + * 16bit / 32bit call trampolines + ****************************************************************/ + +// Place CPU into 32bit mode from 16bit mode. +// %edx = return location (in 32bit mode) +// Clobbers: ecx, flags, segment registers, cr0, idt/gdt + DECLFUNC transition32 +transition32_nmi_off: + // transition32 when NMI and A20 are already initialized + movl %eax, %ecx + jmp 1f +transition32: + movl %eax, %ecx + + // Disable irqs (and clear direction flag) + cli + cld + + // Disable nmi + movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax + outb %al, $PORT_CMOS_INDEX + inb $PORT_CMOS_DATA, %al + + // enable a20 + inb $PORT_A20, %al + orb $A20_ENABLE_BIT, %al + outb %al, $PORT_A20 + + // Set segment descriptors +1: lidtw %cs:pmode_IDT_info + lgdtw %cs:rombios32_gdt_48 + + // Enable protected mode + movl %cr0, %eax + orl $CR0_PE, %eax + movl %eax, %cr0 + + // start 32bit protected mode code + ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f) + + .code32 + // init data segments +2: movl $SEG32_MODE32_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + movl %ecx, %eax + jmpl *%edx + .code16 + +// Place CPU into 16bit mode from 32bit mode. +// %edx = return location (in 16bit mode) +// Clobbers: ecx, flags, segment registers, cr0, idt/gdt + DECLFUNC transition16 + .global transition16big + .code32 +transition16: + movl %eax, %ecx + + // restore data segment limits to 0xffff + movl $SEG32_MODE16_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + +#if CONFIG_DISABLE_A20 + // disable a20 + inb $PORT_A20, %al + andb $~A20_ENABLE_BIT, %al + outb %al, $PORT_A20 +#endif + + // Jump to 16bit mode + ljmpw $SEG32_MODE16_CS, $1f + +transition16big: + movl %eax, %ecx + + movl $SEG32_MODE16BIG_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + ljmpw $SEG32_MODE16BIG_CS, $1f + + .code16 +1: + // Disable protected mode + movl %cr0, %eax + andl $~CR0_PE, %eax + movl %eax, %cr0 + + // far jump to flush CPU queue after transition to real mode + ljmpw $SEG_BIOS, $2f + +2: + // restore IDT to normal real-mode defaults + lidtw %cs:rmode_IDT_info + + // Clear segment registers + xorw %ax, %ax + movw %ax, %fs + movw %ax, %gs + movw %ax, %es + movw %ax, %ds + movw %ax, %ss // Assume stack is in segment 0 + + movl %ecx, %eax + jmpl *%edx + + +/**************************************************************** + * External calling trampolines + ****************************************************************/ + +// Far call a 16bit function from 16bit mode with a specified cpu register state +// %eax = address of struct bregs, %edx = segment of struct bregs +// Clobbers: %e[bc]x, %e[ds]i, flags + DECLFUNC __farcall16 +__farcall16: + // Save %edx/%eax, %ebp + pushl %ebp + pushl %eax + pushl %edx + + // Setup for iretw call + movl %edx, %ds + pushw %cs + pushw $1f // return point + pushw BREGS_flags(%eax) // flags + pushl BREGS_code(%eax) // CS:IP + + // Load calling registers and invoke call + RESTOREBREGS_DSEAX + iretw // XXX - just do a lcalll +1: + // Store flags, es, eax + pushfw + cli + cld + pushw %ds + pushl %eax + movw 0x08(%esp), %ds + movl 0x0c(%esp), %eax + SAVEBREGS_POP_DSEAX + popw BREGS_flags(%eax) + movw %ss, %cx + movw %cx, %ds // Restore %ds == %ss + + // Remove %edx/%eax, restore %ebp + popl %edx + popl %eax + popl %ebp + + retl + +// IRQ trampolines + .macro IRQ_TRAMPOLINE num + DECLFUNC irq_trampoline_0x\num + irq_trampoline_0x\num : + int $0x\num + lretw + .endm + + IRQ_TRAMPOLINE 02 + IRQ_TRAMPOLINE 10 + IRQ_TRAMPOLINE 13 + IRQ_TRAMPOLINE 15 + IRQ_TRAMPOLINE 16 + IRQ_TRAMPOLINE 18 + IRQ_TRAMPOLINE 19 + IRQ_TRAMPOLINE 1c + IRQ_TRAMPOLINE 4a + + +/**************************************************************** + * Misc. entry points. + ****************************************************************/ + +// Entry point for QEMU smi interrupts. + DECLFUNC entry_smi +entry_smi: + // Transition to 32bit mode. + movl $1f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 +1: movl $BUILD_SMM_ADDR + 0x8000, %esp + calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR + rsm + .code16 + +// Entry point for QEMU smp sipi interrupts. + DECLFUNC entry_smp +entry_smp: + // Transition to 32bit mode. + cli + cld + movl $2f + BUILD_BIOS_ADDR, %edx + jmp transition32_nmi_off + .code32 + // Acquire lock and take ownership of shared stack +1: rep ; nop +2: lock btsl $0, SMPLock + jc 1b + movl SMPStack, %esp + // Call handle_smp + calll _cfunc32flat_handle_smp - BUILD_BIOS_ADDR + // Release lock and halt processor. + movl $0, SMPLock +3: hlt + jmp 3b + .code16 + +// Resume (and reboot) entry point - called from entry_post + DECLFUNC entry_resume +entry_resume: + // Disable interrupts + cli + cld + // Use the ExtraStack in low mem. + movl $_zonelow_seg, %eax + movw %ax, %ds + movw %ax, %ss + movl $ExtraStack + BUILD_EXTRA_STACK_SIZE, %esp + // Call handler. + jmp handle_resume + +// PMM entry point + DECLFUNC entry_pmm +entry_pmm: + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp + pushfl // Save registers clobbered by C code + cli + cld + PUSHBREGS + movl %ss, %ecx // Move %ss to %ds + movw %cx, %ds + shll $4, %ecx + movl $_cfunc32flat_handle_pmm, %eax // Setup: call32(handle_pmm, args, -1) + leal PUSHBREGS_size+12(%esp, %ecx), %edx // %edx points to start of args + movl $-1, %ecx + calll call32 + movw %ax, BREGS_eax(%esp) // Modify %ax:%dx to return %eax + shrl $16, %eax + movw %ax, BREGS_edx(%esp) + POPBREGS + popfl + popl %esp + lretw + +// PnP entry points + DECLFUNC entry_pnp_real + .global entry_pnp_prot +entry_pnp_prot: + pushl %esp + jmp 1f +entry_pnp_real: + pushl %esp // Backup %esp, then clear high bits + movzwl %sp, %esp +1: + pushfl // Save registers clobbered by C code + cli + cld + PUSHBREGS + movw %ss, %cx // Move %ss to %ds + movw %cx, %ds + leal PUSHBREGS_size+12(%esp), %eax // %eax points to start of u16 args + calll handle_pnp + movw %ax, BREGS_eax(%esp) // Modify %eax to return %ax + POPBREGS + popfl + popl %esp + lretw + +// APM entry points + DECLFUNC entry_apm16 +entry_apm16: + pushfw // save flags + pushl %eax // dummy + ENTRY_ARG handle_apm + addw $4, %sp // pop dummy + popfw // restore flags + lretw + + DECLFUNC entry_apm32 + .code32 +entry_apm32: + pushfl + pushl %gs + pushl %cs // Move second descriptor after %cs to %gs + addl $16, (%esp) + popl %gs + ENTRY_ARG_ESP _cfunc32seg_handle_apm + popl %gs + popfl + lretl + .code16 + +// PCI-BIOS entry points + DECLFUNC entry_pcibios32 + .code32 +entry_pcibios32: + pushfl + pushl %gs // Backup %gs and set %gs=%ds + pushl %ds + popl %gs + ENTRY_ARG_ESP _cfunc32seg_handle_pcibios + popl %gs + popfl + lretl + .code16 + + DECLFUNC entry_pcibios16 +entry_pcibios16: + ENTRY_ARG handle_pcibios + iretw + +// int 1589 entry point + DECLFUNC entry_1589 +entry_1589: + ENTRY_ARG handle_1589 + iretw + +// BIOS32 support + DECLFUNC entry_bios32 + .code32 +entry_bios32: + pushfl +#if CONFIG_PCIBIOS + // Check for PCI-BIOS request + cmpl $0x49435024, %eax // $PCI + jne 1f + movl $BUILD_BIOS_ADDR, %ebx + movl $BUILD_BIOS_SIZE, %ecx + movl $entry_pcibios32, %edx + xorb %al, %al + jmp 2f +#endif + // Unknown request +1: movb $0x80, %al + // Return to caller +2: popfl + lretl + .code16 + +// 32bit elf entry point + DECLFUNC entry_elf + .code32 +entry_elf: + cli + cld + lidtl (BUILD_BIOS_ADDR + pmode_IDT_info) + lgdtl (BUILD_BIOS_ADDR + rombios32_gdt_48) + movl $SEG32_MODE32_DS, %eax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl $BUILD_STACK_ADDR, %esp + ljmpl $SEG32_MODE32_CS, $_cfunc32flat_handle_post + .code16 + +// UEFI Compatibility Support Module (CSM) entry point + DECLFUNC entry_csm +entry_csm: + // Backup register state + pushfw + cli + cld + pushl %eax // dummy + PUSHBREGS + + // Backup stack location and convert to a "flat pointer" + movl %ss, %eax + movw %ax, BREGS_code+2(%esp) // Store %ss in bregs->code.seg + shll $4, %eax + addl %esp, %eax + + // Change to BUILD_STACK_ADDR stack and call handle_csm(bregs) + ENTRY_INTO32 _cfunc32flat_handle_csm + + DECLFUNC __csm_return + .code32 +__csm_return: + movl $1f, %edx + jmp transition16big + .code16 + + // Switch back to original stack +1: movzwl BREGS_code+2(%eax), %edx + movl %edx, %ecx + shll $4, %ecx + subl %ecx, %eax + movl %edx, %ss + movl %eax, %esp + + // Restore register state and return. + POPBREGS + addw $4, %sp // pop dummy + popfw + lretw + + +/**************************************************************** + * Interrupt entry points + ****************************************************************/ + + // Main entry point for hardware interrupts handled on extra stack + DECLFUNC irqentry_extrastack +irqentry_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movl $_zonelow_seg, %eax + movl %eax, %ds + movl StackPos, %eax + subl $PUSHBREGS_size+8, %eax + SAVEBREGS_POP_DSEAX + popl %ecx + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll *%ecx + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + iretw + + // Main entry point for software interrupts handled on extra stack + DECLFUNC irqentry_arg_extrastack +irqentry_arg_extrastack: + cli + cld + pushw %ds // Set %ds:%eax to space on ExtraStack + pushl %eax + movl $_zonelow_seg, %eax + movl %eax, %ds + movl StackPos, %eax + subl $PUSHBREGS_size+16, %eax + SAVEBREGS_POP_DSEAX // Save registers on extra stack + popl %ecx + movl %esp, PUSHBREGS_size+8(%eax) + movw %ss, PUSHBREGS_size+12(%eax) + popl BREGS_code(%eax) + popw BREGS_flags(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll *%ecx + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+12(%eax), %ss + movl PUSHBREGS_size+8(%eax), %esp + popl %edx + popw %dx + pushw BREGS_flags(%eax) + pushl BREGS_code(%eax) + RESTOREBREGS_DSEAX + iretw + + // Main entry point for software interrupts (using caller's stack) + DECLFUNC irqentry_arg +irqentry_arg: + ENTRY_ARG_ST + iretw + + // Helper macros for hardware interrupt declaration + .macro IRQ_ENTRY num + .global entry_\num + entry_\num : + pushl $ handle_\num + jmp irqentry_extrastack + .endm + + .macro DECL_IRQ_ENTRY num + DECLFUNC entry_\num + IRQ_ENTRY \num + .endm + + // Helper macros for software interrupt declaration + .macro IRQ_ENTRY_ARG num + .global entry_\num + entry_\num : + pushl $ handle_\num +#if CONFIG_ENTRY_EXTRASTACK + jmp irqentry_arg_extrastack +#else + jmp irqentry_arg +#endif + .endm + + .macro DECL_IRQ_ENTRY_ARG num + DECLFUNC entry_\num + IRQ_ENTRY_ARG \num + .endm + + // Various entry points (that don't require a fixed location). + DECL_IRQ_ENTRY_ARG 13 + DECL_IRQ_ENTRY 76 + DECL_IRQ_ENTRY 70 + DECL_IRQ_ENTRY 74 + DECL_IRQ_ENTRY 75 + DECL_IRQ_ENTRY hwpic1 + DECL_IRQ_ENTRY hwpic2 + + // int 18/19 are special - they reset stack and call into 32bit mode. + DECLFUNC entry_19 +entry_19: + ENTRY_INTO32 _cfunc32flat_handle_19 + + DECLFUNC entry_18 +entry_18: + ENTRY_INTO32 _cfunc32flat_handle_18 + + +/**************************************************************** + * Fixed position entry points + ****************************************************************/ + + // Specify a location in the fixed part of bios area. + .macro ORG addr + .section .fixedaddr.\addr + .endm + + ORG 0xe05b +entry_post: + cmpl $0, %cs:HaveRunPost // Check for resume/reboot + jnz entry_resume + ENTRY_INTO32 _cfunc32flat_handle_post // Normal entry point + + ORG 0xe2c3 + IRQ_ENTRY 02 + + ORG 0xe3fe + .global entry_13_official +entry_13_official: + jmp entry_13 + + // 0xe401 - OldFDPT in misc.c + + ORG 0xe6f2 + .global entry_19_official +entry_19_official: + jmp entry_19 + + // 0xe6f5 - BIOS_CONFIG_TABLE in misc.c + + // 0xe729 - BaudTable in misc.c + + ORG 0xe739 + IRQ_ENTRY_ARG 14 + + ORG 0xe82e + IRQ_ENTRY_ARG 16 + + ORG 0xe987 + IRQ_ENTRY 09 + + ORG 0xec59 + IRQ_ENTRY_ARG 40 + + ORG 0xef57 + IRQ_ENTRY 0e + + // 0xefc7 - diskette_param_table in misc.c + + ORG 0xefd2 + IRQ_ENTRY_ARG 17 + + ORG 0xf045 +entry_10_0x0f: + // XXX - INT 10 Functions 0-Fh Entry Point + iretw + + ORG 0xf065 + IRQ_ENTRY_ARG 10 + + // 0xf0a4 - VideoParams in misc.c + + ORG 0xf841 + IRQ_ENTRY_ARG 12 + + ORG 0xf84d + IRQ_ENTRY_ARG 11 + + ORG 0xf859 + .global entry_15_official +entry_15_official: + cmpb $0x89, %ah + je entry_1589 // 1589 calls return in protected mode + IRQ_ENTRY_ARG 15 + + // 0xfa6e - vgafont8 in font.c + + ORG 0xfe6e + .global entry_1a_official +entry_1a_official: + cmpb $0xb1, %ah + je entry_pcibios16 // PCIBIOS calls can be in protected mode + IRQ_ENTRY_ARG 1a + + ORG 0xfea5 + IRQ_ENTRY 08 + + // 0xfef3 - InitVectors in misc.c + + ORG 0xff53 + .global entry_iret_official +entry_iret_official: + iretw + + ORG 0xff54 + IRQ_ENTRY_ARG 05 + + ORG 0xfff0 // Power-up Entry Point + .global reset_vector +reset_vector: + ljmpw $SEG_BIOS, $entry_post + + // 0xfff5 - BiosDate in misc.c + + // 0xfffe - BiosModelId in misc.c + + // 0xffff - BiosChecksum in misc.c + + .end diff --git a/qemu/roms/seabios/src/serial.c b/qemu/roms/seabios/src/serial.c new file mode 100644 index 000000000..88349c8a9 --- /dev/null +++ b/qemu/roms/seabios/src/serial.c @@ -0,0 +1,317 @@ +// 16bit code to handle serial and printer services. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "bregs.h" // struct bregs +#include "hw/serialio.h" // SEROFF_IER +#include "output.h" // debug_enter +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // serial_setup + + +/**************************************************************** + * COM ports + ****************************************************************/ + +static u16 +detect_serial(u16 port, u8 timeout, u8 count) +{ + if (CONFIG_DEBUG_SERIAL && port == CONFIG_DEBUG_SERIAL_PORT + && !romfile_loadint("etc/advertise-serial-debug-port", 1)) + return 0; + outb(0x02, port+SEROFF_IER); + u8 ier = inb(port+SEROFF_IER); + if (ier != 0x02) + return 0; + u8 iir = inb(port+SEROFF_IIR); + if ((iir & 0x3f) != 0x02) + return 0; + + outb(0x00, port+SEROFF_IER); + SET_BDA(port_com[count], port); + SET_BDA(com_timeout[count], timeout); + return 1; +} + +void +serial_setup(void) +{ + if (! CONFIG_SERIAL) + return; + dprintf(3, "init serial\n"); + + u16 count = 0; + count += detect_serial(PORT_SERIAL1, 0x0a, count); + count += detect_serial(PORT_SERIAL2, 0x0a, count); + count += detect_serial(PORT_SERIAL3, 0x0a, count); + count += detect_serial(PORT_SERIAL4, 0x0a, count); + dprintf(1, "Found %d serial ports\n", count); + + // Equipment word bits 9..11 determing # serial ports + set_equipment_flags(0xe00, count << 9); +} + +static u16 +getComAddr(struct bregs *regs) +{ + if (regs->dx >= 4) { + set_invalid(regs); + return 0; + } + u16 addr = GET_BDA(port_com[regs->dx]); + if (! addr) + set_invalid(regs); + return addr; +} + +// SERIAL - INITIALIZE PORT +static void +handle_1400(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + outb(inb(addr+SEROFF_LCR) | 0x80, addr+SEROFF_LCR); + if ((regs->al & 0xE0) == 0) { + outb(0x17, addr+SEROFF_DLL); + outb(0x04, addr+SEROFF_DLH); + } else { + u16 val16 = 0x600 >> ((regs->al & 0xE0) >> 5); + outb(val16 & 0xFF, addr+SEROFF_DLL); + outb(val16 >> 8, addr+SEROFF_DLH); + } + outb(regs->al & 0x1F, addr+SEROFF_LCR); + regs->ah = inb(addr+SEROFF_LSR); + regs->al = inb(addr+SEROFF_MSR); + set_success(regs); +} + +// SERIAL - WRITE CHARACTER TO PORT +static void +handle_1401(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); + for (;;) { + u8 lsr = inb(addr+SEROFF_LSR); + if ((lsr & 0x60) == 0x60) { + // Success - can write data + outb(regs->al, addr+SEROFF_DATA); + // XXX - reread lsr? + regs->ah = lsr; + break; + } + if (irqtimer_check(end)) { + // Timed out - can't write data. + regs->ah = lsr | 0x80; + break; + } + yield(); + } + set_success(regs); +} + +// SERIAL - READ CHARACTER FROM PORT +static void +handle_1402(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + u32 end = irqtimer_calc_ticks(GET_BDA(com_timeout[regs->dx])); + for (;;) { + u8 lsr = inb(addr+SEROFF_LSR); + if (lsr & 0x01) { + // Success - can read data + regs->al = inb(addr+SEROFF_DATA); + regs->ah = lsr; + break; + } + if (irqtimer_check(end)) { + // Timed out - can't read data. + regs->ah = lsr | 0x80; + break; + } + yield(); + } + set_success(regs); +} + +// SERIAL - GET PORT STATUS +static void +handle_1403(struct bregs *regs) +{ + u16 addr = getComAddr(regs); + if (!addr) + return; + regs->ah = inb(addr+SEROFF_LSR); + regs->al = inb(addr+SEROFF_MSR); + set_success(regs); +} + +static void +handle_14XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT 14h Serial Communications Service Entry Point +void VISIBLE16 +handle_14(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_14); + if (! CONFIG_SERIAL) { + handle_14XX(regs); + return; + } + + switch (regs->ah) { + case 0x00: handle_1400(regs); break; + case 0x01: handle_1401(regs); break; + case 0x02: handle_1402(regs); break; + case 0x03: handle_1403(regs); break; + default: handle_14XX(regs); break; + } +} + + +/**************************************************************** + * LPT ports + ****************************************************************/ + +static u16 +detect_parport(u16 port, u8 timeout, u8 count) +{ + // clear input mode + outb(inb(port+2) & 0xdf, port+2); + + outb(0xaa, port); + if (inb(port) != 0xaa) + // Not present + return 0; + SET_BDA(port_lpt[count], port); + SET_BDA(lpt_timeout[count], timeout); + return 1; +} + +void +lpt_setup(void) +{ + if (! CONFIG_LPT) + return; + dprintf(3, "init lpt\n"); + + u16 count = 0; + count += detect_parport(PORT_LPT1, 0x14, count); + count += detect_parport(PORT_LPT2, 0x14, count); + dprintf(1, "Found %d lpt ports\n", count); + + // Equipment word bits 14..15 determing # parallel ports + set_equipment_flags(0xc000, count << 14); +} + +static u16 +getLptAddr(struct bregs *regs) +{ + if (regs->dx >= 3) { + set_invalid(regs); + return 0; + } + u16 addr = GET_BDA(port_lpt[regs->dx]); + if (! addr) + set_invalid(regs); + return addr; +} + +// INT 17 - PRINTER - WRITE CHARACTER +static void +handle_1700(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + + u32 end = irqtimer_calc_ticks(GET_BDA(lpt_timeout[regs->dx])); + + outb(regs->al, addr); + u8 val8 = inb(addr+2); + outb(val8 | 0x01, addr+2); // send strobe + udelay(5); + outb(val8 & ~0x01, addr+2); + + for (;;) { + u8 v = inb(addr+1); + if (!(v & 0x40)) { + // Success + regs->ah = v ^ 0x48; + break; + } + if (irqtimer_check(end)) { + // Timeout + regs->ah = (v ^ 0x48) | 0x01; + break; + } + yield(); + } + + set_success(regs); +} + +// INT 17 - PRINTER - INITIALIZE PORT +static void +handle_1701(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + + u8 val8 = inb(addr+2); + outb(val8 & ~0x04, addr+2); // send init + udelay(5); + outb(val8 | 0x04, addr+2); + + regs->ah = inb(addr+1) ^ 0x48; + set_success(regs); +} + +// INT 17 - PRINTER - GET STATUS +static void +handle_1702(struct bregs *regs) +{ + u16 addr = getLptAddr(regs); + if (!addr) + return; + regs->ah = inb(addr+1) ^ 0x48; + set_success(regs); +} + +static void +handle_17XX(struct bregs *regs) +{ + set_unimplemented(regs); +} + +// INT17h : Printer Service Entry Point +void VISIBLE16 +handle_17(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_17); + if (! CONFIG_LPT) { + handle_17XX(regs); + return; + } + + switch (regs->ah) { + case 0x00: handle_1700(regs); break; + case 0x01: handle_1701(regs); break; + case 0x02: handle_1702(regs); break; + default: handle_17XX(regs); break; + } +} diff --git a/qemu/roms/seabios/src/stacks.c b/qemu/roms/seabios/src/stacks.c new file mode 100644 index 000000000..1dbdfe9bb --- /dev/null +++ b/qemu/roms/seabios/src/stacks.c @@ -0,0 +1,827 @@ +// Code for manipulating stack locations. +// +// Copyright (C) 2009-2014 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // CR0_PE +#include "fw/paravirt.h" // PORT_SMI_CMD +#include "hw/rtc.h" // rtc_use +#include "list.h" // hlist_node +#include "malloc.h" // free +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "stacks.h" // struct mutex_s +#include "util.h" // useRTC + +#define MAIN_STACK_MAX (1024*1024) + + +/**************************************************************** + * 16bit / 32bit calling + ****************************************************************/ + +struct { + u8 method; + u8 cmosindex; + u8 a20; + u16 ss, fs, gs; + struct descloc_s gdt; +} Call32Data VARLOW; + +#define C32_SLOPPY 1 +#define C32_SMM 2 + +int HaveSmmCall32 VARFSEG; + +// Backup state in preparation for call32_smm() +static void +call32_smm_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Backup ss + SET_LOW(Call32Data.ss, GET_SEG(SS)); + + SET_LOW(Call32Data.method, C32_SMM); +} + +// Restore state backed up during call32_smm() +static void +call32_smm_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +#define ASM32_SWITCH16 " .pushsection .text.32fseg." UNIQSEC "\n .code16\n" +#define ASM32_BACK32 " .popsection\n .code32\n" +#define ASM16_SWITCH32 " .code32\n" +#define ASM16_BACK16 " .code16gcc\n" + +// Call a SeaBIOS C function in 32bit mode using smm trampoline +static u32 +call32_smm(void *func, u32 eax) +{ + ASSERT16(); + dprintf(9, "call32_smm %p %x\n", func, eax); + call32_smm_prep(); + u32 bkup_esp; + asm volatile( + // Backup esp / set esp to flat stack location + " movl %%esp, %0\n" + " movl %%ss, %%eax\n" + " shll $4, %%eax\n" + " addl %%eax, %%esp\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM16_SWITCH32 + "1:movl %1, %%eax\n" + " calll *%2\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Restore esp + ASM16_BACK16 + "2:movl %0, %%esp\n" + : "=&r" (bkup_esp), "+r" (eax) + : "r" (func) + : "eax", "ecx", "edx", "ebx", "cc", "memory"); + call32_smm_post(); + + dprintf(9, "call32_smm done %p %x\n", func, eax); + return eax; +} + +// 16bit handler code called from call16_smm() +u32 VISIBLE16 +call16_smm_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + if (!CONFIG_CALL32_SMM) + return eax; + call32_smm_post(); + u32 ret = func(eax, edx); + call32_smm_prep(); + return ret; +} + +static u32 +call16_smm(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (!CONFIG_CALL32_SMM) + return eax; + func -= BUILD_BIOS_ADDR; + dprintf(9, "call16_smm %p %x %x\n", func, eax, edx); + u32 stackoffset = Call32Data.ss << 4; + asm volatile( + // Restore esp + " subl %0, %%esp\n" + + // Transition to 16bit mode, call func, return to 32bit + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_RETURNID) ", %%ecx\n" + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + ASM32_SWITCH16 + "1:movl %1, %%eax\n" + " movl %3, %%ecx\n" + " calll _cfunc16_call16_smm_helper\n" + " movl %%eax, %1\n" + + " movl $" __stringify(CALL32SMM_CMDID) ", %%eax\n" + " movl $" __stringify(CALL32SMM_ENTERID) ", %%ecx\n" + " movl $2f, %%ebx\n" + " outb %%al, $" __stringify(PORT_SMI_CMD) "\n" + " rep; nop\n" + " hlt\n" + + // Set esp to flat stack location + ASM32_BACK32 + "2:addl %0, %%esp\n" + : "+r" (stackoffset), "+r" (eax), "+d" (edx) + : "r" (func) + : "eax", "ecx", "ebx", "cc", "memory"); + return eax; +} + +// Backup state in preparation for call32_sloppy() +static void +call32_sloppy_prep(void) +{ + // Backup cmos index register and disable nmi + u8 cmosindex = inb(PORT_CMOS_INDEX); + outb(cmosindex | NMI_DISABLE_BIT, PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); + SET_LOW(Call32Data.cmosindex, cmosindex); + + // Enable a20 and backup it's previous state + SET_LOW(Call32Data.a20, set_a20(1)); + + // Backup ss/fs/gs and gdt + SET_LOW(Call32Data.ss, GET_SEG(SS)); + SET_LOW(Call32Data.fs, GET_SEG(FS)); + SET_LOW(Call32Data.gs, GET_SEG(GS)); + struct descloc_s gdt; + sgdt(&gdt); + SET_LOW(Call32Data.gdt.length, gdt.length); + SET_LOW(Call32Data.gdt.addr, gdt.addr); + + SET_LOW(Call32Data.method, C32_SLOPPY); +} + +// Restore state backed up during call32_sloppy() +static void +call32_sloppy_post(void) +{ + SET_LOW(Call32Data.method, 0); + SET_LOW(Call32Data.ss, 0); + + // Restore gdt and fs/gs + struct descloc_s gdt; + gdt.length = GET_LOW(Call32Data.gdt.length); + gdt.addr = GET_LOW(Call32Data.gdt.addr); + lgdt(&gdt); + SET_SEG(FS, GET_LOW(Call32Data.fs)); + SET_SEG(GS, GET_LOW(Call32Data.gs)); + + // Restore a20 + set_a20(GET_LOW(Call32Data.a20)); + + // Restore cmos index register + outb(GET_LOW(Call32Data.cmosindex), PORT_CMOS_INDEX); + inb(PORT_CMOS_DATA); +} + +// Call a C function in 32bit mode. This clobbers the 16bit segment +// selector registers. +static u32 +call32_sloppy(void *func, u32 eax) +{ + ASSERT16(); + call32_sloppy_prep(); + u32 bkup_ss, bkup_esp; + asm volatile( + // Backup ss/esp / set esp to flat stack location + " movl %%ss, %0\n" + " movl %%esp, %1\n" + " shll $4, %0\n" + " addl %0, %%esp\n" + " shrl $4, %0\n" + + // Transition to 32bit mode, call func, return to 16bit + " movl $(" __stringify(BUILD_BIOS_ADDR) " + 1f), %%edx\n" + " jmp transition32\n" + ASM16_SWITCH32 + "1:calll *%3\n" + " movl $2f, %%edx\n" + " jmp transition16big\n" + + // Restore ds/ss/esp + ASM16_BACK16 + "2:movl %0, %%ds\n" + " movl %0, %%ss\n" + " movl %1, %%esp\n" + : "=&r" (bkup_ss), "=&r" (bkup_esp), "+a" (eax) + : "r" (func) + : "ecx", "edx", "cc", "memory"); + call32_sloppy_post(); + return eax; +} + +// 16bit handler code called from call16_sloppy() +u32 VISIBLE16 +call16_sloppy_helper(u32 eax, u32 edx, u32 (*func)(u32 eax, u32 edx)) +{ + call32_sloppy_post(); + u32 ret = func(eax, edx); + call32_sloppy_prep(); + return ret; +} + +// Jump back to 16bit mode while in 32bit mode from call32_sloppy() +static u32 +call16_sloppy(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > MAIN_STACK_MAX) + panic("call16_sloppy with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + u32 stackseg = Call32Data.ss; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Setup ss/esp and call func + ASM32_SWITCH16 + "1:movl %3, %%ecx\n" + " shll $4, %3\n" + " movw %%cx, %%ss\n" + " subl %3, %%esp\n" + " movw %%cx, %%ds\n" + " movl %2, %%edx\n" + " movl %1, %%ecx\n" + " calll _cfunc16_call16_sloppy_helper\n" + // Return to 32bit and restore esp + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:addl %3, %%esp\n" + : "+a" (eax) + : "r" (func), "r" (edx), "r" (stackseg) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 32bit SeaBIOS function from a 16bit SeaBIOS function. +u32 VISIBLE16 +call32(void *func, u32 eax, u32 errret) +{ + ASSERT16(); + if (CONFIG_CALL32_SMM && GET_GLOBAL(HaveSmmCall32)) + return call32_smm(func, eax); + u32 cr0 = getcr0(); + if (cr0 & CR0_PE) + // Called in 16bit protected mode?! + return errret; + return call32_sloppy(func, eax); +} + +// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function. +static u32 +call16(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16 with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function in "big real" mode. +static u32 +call16big(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (getesp() > BUILD_STACK_ADDR) + panic("call16big with invalid stack\n"); + func -= BUILD_BIOS_ADDR; + asm volatile( + // Transition to 16bit mode + " movl $(1f - " __stringify(BUILD_BIOS_ADDR) "), %%edx\n" + " jmp transition16big\n" + // Call func + ASM32_SWITCH16 + "1:movl %2, %%edx\n" + " calll *%1\n" + // Return to 32bit + " movl $2f, %%edx\n" + " jmp transition32\n" + ASM32_BACK32 + "2:\n" + : "+a" (eax) + : "r" (func), "r" (edx) + : "edx", "ecx", "cc", "memory"); + return eax; +} + +// Call a 16bit SeaBIOS function, restoring the mode from last call32(). +static u32 +call16_back(u32 eax, u32 edx, void *func) +{ + ASSERT32FLAT(); + if (CONFIG_CALL32_SMM && Call32Data.method == C32_SMM) + return call16_smm(eax, edx, func); + if (Call32Data.method == C32_SLOPPY) + return call16_sloppy(eax, edx, func); + if (in_post()) + return call16big(eax, edx, func); + return call16(eax, edx, func); +} + + +/**************************************************************** + * Extra 16bit stack + ****************************************************************/ + +// Space for a stack for 16bit code. +u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] VARLOW __aligned(8); +u8 *StackPos VARLOW; + +// Test if currently on the extra stack +int +on_extra_stack(void) +{ + return MODE16 && GET_SEG(SS) == SEG_LOW && getesp() > (u32)ExtraStack; +} + +// Switch to the extra stack and call a function. +u32 +stack_hop(u32 eax, u32 edx, void *func) +{ + if (on_extra_stack()) + return ((u32 (*)(u32, u32))func)(eax, edx); + ASSERT16(); + u16 stack_seg = SEG_LOW; + u32 bkup_ss, bkup_esp; + asm volatile( + // Backup current %ss/%esp values. + "movw %%ss, %w3\n" + "movl %%esp, %4\n" + // Copy stack seg to %ds/%ss and set %esp + "movw %w6, %%ds\n" + "movw %w6, %%ss\n" + "movl %5, %%esp\n" + "pushl %3\n" + "pushl %4\n" + // Call func + "calll *%2\n" + "popl %4\n" + "popl %3\n" + // Restore segments and stack + "movw %w3, %%ds\n" + "movw %w3, %%ss\n" + "movl %4, %%esp" + : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss), "=&r" (bkup_esp) + : "m" (StackPos), "r" (stack_seg) + : "cc", "memory"); + return eax; +} + +// Switch back to original caller's stack and call a function. +u32 +stack_hop_back(u32 eax, u32 edx, void *func) +{ + if (!MODESEGMENT) + return call16_back(eax, edx, func); + if (!MODE16 || !on_extra_stack()) + return ((u32 (*)(u32, u32))func)(eax, edx); + ASSERT16(); + u16 bkup_ss; + u32 bkup_stack_pos, temp; + asm volatile( + // Backup stack_pos and current %ss/%esp + "movl %6, %4\n" + "movw %%ss, %w3\n" + "movl %%esp, %6\n" + // Restore original callers' %ss/%esp + "movl -4(%4), %5\n" + "movl %5, %%ss\n" + "movw %%ds:-8(%4), %%sp\n" + "movl %5, %%ds\n" + // Call func + "calll *%2\n" + // Restore %ss/%esp and stack_pos + "movw %w3, %%ds\n" + "movw %w3, %%ss\n" + "movl %6, %%esp\n" + "movl %4, %6" + : "+a" (eax), "+d" (edx), "+c" (func), "=&r" (bkup_ss) + , "=&r" (bkup_stack_pos), "=&r" (temp), "+m" (StackPos) + : + : "cc", "memory"); + return eax; +} + + +/**************************************************************** + * External 16bit interface calling + ****************************************************************/ + +// Far call 16bit code with a specified register state. +void VISIBLE16 +_farcall16(struct bregs *callregs, u16 callregseg) +{ + if (need_hop_back()) { + extern void _cfunc16__farcall16(void); + stack_hop_back((u32)callregs, callregseg, _cfunc16__farcall16); + return; + } + ASSERT16(); + asm volatile( + "calll __farcall16\n" + : "+a" (callregs), "+m" (*callregs), "+d" (callregseg) + : + : "ebx", "ecx", "esi", "edi", "cc", "memory"); +} + +void +farcall16(struct bregs *callregs) +{ + extern void _cfunc16__farcall16(void); + call16((u32)callregs, 0, _cfunc16__farcall16); +} + +void +farcall16big(struct bregs *callregs) +{ + extern void _cfunc16__farcall16(void); + call16big((u32)callregs, 0, _cfunc16__farcall16); +} + +// Invoke a 16bit software interrupt. +void +__call16_int(struct bregs *callregs, u16 offset) +{ + callregs->code.offset = offset; + if (!MODESEGMENT) { + callregs->code.seg = SEG_BIOS; + _farcall16((void*)callregs - Call32Data.ss * 16, Call32Data.ss); + return; + } + callregs->code.seg = GET_SEG(CS); + _farcall16(callregs, GET_SEG(SS)); +} + +// Reset the machine +void +reset(void) +{ + extern void reset_vector(void) __noreturn; + if (!MODE16) + call16_back(0, 0, reset_vector); + reset_vector(); +} + + +/**************************************************************** + * Threads + ****************************************************************/ + +// Thread info - stored at bottom of each thread stack - don't change +// without also updating the inline assembler below. +struct thread_info { + void *stackpos; + struct hlist_node node; +}; +struct thread_info MainThread VARFSEG = { + NULL, { &MainThread.node, &MainThread.node.next } +}; +#define THREADSTACKSIZE 4096 + +// Check if any threads are running. +static int +have_threads(void) +{ + return (CONFIG_THREADS + && GET_FLATPTR(MainThread.node.next) != &MainThread.node); +} + +// Return the 'struct thread_info' for the currently running thread. +struct thread_info * +getCurThread(void) +{ + u32 esp = getesp(); + if (esp <= MAIN_STACK_MAX) + return &MainThread; + return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE); +} + +static int ThreadControl; + +// Initialize the support for internal threads. +void +thread_init(void) +{ + if (! CONFIG_THREADS) + return; + ThreadControl = romfile_loadint("etc/threads", 1); +} + +// Should hardware initialization threads run during optionrom execution. +int +threads_during_optionroms(void) +{ + return CONFIG_THREADS && ThreadControl == 2 && in_post(); +} + +// Switch to next thread stack. +static void +switch_next(struct thread_info *cur) +{ + struct thread_info *next = container_of( + cur->node.next, struct thread_info, node); + if (cur == next) + // Nothing to do. + return; + asm volatile( + " pushl $1f\n" // store return pc + " pushl %%ebp\n" // backup %ebp + " movl %%esp, (%%eax)\n" // cur->stackpos = %esp + " movl (%%ecx), %%esp\n" // %esp = next->stackpos + " popl %%ebp\n" // restore %ebp + " retl\n" // restore pc + "1:\n" + : "+a"(cur), "+c"(next) + : + : "ebx", "edx", "esi", "edi", "cc", "memory"); +} + +// Last thing called from a thread (called on MainThread stack). +static void +__end_thread(struct thread_info *old) +{ + hlist_del(&old->node); + dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old); + free(old); + if (!have_threads()) + dprintf(1, "All threads complete.\n"); +} + +// Create a new thread and start executing 'func' in it. +void +run_thread(void (*func)(void*), void *data) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS || ! ThreadControl) + goto fail; + struct thread_info *thread; + thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE); + if (!thread) + goto fail; + + dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread); + thread->stackpos = (void*)thread + THREADSTACKSIZE; + struct thread_info *cur = getCurThread(); + hlist_add_after(&thread->node, &cur->node); + asm volatile( + // Start thread + " pushl $1f\n" // store return pc + " pushl %%ebp\n" // backup %ebp + " movl %%esp, (%%edx)\n" // cur->stackpos = %esp + " movl (%%ebx), %%esp\n" // %esp = thread->stackpos + " calll *%%ecx\n" // Call func + + // End thread + " movl %%ebx, %%eax\n" // %eax = thread + " movl 4(%%ebx), %%ebx\n" // %ebx = thread->node.next + " movl (%5), %%esp\n" // %esp = MainThread.stackpos + " calll %4\n" // call __end_thread(thread) + " movl -4(%%ebx), %%esp\n" // %esp = next->stackpos + " popl %%ebp\n" // restore %ebp + " retl\n" // restore pc + "1:\n" + : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur) + : "m"(*(u8*)__end_thread), "m"(MainThread) + : "esi", "edi", "cc", "memory"); + return; + +fail: + func(data); +} + + +/**************************************************************** + * Thread helpers + ****************************************************************/ + +// Low-level irq enable. +void VISIBLE16 +check_irqs(void) +{ + if (need_hop_back()) { + extern void _cfunc16_check_irqs(void); + stack_hop_back(0, 0, _cfunc16_check_irqs); + return; + } + asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); +} + +// Briefly permit irqs to occur. +void +yield(void) +{ + if (MODESEGMENT || !CONFIG_THREADS) { + check_irqs(); + return; + } + struct thread_info *cur = getCurThread(); + if (cur == &MainThread) + // Permit irqs to fire + check_irqs(); + + // Switch to the next thread + switch_next(cur); +} + +void VISIBLE16 +wait_irq(void) +{ + if (need_hop_back()) { + extern void _cfunc16_wait_irq(void); + stack_hop_back(0, 0, _cfunc16_wait_irq); + return; + } + asm volatile("sti ; hlt ; cli ; cld": : :"memory"); +} + +// Wait for next irq to occur. +void +yield_toirq(void) +{ + if (!MODESEGMENT && have_threads()) { + // Threads still active - do a yield instead. + yield(); + return; + } + wait_irq(); +} + +// Wait for all threads (other than the main thread) to complete. +void +wait_threads(void) +{ + ASSERT32FLAT(); + while (have_threads()) + yield(); +} + +void +mutex_lock(struct mutex_s *mutex) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS) + return; + while (mutex->isLocked) + yield(); + mutex->isLocked = 1; +} + +void +mutex_unlock(struct mutex_s *mutex) +{ + ASSERT32FLAT(); + if (! CONFIG_THREADS) + return; + mutex->isLocked = 0; +} + + +/**************************************************************** + * Thread preemption + ****************************************************************/ + +int CanPreempt VARFSEG; +static u32 PreemptCount; + +// Turn on RTC irqs and arrange for them to check the 32bit threads. +void +start_preempt(void) +{ + if (! threads_during_optionroms()) + return; + CanPreempt = 1; + PreemptCount = 0; + rtc_use(); +} + +// Turn off RTC irqs / stop checking for thread execution. +void +finish_preempt(void) +{ + if (! threads_during_optionroms()) { + yield(); + return; + } + CanPreempt = 0; + rtc_release(); + dprintf(9, "Done preempt - %d checks\n", PreemptCount); + yield(); +} + +// Check if preemption is on, and wait for it to complete if so. +int +wait_preempt(void) +{ + if (MODESEGMENT || !CONFIG_THREADS || !CanPreempt + || getesp() < MAIN_STACK_MAX) + return 0; + while (CanPreempt) + yield(); + return 1; +} + +// Try to execute 32bit threads. +void VISIBLE32INIT +yield_preempt(void) +{ + PreemptCount++; + switch_next(&MainThread); +} + +// 16bit code that checks if threads are pending and executes them if so. +void +check_preempt(void) +{ + extern void _cfunc32flat_yield_preempt(void); + if (CONFIG_THREADS && GET_GLOBAL(CanPreempt) && have_threads()) + call32(_cfunc32flat_yield_preempt, 0, 0); +} + + +/**************************************************************** + * call32 helper + ****************************************************************/ + +struct call32_params_s { + void *func; + u32 eax, edx, ecx; +}; + +u32 VISIBLE32FLAT +call32_params_helper(struct call32_params_s *params) +{ + return ((u32 (*)(u32, u32, u32))params->func)( + params->eax, params->edx, params->ecx); +} + +u32 +call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret) +{ + ASSERT16(); + struct call32_params_s params = {func, eax, edx, ecx}; + extern void _cfunc32flat_call32_params_helper(void); + return call32(_cfunc32flat_call32_params_helper + , (u32)MAKE_FLATPTR(GET_SEG(SS), ¶ms), errret); +} diff --git a/qemu/roms/seabios/src/stacks.h b/qemu/roms/seabios/src/stacks.h new file mode 100644 index 000000000..82c4c3c85 --- /dev/null +++ b/qemu/roms/seabios/src/stacks.h @@ -0,0 +1,53 @@ +// Misc function and variable declarations. +#ifndef __STACKS_H +#define __STACKS_H + +#include "types.h" // u32 + +#define CALL32SMM_CMDID 0xb5 +#define CALL32SMM_ENTERID 0x1234 +#define CALL32SMM_RETURNID 0x5678 + +// stacks.c +extern int HaveSmmCall32; +u32 call32(void *func, u32 eax, u32 errret); +extern u8 ExtraStack[], *StackPos; +u32 stack_hop(u32 eax, u32 edx, void *func); +u32 stack_hop_back(u32 eax, u32 edx, void *func); +int on_extra_stack(void); +struct bregs; +void farcall16(struct bregs *callregs); +void farcall16big(struct bregs *callregs); +void __call16_int(struct bregs *callregs, u16 offset); +#define call16_int(nr, callregs) do { \ + extern void irq_trampoline_ ##nr (); \ + __call16_int((callregs), (u32)&irq_trampoline_ ##nr ); \ + } while (0) +void reset(void); +extern struct thread_info MainThread; +struct thread_info *getCurThread(void); +void yield(void); +void yield_toirq(void); +void thread_init(void); +int threads_during_optionroms(void); +void run_thread(void (*func)(void*), void *data); +void wait_threads(void); +struct mutex_s { u32 isLocked; }; +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); +void check_preempt(void); +u32 call32_params(void *func, u32 eax, u32 edx, u32 ecx, u32 errret); + +// Inline functions + +// Check if a call to stack_hop_back is needed. +static inline int +need_hop_back(void) +{ + return !MODESEGMENT || on_extra_stack(); +} + +#endif // stacks.h diff --git a/qemu/roms/seabios/src/std/LegacyBios.h b/qemu/roms/seabios/src/std/LegacyBios.h new file mode 100644 index 000000000..5170c3786 --- /dev/null +++ b/qemu/roms/seabios/src/std/LegacyBios.h @@ -0,0 +1,985 @@ +/** @file + The EFI Legacy BIOS Protocol is used to abstract legacy Option ROM usage + under EFI and Legacy OS boot. This file also includes all the related + COMPATIBILIY16 structures and defintions. + + Note: The names for EFI_IA32_REGISTER_SET elements were picked to follow + well known naming conventions. + + Thunk is the code that switches from 32-bit protected environment into the 16-bit real-mode + environment. Reverse thunk is the code that does the opposite. + +Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + This protocol is defined in Framework for EFI Compatibility Support Module spec + Version 0.97. + +**/ + +#ifndef _EFI_LEGACY_BIOS_H_ +#define _EFI_LEGACY_BIOS_H_ + +/// +/// +/// +#pragma pack(1) + +typedef UINT8 SERIAL_MODE; +typedef UINT8 PARALLEL_MODE; + +#define EFI_COMPATIBILITY16_TABLE_SIGNATURE SIGNATURE_32 ('I', 'F', 'E', '$') + +/// +/// There is a table located within the traditional BIOS in either the 0xF000:xxxx or 0xE000:xxxx +/// physical address range. It is located on a 16-byte boundary and provides the physical address of the +/// entry point for the Compatibility16 functions. These functions provide the platform-specific +/// information that is required by the generic EfiCompatibility code. The functions are invoked via +/// thunking by using EFI_LEGACY_BIOS_PROTOCOL.FarCall86() with the 32-bit physical +/// entry point. +/// +typedef struct { + /// + /// The string "$EFI" denotes the start of the EfiCompatibility table. Byte 0 is "I," byte + /// 1 is "F," byte 2 is "E," and byte 3 is "$" and is normally accessed as a DWORD or UINT32. + /// + UINT32 Signature; + + /// + /// The value required such that byte checksum of TableLength equals zero. + /// + UINT8 TableChecksum; + + /// + /// The length of this table. + /// + UINT8 TableLength; + + /// + /// The major EFI revision for which this table was generated. + /// + UINT8 EfiMajorRevision; + + /// + /// The minor EFI revision for which this table was generated. + /// + UINT8 EfiMinorRevision; + + /// + /// The major revision of this table. + /// + UINT8 TableMajorRevision; + + /// + /// The minor revision of this table. + /// + UINT8 TableMinorRevision; + + /// + /// Reserved for future usage. + /// + UINT16 Reserved; + + /// + /// The segment of the entry point within the traditional BIOS for Compatibility16 functions. + /// + UINT16 Compatibility16CallSegment; + + /// + /// The offset of the entry point within the traditional BIOS for Compatibility16 functions. + /// + UINT16 Compatibility16CallOffset; + + /// + /// The segment of the entry point within the traditional BIOS for EfiCompatibility + /// to invoke the PnP installation check. + /// + UINT16 PnPInstallationCheckSegment; + + /// + /// The Offset of the entry point within the traditional BIOS for EfiCompatibility + /// to invoke the PnP installation check. + /// + UINT16 PnPInstallationCheckOffset; + + /// + /// EFI system resources table. Type EFI_SYSTEM_TABLE is defined in the IntelPlatform + ///Innovation Framework for EFI Driver Execution Environment Core Interface Specification (DXE CIS). + /// + UINT32 EfiSystemTable; + + /// + /// The address of an OEM-provided identifier string. The string is null terminated. + /// + UINT32 OemIdStringPointer; + + /// + /// The 32-bit physical address where ACPI RSD PTR is stored within the traditional + /// BIOS. The remained of the ACPI tables are located at their EFI addresses. The size + /// reserved is the maximum for ACPI 2.0. The EfiCompatibility will fill in the ACPI + /// RSD PTR with either the ACPI 1.0b or 2.0 values. + /// + UINT32 AcpiRsdPtrPointer; + + /// + /// The OEM revision number. Usage is undefined but provided for OEM module usage. + /// + UINT16 OemRevision; + + /// + /// The 32-bit physical address where INT15 E820 data is stored within the traditional + /// BIOS. The EfiCompatibility code will fill in the E820Pointer value and copy the + /// data to the indicated area. + /// + UINT32 E820Pointer; + + /// + /// The length of the E820 data and is filled in by the EfiCompatibility code. + /// + UINT32 E820Length; + + /// + /// The 32-bit physical address where the $PIR table is stored in the traditional BIOS. + /// The EfiCompatibility code will fill in the IrqRoutingTablePointer value and + /// copy the data to the indicated area. + /// + UINT32 IrqRoutingTablePointer; + + /// + /// The length of the $PIR table and is filled in by the EfiCompatibility code. + /// + UINT32 IrqRoutingTableLength; + + /// + /// The 32-bit physical address where the MP table is stored in the traditional BIOS. + /// The EfiCompatibility code will fill in the MpTablePtr value and copy the data + /// to the indicated area. + /// + UINT32 MpTablePtr; + + /// + /// The length of the MP table and is filled in by the EfiCompatibility code. + /// + UINT32 MpTableLength; + + /// + /// The segment of the OEM-specific INT table/code. + /// + UINT16 OemIntSegment; + + /// + /// The offset of the OEM-specific INT table/code. + /// + UINT16 OemIntOffset; + + /// + /// The segment of the OEM-specific 32-bit table/code. + /// + UINT16 Oem32Segment; + + /// + /// The offset of the OEM-specific 32-bit table/code. + /// + UINT16 Oem32Offset; + + /// + /// The segment of the OEM-specific 16-bit table/code. + /// + UINT16 Oem16Segment; + + /// + /// The offset of the OEM-specific 16-bit table/code. + /// + UINT16 Oem16Offset; + + /// + /// The segment of the TPM binary passed to 16-bit CSM. + /// + UINT16 TpmSegment; + + /// + /// The offset of the TPM binary passed to 16-bit CSM. + /// + UINT16 TpmOffset; + + /// + /// A pointer to a string identifying the independent BIOS vendor. + /// + UINT32 IbvPointer; + + /// + /// This field is NULL for all systems not supporting PCI Express. This field is the base + /// value of the start of the PCI Express memory-mapped configuration registers and + /// must be filled in prior to EfiCompatibility code issuing the Compatibility16 function + /// Compatibility16InitializeYourself(). + /// Compatibility16InitializeYourself() is defined in Compatability16 + /// Functions. + /// + UINT32 PciExpressBase; + + /// + /// Maximum PCI bus number assigned. + /// + UINT8 LastPciBus; + + /// + /// Start address of UMB RAM + /// + UINT32 UmaAddress; + + /// + /// Size of UMB RAM + /// + UINT32 UmaSize; + + /// + /// Start address of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemoryAddress; + + /// + /// Size of persistent allocation in high (>1MiB) memory + /// + UINT32 HiPermanentMemorySize; +} EFI_COMPATIBILITY16_TABLE; + +/// +/// Functions provided by the CSM binary which communicate between the EfiCompatibility +/// and Compatability16 code. +/// +/// Inconsistent with the specification here: +/// The member's name started with "Compatibility16" [defined in Intel Framework +/// Compatibility Support Module Specification / 0.97 version] +/// has been changed to "Legacy16" since keeping backward compatible. +/// +typedef enum { + /// + /// Causes the Compatibility16 code to do any internal initialization required. + /// Input: + /// AX = Compatibility16InitializeYourself + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_INIT_TABLE + /// Return: + /// AX = Return Status codes + /// + Legacy16InitializeYourself = 0x0000, + + /// + /// Causes the Compatibility16 BIOS to perform any drive number translations to match the boot sequence. + /// Input: + /// AX = Compatibility16UpdateBbs + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE + /// Return: + /// AX = Returned status codes + /// + Legacy16UpdateBbs = 0x0001, + + /// + /// Allows the Compatibility16 code to perform any final actions before booting. The Compatibility16 + /// code is read/write. + /// Input: + /// AX = Compatibility16PrepareToBoot + /// ES:BX = Pointer to EFI_TO_COMPATIBILITY16_BOOT_TABLE structure + /// Return: + /// AX = Returned status codes + /// + Legacy16PrepareToBoot = 0x0002, + + /// + /// Causes the Compatibility16 BIOS to boot. The Compatibility16 code is Read/Only. + /// Input: + /// AX = Compatibility16Boot + /// Output: + /// AX = Returned status codes + /// + Legacy16Boot = 0x0003, + + /// + /// Allows the Compatibility16 code to get the last device from which a boot was attempted. This is + /// stored in CMOS and is the priority number of the last attempted boot device. + /// Input: + /// AX = Compatibility16RetrieveLastBootDevice + /// Output: + /// AX = Returned status codes + /// BX = Priority number of the boot device. + /// + Legacy16RetrieveLastBootDevice = 0x0004, + + /// + /// Allows the Compatibility16 code rehook INT13, INT18, and/or INT19 after dispatching a legacy OpROM. + /// Input: + /// AX = Compatibility16DispatchOprom + /// ES:BX = Pointer to EFI_DISPATCH_OPROM_TABLE + /// Output: + /// AX = Returned status codes + /// BX = Number of non-BBS-compliant devices found. Equals 0 if BBS compliant. + /// + Legacy16DispatchOprom = 0x0005, + + /// + /// Finds a free area in the 0xFxxxx or 0xExxxx region of the specified length and returns the address + /// of that region. + /// Input: + /// AX = Compatibility16GetTableAddress + /// BX = Allocation region + /// 00 = Allocate from either 0xE0000 or 0xF0000 64 KB blocks. + /// Bit 0 = 1 Allocate from 0xF0000 64 KB block + /// Bit 1 = 1 Allocate from 0xE0000 64 KB block + /// CX = Requested length in bytes. + /// DX = Required address alignment. Bit mapped. First non-zero bit from the right is the alignment. + /// Output: + /// AX = Returned status codes + /// DS:BX = Address of the region + /// + Legacy16GetTableAddress = 0x0006, + + /// + /// Enables the EfiCompatibility module to do any nonstandard processing of keyboard LEDs or state. + /// Input: + /// AX = Compatibility16SetKeyboardLeds + /// CL = LED status. + /// Bit 0 Scroll Lock 0 = Off + /// Bit 1 NumLock + /// Bit 2 Caps Lock + /// Output: + /// AX = Returned status codes + /// + Legacy16SetKeyboardLeds = 0x0007, + + /// + /// Enables the EfiCompatibility module to install an interrupt handler for PCI mass media devices that + /// do not have an OpROM associated with them. An example is SATA. + /// Input: + /// AX = Compatibility16InstallPciHandler + /// ES:BX = Pointer to EFI_LEGACY_INSTALL_PCI_HANDLER structure + /// Output: + /// AX = Returned status codes + /// + Legacy16InstallPciHandler = 0x0008 +} EFI_COMPATIBILITY_FUNCTIONS; + + +/// +/// EFI_DISPATCH_OPROM_TABLE +/// +typedef struct { + UINT16 PnPInstallationCheckSegment; ///< A pointer to the PnpInstallationCheck data structure. + UINT16 PnPInstallationCheckOffset; ///< A pointer to the PnpInstallationCheck data structure. + UINT16 OpromSegment; ///< The segment where the OpROM was placed. Offset is assumed to be 3. + UINT8 PciBus; ///< The PCI bus. + UINT8 PciDeviceFunction; ///< The PCI device * 0x08 | PCI function. + UINT8 NumberBbsEntries; ///< The number of valid BBS table entries upon entry and exit. The IBV code may + ///< increase this number, if BBS-compliant devices also hook INTs in order to force the + ///< OpROM BIOS Setup to be executed. + UINT32 BbsTablePointer; ///< A pointer to the BBS table. + UINT16 RuntimeSegment; ///< The segment where the OpROM can be relocated to. If this value is 0x0000, this + ///< means that the relocation of this run time code is not supported. + ///< Inconsistent with specification here: + ///< The member's name "OpromDestinationSegment" [defined in Intel Framework Compatibility Support Module Specification / 0.97 version] + ///< has been changed to "RuntimeSegment" since keeping backward compatible. + +} EFI_DISPATCH_OPROM_TABLE; + +/// +/// EFI_TO_COMPATIBILITY16_INIT_TABLE +/// +typedef struct { + /// + /// Starting address of memory under 1 MB. The ending address is assumed to be 640 KB or 0x9FFFF. + /// + UINT32 BiosLessThan1MB; + + /// + /// The starting address of the high memory block. + /// + UINT32 HiPmmMemory; + + /// + /// The length of high memory block. + /// + UINT32 HiPmmMemorySizeInBytes; + + /// + /// The segment of the reverse thunk call code. + /// + UINT16 ReverseThunkCallSegment; + + /// + /// The offset of the reverse thunk call code. + /// + UINT16 ReverseThunkCallOffset; + + /// + /// The number of E820 entries copied to the Compatibility16 BIOS. + /// + UINT32 NumberE820Entries; + + /// + /// The amount of usable memory above 1 MB, e.g., E820 type 1 memory. + /// + UINT32 OsMemoryAbove1Mb; + + /// + /// The start of thunk code in main memory. Memory cannot be used by BIOS or PMM. + /// + UINT32 ThunkStart; + + /// + /// The size of the thunk code. + /// + UINT32 ThunkSizeInBytes; + + /// + /// Starting address of memory under 1 MB. + /// + UINT32 LowPmmMemory; + + /// + /// The length of low Memory block. + /// + UINT32 LowPmmMemorySizeInBytes; +} EFI_TO_COMPATIBILITY16_INIT_TABLE; + +/// +/// DEVICE_PRODUCER_SERIAL. +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the serial port. + UINT8 Irq; ///< IRQ assigned to the serial port. + SERIAL_MODE Mode; ///< Mode of serial port. Values are defined below. +} DEVICE_PRODUCER_SERIAL; + +/// +/// DEVICE_PRODUCER_SERIAL's modes. +///@{ +#define DEVICE_SERIAL_MODE_NORMAL 0x00 +#define DEVICE_SERIAL_MODE_IRDA 0x01 +#define DEVICE_SERIAL_MODE_ASK_IR 0x02 +#define DEVICE_SERIAL_MODE_DUPLEX_HALF 0x00 +#define DEVICE_SERIAL_MODE_DUPLEX_FULL 0x10 +///@) + +/// +/// DEVICE_PRODUCER_PARALLEL. +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the parallel port. + UINT8 Irq; ///< IRQ assigned to the parallel port. + UINT8 Dma; ///< DMA assigned to the parallel port. + PARALLEL_MODE Mode; ///< Mode of the parallel port. Values are defined below. +} DEVICE_PRODUCER_PARALLEL; + +/// +/// DEVICE_PRODUCER_PARALLEL's modes. +///@{ +#define DEVICE_PARALLEL_MODE_MODE_OUTPUT_ONLY 0x00 +#define DEVICE_PARALLEL_MODE_MODE_BIDIRECTIONAL 0x01 +#define DEVICE_PARALLEL_MODE_MODE_EPP 0x02 +#define DEVICE_PARALLEL_MODE_MODE_ECP 0x03 +///@} + +/// +/// DEVICE_PRODUCER_FLOPPY +/// +typedef struct { + UINT16 Address; ///< I/O address assigned to the floppy. + UINT8 Irq; ///< IRQ assigned to the floppy. + UINT8 Dma; ///< DMA assigned to the floppy. + UINT8 NumberOfFloppy; ///< Number of floppies in the system. +} DEVICE_PRODUCER_FLOPPY; + +/// +/// LEGACY_DEVICE_FLAGS +/// +typedef struct { + UINT32 A20Kybd : 1; ///< A20 controller by keyboard controller. + UINT32 A20Port90 : 1; ///< A20 controlled by port 0x92. + UINT32 Reserved : 30; ///< Reserved for future usage. +} LEGACY_DEVICE_FLAGS; + +/// +/// DEVICE_PRODUCER_DATA_HEADER +/// +typedef struct { + DEVICE_PRODUCER_SERIAL Serial[4]; ///< Data for serial port x. Type DEVICE_PRODUCER_SERIAL is defined below. + DEVICE_PRODUCER_PARALLEL Parallel[3]; ///< Data for parallel port x. Type DEVICE_PRODUCER_PARALLEL is defined below. + DEVICE_PRODUCER_FLOPPY Floppy; ///< Data for floppy. Type DEVICE_PRODUCER_FLOPPY is defined below. + UINT8 MousePresent; ///< Flag to indicate if mouse is present. + LEGACY_DEVICE_FLAGS Flags; ///< Miscellaneous Boolean state information passed to CSM. +} DEVICE_PRODUCER_DATA_HEADER; + +/// +/// ATAPI_IDENTIFY +/// +typedef struct { + UINT16 Raw[256]; ///< Raw data from the IDE IdentifyDrive command. +} ATAPI_IDENTIFY; + +/// +/// HDD_INFO +/// +typedef struct { + /// + /// Status of IDE device. Values are defined below. There is one HDD_INFO structure + /// per IDE controller. The IdentifyDrive is per drive. Index 0 is master and index + /// 1 is slave. + /// + UINT16 Status; + + /// + /// PCI bus of IDE controller. + /// + UINT32 Bus; + + /// + /// PCI device of IDE controller. + /// + UINT32 Device; + + /// + /// PCI function of IDE controller. + /// + UINT32 Function; + + /// + /// Command ports base address. + /// + UINT16 CommandBaseAddress; + + /// + /// Control ports base address. + /// + UINT16 ControlBaseAddress; + + /// + /// Bus master address. + /// + UINT16 BusMasterAddress; + + UINT8 HddIrq; + + /// + /// Data that identifies the drive data; one per possible attached drive. + /// + ATAPI_IDENTIFY IdentifyDrive[2]; +} HDD_INFO; + +/// +/// HDD_INFO status bits +/// +#define HDD_PRIMARY 0x01 +#define HDD_SECONDARY 0x02 +#define HDD_MASTER_ATAPI_CDROM 0x04 +#define HDD_SLAVE_ATAPI_CDROM 0x08 +#define HDD_MASTER_IDE 0x20 +#define HDD_SLAVE_IDE 0x40 +#define HDD_MASTER_ATAPI_ZIPDISK 0x10 +#define HDD_SLAVE_ATAPI_ZIPDISK 0x80 + +/// +/// BBS_STATUS_FLAGS;\. +/// +typedef struct { + UINT16 OldPosition : 4; ///< Prior priority. + UINT16 Reserved1 : 4; ///< Reserved for future use. + UINT16 Enabled : 1; ///< If 0, ignore this entry. + UINT16 Failed : 1; ///< 0 = Not known if boot failure occurred. + ///< 1 = Boot attempted failed. + + /// + /// State of media present. + /// 00 = No bootable media is present in the device. + /// 01 = Unknown if a bootable media present. + /// 10 = Media is present and appears bootable. + /// 11 = Reserved. + /// + UINT16 MediaPresent : 2; + UINT16 Reserved2 : 4; ///< Reserved for future use. +} BBS_STATUS_FLAGS; + +/// +/// BBS_TABLE, device type values & boot priority values. +/// +typedef struct { + /// + /// The boot priority for this boot device. Values are defined below. + /// + UINT16 BootPriority; + + /// + /// The PCI bus for this boot device. + /// + UINT32 Bus; + + /// + /// The PCI device for this boot device. + /// + UINT32 Device; + + /// + /// The PCI function for the boot device. + /// + UINT32 Function; + + /// + /// The PCI class for this boot device. + /// + UINT8 Class; + + /// + /// The PCI Subclass for this boot device. + /// + UINT8 SubClass; + + /// + /// Segment:offset address of an ASCIIZ description string describing the manufacturer. + /// + UINT16 MfgStringOffset; + + /// + /// Segment:offset address of an ASCIIZ description string describing the manufacturer. + /// + UINT16 MfgStringSegment; + + /// + /// BBS device type. BBS device types are defined below. + /// + UINT16 DeviceType; + + /// + /// Status of this boot device. Type BBS_STATUS_FLAGS is defined below. + /// + BBS_STATUS_FLAGS StatusFlags; + + /// + /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for + /// BCV devices. + /// + UINT16 BootHandlerOffset; + + /// + /// Segment:Offset address of boot loader for IPL devices or install INT13 handler for + /// BCV devices. + /// + UINT16 BootHandlerSegment; + + /// + /// Segment:offset address of an ASCIIZ description string describing this device. + /// + UINT16 DescStringOffset; + + /// + /// Segment:offset address of an ASCIIZ description string describing this device. + /// + UINT16 DescStringSegment; + + /// + /// Reserved. + /// + UINT32 InitPerReserved; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq13Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq18Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq19Handler; + + /// + /// The use of these fields is IBV dependent. They can be used to flag that an OpROM + /// has hooked the specified IRQ. The OpROM may be BBS compliant as some SCSI + /// BBS-compliant OpROMs also hook IRQ vectors in order to run their BIOS Setup + /// + UINT32 AdditionalIrq40Handler; + UINT8 AssignedDriveNumber; + UINT32 AdditionalIrq41Handler; + UINT32 AdditionalIrq46Handler; + UINT32 IBV1; + UINT32 IBV2; +} BBS_TABLE; + +/// +/// BBS device type values +///@{ +#define BBS_FLOPPY 0x01 +#define BBS_HARDDISK 0x02 +#define BBS_CDROM 0x03 +#define BBS_PCMCIA 0x04 +#define BBS_USB 0x05 +#define BBS_EMBED_NETWORK 0x06 +#define BBS_BEV_DEVICE 0x80 +#define BBS_UNKNOWN 0xff +///@} + +/// +/// BBS boot priority values +///@{ +#define BBS_DO_NOT_BOOT_FROM 0xFFFC +#define BBS_LOWEST_PRIORITY 0xFFFD +#define BBS_UNPRIORITIZED_ENTRY 0xFFFE +#define BBS_IGNORE_ENTRY 0xFFFF +///@} + +/// +/// SMM_ATTRIBUTES +/// +typedef struct { + /// + /// Access mechanism used to generate the soft SMI. Defined types are below. The other + /// values are reserved for future usage. + /// + UINT16 Type : 3; + + /// + /// The size of "port" in bits. Defined values are below. + /// + UINT16 PortGranularity : 3; + + /// + /// The size of data in bits. Defined values are below. + /// + UINT16 DataGranularity : 3; + + /// + /// Reserved for future use. + /// + UINT16 Reserved : 7; +} SMM_ATTRIBUTES; + +/// +/// SMM_ATTRIBUTES type values. +///@{ +#define STANDARD_IO 0x00 +#define STANDARD_MEMORY 0x01 +///@} + +/// +/// SMM_ATTRIBUTES port size constants. +///@{ +#define PORT_SIZE_8 0x00 +#define PORT_SIZE_16 0x01 +#define PORT_SIZE_32 0x02 +#define PORT_SIZE_64 0x03 +///@} + +/// +/// SMM_ATTRIBUTES data size constants. +///@{ +#define DATA_SIZE_8 0x00 +#define DATA_SIZE_16 0x01 +#define DATA_SIZE_32 0x02 +#define DATA_SIZE_64 0x03 +///@} + +/// +/// SMM_FUNCTION & relating constants. +/// +typedef struct { + UINT16 Function : 15; + UINT16 Owner : 1; +} SMM_FUNCTION; + +/// +/// SMM_FUNCTION Function constants. +///@{ +#define INT15_D042 0x0000 +#define GET_USB_BOOT_INFO 0x0001 +#define DMI_PNP_50_57 0x0002 +///@} + +/// +/// SMM_FUNCTION Owner constants. +///@{ +#define STANDARD_OWNER 0x0 +#define OEM_OWNER 0x1 +///@} + +/// +/// This structure assumes both port and data sizes are 1. SmmAttribute must be +/// properly to reflect that assumption. +/// +typedef struct { + /// + /// Describes the access mechanism, SmmPort, and SmmData sizes. Type + /// SMM_ATTRIBUTES is defined below. + /// + SMM_ATTRIBUTES SmmAttributes; + + /// + /// Function Soft SMI is to perform. Type SMM_FUNCTION is defined below. + /// + SMM_FUNCTION SmmFunction; + + /// + /// SmmPort size depends upon SmmAttributes and ranges from2 bytes to 16 bytes. + /// + UINT8 SmmPort; + + /// + /// SmmData size depends upon SmmAttributes and ranges from2 bytes to 16 bytes. + /// + UINT8 SmmData; +} SMM_ENTRY; + +/// +/// SMM_TABLE +/// +typedef struct { + UINT16 NumSmmEntries; ///< Number of entries represented by SmmEntry. + SMM_ENTRY SmmEntry; ///< One entry per function. Type SMM_ENTRY is defined below. +} SMM_TABLE; + +/// +/// UDC_ATTRIBUTES +/// +typedef struct { + /// + /// This bit set indicates that the ServiceAreaData is valid. + /// + UINT8 DirectoryServiceValidity : 1; + + /// + /// This bit set indicates to use the Reserve Area Boot Code Address (RACBA) only if + /// DirectoryServiceValidity is 0. + /// + UINT8 RabcaUsedFlag : 1; + + /// + /// This bit set indicates to execute hard disk diagnostics. + /// + UINT8 ExecuteHddDiagnosticsFlag : 1; + + /// + /// Reserved for future use. Set to 0. + /// + UINT8 Reserved : 5; +} UDC_ATTRIBUTES; + +/// +/// UD_TABLE +/// +typedef struct { + /// + /// This field contains the bit-mapped attributes of the PARTIES information. Type + /// UDC_ATTRIBUTES is defined below. + /// + UDC_ATTRIBUTES Attributes; + + /// + /// This field contains the zero-based device on which the selected + /// ServiceDataArea is present. It is 0 for master and 1 for the slave device. + /// + UINT8 DeviceNumber; + + /// + /// This field contains the zero-based index into the BbsTable for the parent device. + /// This index allows the user to reference the parent device information such as PCI + /// bus, device function. + /// + UINT8 BbsTableEntryNumberForParentDevice; + + /// + /// This field contains the zero-based index into the BbsTable for the boot entry. + /// + UINT8 BbsTableEntryNumberForBoot; + + /// + /// This field contains the zero-based index into the BbsTable for the HDD diagnostics entry. + /// + UINT8 BbsTableEntryNumberForHddDiag; + + /// + /// The raw Beer data. + /// + UINT8 BeerData[128]; + + /// + /// The raw data of selected service area. + /// + UINT8 ServiceAreaData[64]; +} UD_TABLE; + +#define EFI_TO_LEGACY_MAJOR_VERSION 0x02 +#define EFI_TO_LEGACY_MINOR_VERSION 0x00 +#define MAX_IDE_CONTROLLER 8 + +/// +/// EFI_TO_COMPATIBILITY16_BOOT_TABLE +/// +typedef struct { + UINT16 MajorVersion; ///< The EfiCompatibility major version number. + UINT16 MinorVersion; ///< The EfiCompatibility minor version number. + UINT32 AcpiTable; ///< The location of the RSDT ACPI table. < 4G range. + UINT32 SmbiosTable; ///< The location of the SMBIOS table in EFI memory. < 4G range. + UINT32 SmbiosTableLength; + // + // Legacy SIO state + // + DEVICE_PRODUCER_DATA_HEADER SioData; ///< Standard traditional device information. + UINT16 DevicePathType; ///< The default boot type. + UINT16 PciIrqMask; ///< Mask of which IRQs have been assigned to PCI. + UINT32 NumberE820Entries; ///< Number of E820 entries. The number can change from the + ///< Compatibility16InitializeYourself() function. + // + // Controller & Drive Identify[2] per controller information + // + HDD_INFO HddInfo[MAX_IDE_CONTROLLER]; ///< Hard disk drive information, including raw Identify Drive data. + UINT32 NumberBbsEntries; ///< Number of entries in the BBS table + UINT32 BbsTable; ///< A pointer to the BBS table. Type BBS_TABLE is defined below. + UINT32 SmmTable; ///< A pointer to the SMM table. Type SMM_TABLE is defined below. + UINT32 OsMemoryAbove1Mb; ///< The amount of usable memory above 1 MB, i.e. E820 type 1 memory. This value can + ///< differ from the value in EFI_TO_COMPATIBILITY16_INIT_TABLE as more + ///< memory may have been discovered. + UINT32 UnconventionalDeviceTable; ///< Information to boot off an unconventional device like a PARTIES partition. Type + ///< UD_TABLE is defined below. +} EFI_TO_COMPATIBILITY16_BOOT_TABLE; + +/// +/// EFI_LEGACY_INSTALL_PCI_HANDLER +/// +typedef struct { + UINT8 PciBus; ///< The PCI bus of the device. + UINT8 PciDeviceFun; ///< The PCI device in bits 7:3 and function in bits 2:0. + UINT8 PciSegment; ///< The PCI segment of the device. + UINT8 PciClass; ///< The PCI class code of the device. + UINT8 PciSubclass; ///< The PCI subclass code of the device. + UINT8 PciInterface; ///< The PCI interface code of the device. + // + // Primary section + // + UINT8 PrimaryIrq; ///< The primary device IRQ. + UINT8 PrimaryReserved; ///< Reserved. + UINT16 PrimaryControl; ///< The primary device control I/O base. + UINT16 PrimaryBase; ///< The primary device I/O base. + UINT16 PrimaryBusMaster; ///< The primary device bus master I/O base. + // + // Secondary Section + // + UINT8 SecondaryIrq; ///< The secondary device IRQ. + UINT8 SecondaryReserved; ///< Reserved. + UINT16 SecondaryControl; ///< The secondary device control I/O base. + UINT16 SecondaryBase; ///< The secondary device I/O base. + UINT16 SecondaryBusMaster; ///< The secondary device bus master I/O base. +} EFI_LEGACY_INSTALL_PCI_HANDLER; + +#endif diff --git a/qemu/roms/seabios/src/std/acpi.h b/qemu/roms/seabios/src/std/acpi.h new file mode 100644 index 000000000..e0d9516ba --- /dev/null +++ b/qemu/roms/seabios/src/std/acpi.h @@ -0,0 +1,297 @@ +#ifndef __ACPI_H +#define __ACPI_H + +#include "types.h" // u32 + +/* + * ACPI 2.0 Generic Address Space definition. + */ +struct acpi_20_generic_address { + u8 address_space_id; + u8 register_bit_width; + u8 register_bit_offset; + u8 reserved; + u64 address; +} PACKED; + +#define RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR " + +struct rsdp_descriptor { /* Root System Descriptor Pointer */ + u64 signature; /* ACPI signature, contains "RSD PTR " */ + u8 checksum; /* To make sum of struct == 0 */ + u8 oem_id [6]; /* OEM identification */ + u8 revision; /* Must be 0 for 1.0, 2 for 2.0 */ + u32 rsdt_physical_address; /* 32-bit physical address of RSDT */ + u32 length; /* XSDT Length in bytes including hdr */ + u64 xsdt_physical_address; /* 64-bit physical address of XSDT */ + u8 extended_checksum; /* Checksum of entire table */ + u8 reserved [3]; /* Reserved field must be 0 */ +}; + +/* Table structure from Linux kernel (the ACPI tables are under the + BSD license) */ + +#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ + u32 signature; /* ACPI signature (4 ASCII characters) */ \ + u32 length; /* Length of table, in bytes, including header */ \ + u8 revision; /* ACPI Specification minor version # */ \ + u8 checksum; /* To make sum of entire table == 0 */ \ + u8 oem_id [6]; /* OEM identification */ \ + u8 oem_table_id [8]; /* OEM table identification */ \ + u32 oem_revision; /* OEM revision number */ \ + u8 asl_compiler_id [4]; /* ASL compiler vendor ID */ \ + u32 asl_compiler_revision; /* ASL compiler revision number */ + +/* + * Fixed ACPI Description Table Fixed Feature Flags + */ +#define ACPI_FADT_F_WBINVD (1 << 0) +#define ACPI_FADT_F_WBINVD_FLUSH (1 << 1) +#define ACPI_FADT_F_PROC_C1 (1 << 2) +#define ACPI_FADT_F_P_LVL2_UP (1 << 3) +#define ACPI_FADT_F_PWR_BUTTON (1 << 4) +#define ACPI_FADT_F_SLP_BUTTON (1 << 5) +#define ACPI_FADT_F_FIX_RTC (1 << 6) +#define ACPI_FADT_F_RTC_S4 (1 << 7) +#define ACPI_FADT_F_TMR_VAL_EXT (1 << 8) +#define ACPI_FADT_F_DCK_CAP (1 << 9) +#define ACPI_FADT_F_RESET_REG_SUP (1 << 10) +#define ACPI_FADT_F_SEALED_CASE (1 << 11) +#define ACPI_FADT_F_HEADLESS (1 << 12) +#define ACPI_FADT_F_CPU_SW_SLP (1 << 13) +#define ACPI_FADT_F_PCI_EXP_WAK (1 << 14) +#define ACPI_FADT_F_USE_PLATFORM_CLOCK (1 << 15) +#define ACPI_FADT_F_S4_RTC_STS_VALID (1 << 16) +#define ACPI_FADT_F_REMOTE_POWER_ON_CAPABLE (1 << 17) +#define ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL (1 << 18) +#define ACPI_FADT_F_FORCE_APIC_PHYSICAL_DESTINATION_MODE (1 << 19) +#define ACPI_FADT_F_HW_REDUCED_ACPI (1 << 20) +#define ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE (1 << 21) + +/* + * ACPI 1.0 Fixed ACPI Description Table (FADT) + */ +#define FACP_SIGNATURE 0x50434146 // FACP +struct fadt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 firmware_ctrl; /* Physical address of FACS */ + u32 dsdt; /* Physical address of DSDT */ + u8 model; /* System Interrupt Model */ + u8 reserved1; /* Reserved */ + u16 sci_int; /* System vector of SCI interrupt */ + u32 smi_cmd; /* Port address of SMI command port */ + u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */ + u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */ + u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ + u8 reserved2; /* Reserved - must be zero */ + u32 pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ + u32 pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ + u32 pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + u32 pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + u32 pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + u32 pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + u32 gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ + u32 gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ + u8 pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ + u8 pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ + u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + u8 pm_tmr_len; /* Byte Length of ports at pm_tm_blk */ + u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ + u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + u8 gpe1_base; /* Offset in gpe model where gpe1 events start */ + u8 reserved3; /* Reserved */ + u16 plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ + u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ + u16 flush_size; /* Size of area read to flush caches */ + u16 flush_stride; /* Stride used in flushing caches */ + u8 duty_offset; /* Bit location of duty cycle field in p_cnt reg */ + u8 duty_width; /* Bit width of duty cycle field in p_cnt reg */ + u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ + u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ + u8 century; /* Index to century in RTC CMOS RAM */ + u8 reserved4; /* Reserved */ + u8 reserved4a; /* Reserved */ + u8 reserved4b; /* Reserved */ + u32 flags; +} PACKED; + +struct acpi_table_header /* ACPI common table header */ +{ + ACPI_TABLE_HEADER_DEF +} PACKED; + +/* + * ACPI 1.0 Root System Description Table (RSDT) + */ +#define RSDT_SIGNATURE 0x54445352 // RSDT +struct rsdt_descriptor_rev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 table_offset_entry[0]; /* Array of pointers to other */ + /* ACPI tables */ +} PACKED; + +/* + * ACPI 1.0 Firmware ACPI Control Structure (FACS) + */ +#define FACS_SIGNATURE 0x53434146 // FACS +struct facs_descriptor_rev1 +{ + u32 signature; /* ACPI Signature */ + u32 length; /* Length of structure, in bytes */ + u32 hardware_signature; /* Hardware configuration signature */ + u32 firmware_waking_vector; /* ACPI OS waking vector */ + u32 global_lock; /* Global Lock */ + u32 flags; + u8 resverved3 [40]; /* Reserved - must be zero */ +} PACKED; + +/* + * Differentiated System Description Table (DSDT) + */ +#define DSDT_SIGNATURE 0x54445344 // DSDT + +/* + * MADT values and structures + */ + +/* Values for MADT PCATCompat */ + +#define DUAL_PIC 0 +#define MULTIPLE_APIC 1 + +/* Master MADT */ + +#define APIC_SIGNATURE 0x43495041 // APIC +struct multiple_apic_table +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 local_apic_address; /* Physical address of local APIC */ + u32 flags; +} PACKED; + +/* Values for Type in APIC sub-headers */ + +#define APIC_PROCESSOR 0 +#define APIC_IO 1 +#define APIC_XRUPT_OVERRIDE 2 +#define APIC_NMI 3 +#define APIC_LOCAL_NMI 4 +#define APIC_ADDRESS_OVERRIDE 5 +#define APIC_IO_SAPIC 6 +#define APIC_LOCAL_SAPIC 7 +#define APIC_XRUPT_SOURCE 8 +#define APIC_RESERVED 9 /* 9 and greater are reserved */ + +/* + * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) + */ +#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ + u8 type; \ + u8 length; + +/* Sub-structures for MADT */ + +struct madt_processor_apic +{ + ACPI_SUB_HEADER_DEF + u8 processor_id; /* ACPI processor id */ + u8 local_apic_id; /* Processor's local APIC id */ + u32 flags; +} PACKED; + +struct madt_io_apic +{ + ACPI_SUB_HEADER_DEF + u8 io_apic_id; /* I/O APIC ID */ + u8 reserved; /* Reserved - must be zero */ + u32 address; /* APIC physical address */ + u32 interrupt; /* Global system interrupt where INTI + * lines start */ +} PACKED; + +struct madt_intsrcovr { + ACPI_SUB_HEADER_DEF + u8 bus; + u8 source; + u32 gsi; + u16 flags; +} PACKED; + +struct madt_local_nmi { + ACPI_SUB_HEADER_DEF + u8 processor_id; /* ACPI processor id */ + u16 flags; /* MPS INTI flags */ + u8 lint; /* Local APIC LINT# */ +} PACKED; + +/* + * HPET Description Table + */ +#define HPET_SIGNATURE 0x54455048 // HPET +struct acpi_20_hpet { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + u32 timer_block_id; + struct acpi_20_generic_address addr; + u8 hpet_number; + u16 min_tick; + u8 page_protect; +} PACKED; + +/* + * SRAT (NUMA topology description) table + */ + +#define SRAT_SIGNATURE 0x54415253 // SRAT +struct system_resource_affinity_table +{ + ACPI_TABLE_HEADER_DEF + u32 reserved1; + u32 reserved2[2]; +} PACKED; + +#define SRAT_PROCESSOR 0 +#define SRAT_MEMORY 1 + +struct srat_processor_affinity +{ + ACPI_SUB_HEADER_DEF + u8 proximity_lo; + u8 local_apic_id; + u32 flags; + u8 local_sapic_eid; + u8 proximity_hi[3]; + u32 reserved; +} PACKED; + +struct srat_memory_affinity +{ + ACPI_SUB_HEADER_DEF + u8 proximity[4]; + u16 reserved1; + u64 base_addr; + u64 range_length; + u32 reserved2; + u32 flags; + u32 reserved3[2]; +} PACKED; + +/* PCI fw r3.0 MCFG table. */ +/* Subtable */ +struct acpi_mcfg_allocation { + u64 address; /* Base address, processor-relative */ + u16 pci_segment; /* PCI segment group number */ + u8 start_bus_number; /* Starting PCI Bus number */ + u8 end_bus_number; /* Final PCI Bus number */ + u32 reserved; +} PACKED; + +#define MCFG_SIGNATURE 0x4746434d // MCFG +struct acpi_table_mcfg { + ACPI_TABLE_HEADER_DEF; + u8 reserved[8]; + struct acpi_mcfg_allocation allocation[0]; +} PACKED; + +#endif // acpi.h diff --git a/qemu/roms/seabios/src/std/bda.h b/qemu/roms/seabios/src/std/bda.h new file mode 100644 index 000000000..c321266e2 --- /dev/null +++ b/qemu/roms/seabios/src/std/bda.h @@ -0,0 +1,154 @@ +// BIOS Data Area (and similar) definitions +#ifndef __BDA_H +#define __BDA_H + +#include "disk.h" // struct fdpt_s +#include "types.h" // u8 + + +/**************************************************************** + * Interupt vector table + ****************************************************************/ + +struct rmode_IVT { + struct segoff_s ivec[256]; +}; + + +/**************************************************************** + * Bios Data Area (BDA) + ****************************************************************/ + +struct bios_data_area_s { + // 40:00 + u16 port_com[4]; + u16 port_lpt[3]; + u16 ebda_seg; + // 40:10 + u16 equipment_list_flags; + u8 pad1; + u16 mem_size_kb; + u8 pad2; + u8 ps2_ctrl_flag; + u8 kbd_flag0; + u8 kbd_flag1; + u8 alt_keypad; + u16 kbd_buf_head; + u16 kbd_buf_tail; + // 40:1e + u8 kbd_buf[32]; + u8 floppy_recalibration_status; + u8 floppy_motor_status; + // 40:40 + u8 floppy_motor_counter; + u8 floppy_last_status; + u8 floppy_return_status[7]; + u8 video_mode; + u16 video_cols; + u16 video_pagesize; + u16 video_pagestart; + // 40:50 + u16 cursor_pos[8]; + // 40:60 + u16 cursor_type; + u8 video_page; + u16 crtc_address; + u8 video_msr; + u8 video_pal; + struct segoff_s jump; + u8 other_6b; + u32 timer_counter; + // 40:70 + u8 timer_rollover; + u8 break_flag; + u16 soft_reset_flag; + u8 disk_last_status; + u8 hdcount; + u8 disk_control_byte; + u8 port_disk; + u8 lpt_timeout[4]; + u8 com_timeout[4]; + // 40:80 + u16 kbd_buf_start_offset; + u16 kbd_buf_end_offset; + u8 video_rows; + u16 char_height; + u8 video_ctl; + u8 video_switches; + u8 modeset_ctl; + u8 dcc_index; + u8 floppy_last_data_rate; + u8 disk_status_controller; + u8 disk_error_controller; + u8 disk_interrupt_flag; + u8 floppy_harddisk_info; + // 40:90 + u8 floppy_media_state[4]; + u8 floppy_track[2]; + u8 kbd_flag2; + u8 kbd_led; + struct segoff_s user_wait_complete_flag; + u32 user_wait_timeout; + // 40:A0 + u8 rtc_wait_flag; + u8 other_a1[7]; + struct segoff_s video_savetable; + u8 other_ac[4]; + // 40:B0 + u8 other_b0[5*16]; +} PACKED; + +// BDA floppy_recalibration_status bitdefs +#define FRS_IRQ (1<<7) + +// BDA rtc_wait_flag bitdefs +#define RWS_WAIT_PENDING (1<<0) +#define RWS_WAIT_ELAPSED (1<<7) + +// BDA floppy_media_state bitdefs +#define FMS_DRIVE_STATE_MASK (0x07) +#define FMS_MEDIA_DRIVE_ESTABLISHED (1<<4) +#define FMS_DOUBLE_STEPPING (1<<5) +#define FMS_DATA_RATE_MASK (0xc0) + +// Limit of BDA timer_counter field +#define TICKS_PER_DAY 1573040 + + +/**************************************************************** + * Extended Bios Data Area (EBDA) + ****************************************************************/ + +struct extended_bios_data_area_s { + u8 size; + u8 reserved1[0x21]; + struct segoff_s far_call_pointer; + u8 mouse_flag1; + u8 mouse_flag2; + u8 mouse_data[0x08]; + // 0x30 + u8 other1[0x0d]; + + // 0x3d + struct fdpt_s fdpt[2]; + + // 0x5d + u8 other2[0xC4]; + + // 0x121 - Begin custom storage. +} PACKED; + + +/**************************************************************** + * Bios Config Table + ****************************************************************/ + +struct bios_config_table_s { + u16 size; + u8 model; + u8 submodel; + u8 biosrev; + u8 feature1, feature2, feature3, feature4, feature5; +} PACKED; + +#endif // bda.h diff --git a/qemu/roms/seabios/src/std/disk.h b/qemu/roms/seabios/src/std/disk.h new file mode 100644 index 000000000..639710899 --- /dev/null +++ b/qemu/roms/seabios/src/std/disk.h @@ -0,0 +1,175 @@ +// Standard disk BIOS definitions. +#ifndef __DISK_H +#define __DISK_H + +#include "types.h" // u8 + +#define DISK_RET_SUCCESS 0x00 +#define DISK_RET_EPARAM 0x01 +#define DISK_RET_EADDRNOTFOUND 0x02 +#define DISK_RET_EWRITEPROTECT 0x03 +#define DISK_RET_ECHANGED 0x06 +#define DISK_RET_EBOUNDARY 0x09 +#define DISK_RET_EBADTRACK 0x0c +#define DISK_RET_ECONTROLLER 0x20 +#define DISK_RET_ETIMEOUT 0x80 +#define DISK_RET_ENOTLOCKED 0xb0 +#define DISK_RET_ELOCKED 0xb1 +#define DISK_RET_ENOTREMOVABLE 0xb2 +#define DISK_RET_ETOOMANYLOCKS 0xb4 +#define DISK_RET_EMEDIA 0xC0 +#define DISK_RET_ENOTREADY 0xAA + + +/**************************************************************** + * Interface structs + ****************************************************************/ + +// Bios disk structures. +struct int13ext_s { + u8 size; + u8 reserved; + u16 count; + struct segoff_s data; + u64 lba; +} PACKED; + +// DPTE definition +struct dpte_s { + u16 iobase1; + u16 iobase2; + u8 prefix; + u8 unused; + u8 irq; + u8 blkcount; + u8 dma; + u8 pio; + u16 options; + u16 reserved; + u8 revision; + u8 checksum; +}; + +// Disk Physical Table definition +struct int13dpt_s { + u16 size; + u16 infos; + u32 cylinders; + u32 heads; + u32 spt; + u64 sector_count; + u16 blksize; + struct segoff_s dpte; + u16 key; + u8 dpi_length; + u8 reserved1; + u16 reserved2; + u8 host_bus[4]; + u8 iface_type[8]; + u64 iface_path; + union { + struct { + u64 device_path; + u8 reserved3; + u8 checksum; + } phoenix; + struct { + u64 device_path[2]; + u8 reserved3; + u8 checksum; + } t13; + }; +} PACKED; + +// Floppy info +struct fdpt_s { + u16 cylinders; + u8 heads; + u8 a0h_signature; + u8 phys_sectors; + u16 precompensation; + u8 reserved; + u8 drive_control_byte; + u16 phys_cylinders; + u8 phys_heads; + u16 landing_zone; + u8 sectors; + u8 checksum; +} PACKED; + +// Floppy "Disk Base Table" +struct floppy_dbt_s { + u8 specify1; + u8 specify2; + u8 shutoff_ticks; + u8 bps_code; + u8 sectors; + u8 interblock_len; + u8 data_len; + u8 gap_len; + u8 fill_byte; + u8 settle_time; + u8 startup_time; +} PACKED; + +struct floppy_ext_dbt_s { + struct floppy_dbt_s dbt; + // Extra fields + u8 max_track; + u8 data_rate; + u8 drive_type; +} PACKED; + + +/**************************************************************** + * Master boot record + ****************************************************************/ + +struct packed_chs_s { + u8 heads; + u8 sptcyl; + u8 cyllow; +} PACKED; + +struct partition_s { + u8 status; + struct packed_chs_s first; + u8 type; + struct packed_chs_s last; + u32 lba; + u32 count; +} PACKED; + +struct mbr_s { + u8 code[440]; + // 0x01b8 + u32 diskseg; + // 0x01bc + u16 null; + // 0x01be + struct partition_s partitions[4]; + // 0x01fe + u16 signature; +} PACKED; + +#define MBR_SIGNATURE 0xaa55 + + +/**************************************************************** + * ElTorito CDROM interface + ****************************************************************/ + +struct eltorito_s { + u8 size; + u8 media; + u8 emulated_drive; + u8 controller_index; + u32 ilba; + u16 device_spec; + u16 buffer_segment; + u16 load_segment; + u16 sector_count; + struct packed_chs_s chs; +} PACKED; + +#endif // disk.h diff --git a/qemu/roms/seabios/src/std/mptable.h b/qemu/roms/seabios/src/std/mptable.h new file mode 100644 index 000000000..fa6a22949 --- /dev/null +++ b/qemu/roms/seabios/src/std/mptable.h @@ -0,0 +1,77 @@ +#ifndef __MPTABLE_H +#define __MPTABLE_H + +#include "types.h" // u32 + +#define MPTABLE_SIGNATURE 0x5f504d5f // "_MP_" + +struct mptable_floating_s { + u32 signature; + u32 physaddr; + u8 length; + u8 spec_rev; + u8 checksum; + u8 feature1; + u8 feature2; + u8 reserved[3]; +}; + +#define MPCONFIG_SIGNATURE 0x504d4350 // "PCMP" + +struct mptable_config_s { + u32 signature; + u16 length; + u8 spec; + u8 checksum; + char oemid[8]; + char productid[12]; + u32 oemptr; + u16 oemsize; + u16 entrycount; + u32 lapic; + u16 exttable_length; + u8 exttable_checksum; + u8 reserved; +} PACKED; + +#define MPT_TYPE_CPU 0 +#define MPT_TYPE_BUS 1 +#define MPT_TYPE_IOAPIC 2 +#define MPT_TYPE_INTSRC 3 +#define MPT_TYPE_LOCAL_INT 4 + +struct mpt_cpu { + u8 type; + u8 apicid; + u8 apicver; + u8 cpuflag; + u32 cpusignature; + u32 featureflag; + u32 reserved[2]; +} PACKED; + +struct mpt_bus { + u8 type; + u8 busid; + char bustype[6]; +} PACKED; + +struct mpt_ioapic { + u8 type; + u8 apicid; + u8 apicver; + u8 flags; + u32 apicaddr; +} PACKED; + +struct mpt_intsrc { + u8 type; + u8 irqtype; + u16 irqflag; + u8 srcbus; + u8 srcbusirq; + u8 dstapic; + u8 dstirq; +} PACKED; + +#endif // mptable.h diff --git a/qemu/roms/seabios/src/std/optionrom.h b/qemu/roms/seabios/src/std/optionrom.h new file mode 100644 index 000000000..94ca4ae10 --- /dev/null +++ b/qemu/roms/seabios/src/std/optionrom.h @@ -0,0 +1,59 @@ +#ifndef __OPTIONROMS_H +#define __OPTIONROMS_H + +#include "types.h" // u32 + +#define OPTION_ROM_SIGNATURE 0xaa55 + +struct rom_header { + u16 signature; + u8 size; + u8 initVector[4]; + u8 reserved[17]; + u16 pcioffset; + u16 pnpoffset; +} PACKED; + +#define PCI_ROM_SIGNATURE 0x52494350 // "PCIR" + +struct pci_data { + u32 signature; + u16 vendor; + u16 device; + u16 vitaldata; + u16 dlen; + u8 drevision; + u8 class_lo; + u16 class_hi; + u16 ilen; + u16 irevision; + u8 type; + u8 indicator; + u16 reserved; +} PACKED; + +struct pnp_data { + u32 signature; + u8 revision; + u8 len; + u16 nextoffset; + u8 reserved_08; + u8 checksum; + u32 devid; + u16 manufacturer; + u16 productname; + u8 type_lo; + u16 type_hi; + u8 dev_flags; + u16 bcv; + u16 dv; + u16 bev; + u16 reserved_1c; + u16 staticresource; +} PACKED; + +#define OPTION_ROM_ALIGN 2048 +#define OPTION_ROM_INITVECTOR offsetof(struct rom_header, initVector[0]) +#define PCIROM_CODETYPE_X86 0 + +#endif diff --git a/qemu/roms/seabios/src/std/pirtable.h b/qemu/roms/seabios/src/std/pirtable.h new file mode 100644 index 000000000..9de3a4389 --- /dev/null +++ b/qemu/roms/seabios/src/std/pirtable.h @@ -0,0 +1,35 @@ +#ifndef __PIRTABLE_H +#define __PIRTABLE_H + +#include "types.h" // u32 + +struct link_info { + u8 link; + u16 bitmap; +} PACKED; + +struct pir_slot { + u8 bus; + u8 dev; + struct link_info links[4]; + u8 slot_nr; + u8 reserved; +} PACKED; + +struct pir_header { + u32 signature; + u16 version; + u16 size; + u8 router_bus; + u8 router_devfunc; + u16 exclusive_irqs; + u32 compatible_devid; + u32 miniport_data; + u8 reserved[11]; + u8 checksum; + struct pir_slot slots[0]; +} PACKED; + +#define PIR_SIGNATURE 0x52495024 // $PIR + +#endif // pirtable.h diff --git a/qemu/roms/seabios/src/std/pmm.h b/qemu/roms/seabios/src/std/pmm.h new file mode 100644 index 000000000..80027f38b --- /dev/null +++ b/qemu/roms/seabios/src/std/pmm.h @@ -0,0 +1,19 @@ +#ifndef __PMM_H +#define __PMM_H + +#include "types.h" // u32 + +#define PMM_SIGNATURE 0x4d4d5024 // $PMM + +struct pmmheader { + u32 signature; + u8 version; + u8 length; + u8 checksum; + struct segoff_s entry; + u8 reserved[5]; +} PACKED; + +#define PMM_FUNCTION_NOT_SUPPORTED 0xffffffff + +#endif // pmm.h diff --git a/qemu/roms/seabios/src/std/pnpbios.h b/qemu/roms/seabios/src/std/pnpbios.h new file mode 100644 index 000000000..0871e3ab5 --- /dev/null +++ b/qemu/roms/seabios/src/std/pnpbios.h @@ -0,0 +1,24 @@ +#ifndef __PNPHEADER_H +#define __PNPHEADER_H + +#define PNP_SIGNATURE 0x506e5024 // $PnP + +struct pnpheader { + u32 signature; + u8 version; + u8 length; + u16 control; + u8 checksum; + u32 eventloc; + u16 real_ip; + u16 real_cs; + u16 prot_ip; + u32 prot_base; + u32 oemid; + u16 real_ds; + u32 prot_database; +} PACKED; + +#define FUNCTION_NOT_SUPPORTED 0x82 + +#endif // pnpheader.h diff --git a/qemu/roms/seabios/src/std/smbios.h b/qemu/roms/seabios/src/std/smbios.h new file mode 100644 index 000000000..05137167a --- /dev/null +++ b/qemu/roms/seabios/src/std/smbios.h @@ -0,0 +1,165 @@ +#ifndef __SMBIOS_H +#define __SMBIOS_H + +#include "types.h" // u32 + +/* SMBIOS entry point -- must be written to a 16-bit aligned address + between 0xf0000 and 0xfffff. + */ +struct smbios_entry_point { + char anchor_string[4]; + u8 checksum; + u8 length; + u8 smbios_major_version; + u8 smbios_minor_version; + u16 max_structure_size; + u8 entry_point_revision; + u8 formatted_area[5]; + char intermediate_anchor_string[5]; + u8 intermediate_checksum; + u16 structure_table_length; + u32 structure_table_address; + u16 number_of_structures; + u8 smbios_bcd_revision; +} PACKED; + +/* This goes at the beginning of every SMBIOS structure. */ +struct smbios_structure_header { + u8 type; + u8 length; + u16 handle; +} PACKED; + +/* SMBIOS type 0 - BIOS Information */ +struct smbios_type_0 { + struct smbios_structure_header header; + u8 vendor_str; + u8 bios_version_str; + u16 bios_starting_address_segment; + u8 bios_release_date_str; + u8 bios_rom_size; + u8 bios_characteristics[8]; + u8 bios_characteristics_extension_bytes[2]; + u8 system_bios_major_release; + u8 system_bios_minor_release; + u8 embedded_controller_major_release; + u8 embedded_controller_minor_release; +} PACKED; + +/* SMBIOS type 1 - System Information */ +struct smbios_type_1 { + struct smbios_structure_header header; + u8 manufacturer_str; + u8 product_name_str; + u8 version_str; + u8 serial_number_str; + u8 uuid[16]; + u8 wake_up_type; + u8 sku_number_str; + u8 family_str; +} PACKED; + +/* SMBIOS type 3 - System Enclosure (v2.3) */ +struct smbios_type_3 { + struct smbios_structure_header header; + u8 manufacturer_str; + u8 type; + u8 version_str; + u8 serial_number_str; + u8 asset_tag_number_str; + u8 boot_up_state; + u8 power_supply_state; + u8 thermal_state; + u8 security_status; + u32 oem_defined; + u8 height; + u8 number_of_power_cords; + u8 contained_element_count; + // contained elements follow +} PACKED; + +/* SMBIOS type 4 - Processor Information (v2.0) */ +struct smbios_type_4 { + struct smbios_structure_header header; + u8 socket_designation_str; + u8 processor_type; + u8 processor_family; + u8 processor_manufacturer_str; + u32 processor_id[2]; + u8 processor_version_str; + u8 voltage; + u16 external_clock; + u16 max_speed; + u16 current_speed; + u8 status; + u8 processor_upgrade; + u16 l1_cache_handle; + u16 l2_cache_handle; + u16 l3_cache_handle; +} PACKED; + +/* SMBIOS type 16 - Physical Memory Array + * Associated with one type 17 (Memory Device). + */ +struct smbios_type_16 { + struct smbios_structure_header header; + u8 location; + u8 use; + u8 error_correction; + u32 maximum_capacity; + u16 memory_error_information_handle; + u16 number_of_memory_devices; +} PACKED; + +/* SMBIOS type 17 - Memory Device + * Associated with one type 19 + */ +struct smbios_type_17 { + struct smbios_structure_header header; + u16 physical_memory_array_handle; + u16 memory_error_information_handle; + u16 total_width; + u16 data_width; + u16 size; + u8 form_factor; + u8 device_set; + u8 device_locator_str; + u8 bank_locator_str; + u8 memory_type; + u16 type_detail; +} PACKED; + +/* SMBIOS type 19 - Memory Array Mapped Address */ +struct smbios_type_19 { + struct smbios_structure_header header; + u32 starting_address; + u32 ending_address; + u16 memory_array_handle; + u8 partition_width; +} PACKED; + +/* SMBIOS type 20 - Memory Device Mapped Address */ +struct smbios_type_20 { + struct smbios_structure_header header; + u32 starting_address; + u32 ending_address; + u16 memory_device_handle; + u16 memory_array_mapped_address_handle; + u8 partition_row_position; + u8 interleave_position; + u8 interleaved_data_depth; +} PACKED; + +/* SMBIOS type 32 - System Boot Information */ +struct smbios_type_32 { + struct smbios_structure_header header; + u8 reserved[6]; + u8 boot_status; +} PACKED; + +/* SMBIOS type 127 -- End-of-table */ +struct smbios_type_127 { + struct smbios_structure_header header; +} PACKED; + +#endif // smbios.h diff --git a/qemu/roms/seabios/src/std/vbe.h b/qemu/roms/seabios/src/std/vbe.h new file mode 100644 index 000000000..94b4ad86f --- /dev/null +++ b/qemu/roms/seabios/src/std/vbe.h @@ -0,0 +1,156 @@ +#ifndef __VBE_H +#define __VBE_H + +#include "types.h" // u8 + +#define VESA_SIGNATURE 0x41534556 // VESA +#define VBE2_SIGNATURE 0x32454256 // VBE2 + +struct vbe_info { + u32 signature; + u16 version; + struct segoff_s oem_string; + u32 capabilities; + struct segoff_s video_mode; + u16 total_memory; + u16 oem_revision; + struct segoff_s oem_vendor_string; + struct segoff_s oem_product_string; + struct segoff_s oem_revision_string; + u8 reserved[222]; +} PACKED; + +struct vbe_mode_info { + /* VBE */ + u16 mode_attributes; + u8 winA_attributes; + u8 winB_attributes; + u16 win_granularity; + u16 win_size; + u16 winA_seg; + u16 winB_seg; + struct segoff_s win_func_ptr; + u16 bytes_per_scanline; + /* VBE 1.2 */ + u16 xres; + u16 yres; + u8 xcharsize; + u8 ycharsize; + u8 planes; + u8 bits_per_pixel; + u8 banks; + u8 mem_model; + u8 bank_size; + u8 pages; + u8 reserved0; + /* Direct Color */ + u8 red_size; + u8 red_pos; + u8 green_size; + u8 green_pos; + u8 blue_size; + u8 blue_pos; + u8 alpha_size; + u8 alpha_pos; + u8 directcolor_info; + /* VBE 2.0 */ + u32 phys_base; + u32 reserved1; + u16 reserved2; + /* VBE 3.0 */ + u16 linear_bytes_per_scanline; + u8 bank_pages; + u8 linear_pages; + u8 linear_red_size; + u8 linear_red_pos; + u8 linear_green_size; + u8 linear_green_pos; + u8 linear_blue_size; + u8 linear_blue_pos; + u8 linear_alpha_size; + u8 linear_alpha_pos; + u32 pixclock_max; + u8 reserved[190]; +} PACKED; + +struct vbe_crtc_info { + u16 horiz_total; + u16 horiz_sync_start; + u16 horiz_sync_end; + u16 vert_total; + u16 vert_sync_start; + u16 vert_sync_end; + u8 flags; + u32 pixclock; + u16 refresh_rate; + u8 reserved[40]; +} PACKED; + +/* VBE Return Status Info */ +/* AL */ +#define VBE_RETURN_STATUS_SUPPORTED 0x4F +#define VBE_RETURN_STATUS_UNSUPPORTED 0x00 +/* AH */ +#define VBE_RETURN_STATUS_SUCCESSFULL 0x00 +#define VBE_RETURN_STATUS_FAILED 0x01 +#define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02 +#define VBE_RETURN_STATUS_INVALID 0x03 + +/* VBE Mode Numbers */ + +#define VBE_MODE_VESA_DEFINED 0x0100 +#define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800 +#define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000 +#define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000 + +#define VBE_VESA_MODE_END_OF_LIST 0xFFFF + +/* Capabilities */ + +#define VBE_CAPABILITY_8BIT_DAC 0x0001 +#define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002 +#define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004 +#define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008 +#define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010 + +/* Mode Attributes */ + +#define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001 +#define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002 +#define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004 +#define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008 +#define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010 +#define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020 +#define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040 +#define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080 +#define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100 +#define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200 +#define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400 +#define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800 +#define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000 + +#define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE ) + +/* Window attributes */ + +#define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01 +#define VBE_WINDOW_ATTRIBUTE_READABLE 0x02 +#define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04 + +/* Memory model */ + +#define VBE_MEMORYMODEL_TEXT_MODE 0x00 +#define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01 +#define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02 +#define VBE_MEMORYMODEL_PLANAR 0x03 +#define VBE_MEMORYMODEL_PACKED_PIXEL 0x04 +#define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05 +#define VBE_MEMORYMODEL_DIRECT_COLOR 0x06 +#define VBE_MEMORYMODEL_YUV 0x07 + +/* DirectColorModeInfo */ + +#define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01 +#define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02 + +#endif diff --git a/qemu/roms/seabios/src/std/vga.h b/qemu/roms/seabios/src/std/vga.h new file mode 100644 index 000000000..de9ec7506 --- /dev/null +++ b/qemu/roms/seabios/src/std/vga.h @@ -0,0 +1,63 @@ +#ifndef __VGA_H +#define __VGA_H +// Standard structure definitions for vgabios video tables + +#include "types.h" // u8 + +// standard BIOS Video Parameter Table +struct video_param_s { + u8 twidth; + u8 theightm1; + u8 cheight; + u16 slength; + u8 sequ_regs[4]; + u8 miscreg; + u8 crtc_regs[25]; + u8 actl_regs[20]; + u8 grdc_regs[9]; +} PACKED; + +// Standard Video Save Pointer Table +struct video_save_pointer_s { + struct segoff_s videoparam; + struct segoff_s paramdynamicsave; + struct segoff_s textcharset; + struct segoff_s graphcharset; + struct segoff_s secsavepointer; + u8 reserved[8]; +} PACKED; + +// Data returned by int101B +struct video_func_static { + u32 modes; + u8 reserved_0x04[3]; + u8 scanlines; + u8 cblocks; + u8 active_cblocks; + u16 misc_flags; + u8 reserved_0x0c[2]; + u8 save_flags; + u8 reserved_0x0f; +} PACKED; + +struct video_func_info { + struct segoff_s static_functionality; + u8 bda_0x49[30]; + u8 bda_0x84[3]; + u8 dcc_index; + u8 dcc_alt; + u16 colors; + u8 pages; + u8 scan_lines; + u8 primary_char; + u8 secondar_char; + u8 misc; + u8 non_vga_mode; + u8 reserved_2f[2]; + u8 video_mem; + u8 save_flags; + u8 disp_info; + u8 reserved_34[12]; +} PACKED; + +#endif // vga.h diff --git a/qemu/roms/seabios/src/string.c b/qemu/roms/seabios/src/string.c new file mode 100644 index 000000000..2e4e43746 --- /dev/null +++ b/qemu/roms/seabios/src/string.c @@ -0,0 +1,251 @@ +// String manipulation functions. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "stacks.h" // yield +#include "string.h" // memcpy +#include "farptr.h" // SET_SEG + + +/**************************************************************** + * String ops + ****************************************************************/ + +// Sum the bytes in the specified area. +u8 +checksum_far(u16 buf_seg, void *buf_far, u32 len) +{ + SET_SEG(ES, buf_seg); + u32 i; + u8 sum = 0; + for (i=0; i<len; i++) + sum += GET_VAR(ES, ((u8*)buf_far)[i]); + return sum; +} + +u8 +checksum(void *buf, u32 len) +{ + return checksum_far(GET_SEG(SS), buf, len); +} + +size_t +strlen(const char *s) +{ + if (__builtin_constant_p(s)) + return __builtin_strlen(s); + const char *p = s; + while (*p) + p++; + return p-s; +} + +int +memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n) +{ + while (n--) { + int d = GET_FARVAR(s1seg, *(u8*)s1) - GET_FARVAR(s2seg, *(u8*)s2); + if (d) + return d < 0 ? -1 : 1; + s1++; + s2++; + } + return 0; +} + +// Compare two areas of memory. +int +memcmp(const void *s1, const void *s2, size_t n) +{ + while (n) { + if (*(u8*)s1 != *(u8*)s2) + return *(u8*)s1 < *(u8*)s2 ? -1 : 1; + s1++; + s2++; + n--; + } + return 0; +} + +// Compare two strings. +int +strcmp(const char *s1, const char *s2) +{ + for (;;) { + if (*s1 != *s2) + return *s1 < *s2 ? -1 : 1; + if (! *s1) + return 0; + s1++; + s2++; + } +} + +inline void +memset_far(u16 d_seg, void *d_far, u8 c, size_t len) +{ + SET_SEG(ES, d_seg); + asm volatile( + "rep stosb %%es:(%%di)" + : "+c"(len), "+D"(d_far) + : "a"(c), "m" (__segment_ES) + : "cc", "memory"); +} + +inline void +memset16_far(u16 d_seg, void *d_far, u16 c, size_t len) +{ + len /= 2; + SET_SEG(ES, d_seg); + asm volatile( + "rep stosw %%es:(%%di)" + : "+c"(len), "+D"(d_far) + : "a"(c), "m" (__segment_ES) + : "cc", "memory"); +} + +void * +memset(void *s, int c, size_t n) +{ + while (n) + ((char *)s)[--n] = c; + return s; +} + +void memset_fl(void *ptr, u8 val, size_t size) +{ + if (MODESEGMENT) + memset_far(FLATPTR_TO_SEG(ptr), (void*)(FLATPTR_TO_OFFSET(ptr)), + val, size); + else + memset(ptr, val, size); +} + +inline void +memcpy_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len) +{ + SET_SEG(ES, d_seg); + u16 bkup_ds; + asm volatile( + "movw %%ds, %w0\n" + "movw %w4, %%ds\n" + "rep movsb (%%si),%%es:(%%di)\n" + "movw %w0, %%ds" + : "=&r"(bkup_ds), "+c"(len), "+S"(s_far), "+D"(d_far) + : "r"(s_seg), "m" (__segment_ES) + : "cc", "memory"); +} + +inline void +memcpy_fl(void *d_fl, const void *s_fl, size_t len) +{ + if (MODESEGMENT) + memcpy_far(FLATPTR_TO_SEG(d_fl), (void*)FLATPTR_TO_OFFSET(d_fl) + , FLATPTR_TO_SEG(s_fl), (void*)FLATPTR_TO_OFFSET(s_fl) + , len); + else + memcpy(d_fl, s_fl, len); +} + +void * +#undef memcpy +memcpy(void *d1, const void *s1, size_t len) +#if MODESEGMENT == 0 +#define memcpy __builtin_memcpy +#endif +{ + SET_SEG(ES, GET_SEG(SS)); + void *d = d1; + if (((u32)d1 | (u32)s1 | len) & 3) { + // non-aligned memcpy + asm volatile( + "rep movsb (%%esi),%%es:(%%edi)" + : "+c"(len), "+S"(s1), "+D"(d) + : "m" (__segment_ES) : "cc", "memory"); + return d1; + } + // Common case - use 4-byte copy + len /= 4; + asm volatile( + "rep movsl (%%esi),%%es:(%%edi)" + : "+c"(len), "+S"(s1), "+D"(d) + : "m" (__segment_ES) : "cc", "memory"); + return d1; +} + +// Copy to/from memory mapped IO. IO mem is very slow, so yield +// periodically. +void +iomemcpy(void *d, const void *s, u32 len) +{ + ASSERT32FLAT(); + yield(); + while (len > 3) { + u32 copylen = len; + if (copylen > 2048) + copylen = 2048; + copylen /= 4; + len -= copylen * 4; + asm volatile( + "rep movsl (%%esi),%%es:(%%edi)" + : "+c"(copylen), "+S"(s), "+D"(d) + : : "cc", "memory"); + yield(); + } + if (len) + // Copy any remaining bytes. + memcpy(d, s, len); +} + +void * +memmove(void *d, const void *s, size_t len) +{ + if (s >= d) + return memcpy(d, s, len); + + d += len-1; + s += len-1; + while (len--) { + *(char*)d = *(char*)s; + d--; + s--; + } + + return d; +} + +// Copy a string - truncating it if necessary. +char * +strtcpy(char *dest, const char *src, size_t len) +{ + char *d = dest; + while (--len && *src != '\0') + *d++ = *src++; + *d = '\0'; + return dest; +} + +// locate first occurance of character c in the string s +char * +strchr(const char *s, int c) +{ + for (; *s; s++) + if (*s == c) + return (char*)s; + return NULL; +} + +// Remove any trailing blank characters (spaces, new lines, carriage returns) +char * +nullTrailingSpace(char *buf) +{ + int len = strlen(buf); + char *end = &buf[len-1]; + while (end >= buf && *end <= ' ') + *(end--) = '\0'; + while (*buf && *buf <= ' ') + buf++; + return buf; +} diff --git a/qemu/roms/seabios/src/string.h b/qemu/roms/seabios/src/string.h new file mode 100644 index 000000000..a557d6a44 --- /dev/null +++ b/qemu/roms/seabios/src/string.h @@ -0,0 +1,31 @@ +// String manipulation function defs. +#ifndef __STRING_H +#define __STRING_H + +#include "types.h" // u32 + +// string.c +u8 checksum_far(u16 buf_seg, void *buf_far, u32 len); +u8 checksum(void *buf, u32 len); +size_t strlen(const char *s); +int memcmp_far(u16 s1seg, const void *s1, u16 s2seg, const void *s2, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +int strcmp(const char *s1, const char *s2); +inline void memset_far(u16 d_seg, void *d_far, u8 c, size_t len); +inline void memset16_far(u16 d_seg, void *d_far, u16 c, size_t len); +void *memset(void *s, int c, size_t n); +void memset_fl(void *ptr, u8 val, size_t size); +inline void memcpy_far(u16 d_seg, void *d_far + , u16 s_seg, const void *s_far, size_t len); +void memcpy_fl(void *d_fl, const void *s_fl, size_t len); +void *memcpy(void *d1, const void *s1, size_t len); +#if MODESEGMENT == 0 +#define memcpy __builtin_memcpy +#endif +void iomemcpy(void *d, const void *s, u32 len); +void *memmove(void *d, const void *s, size_t len); +char *strtcpy(char *dest, const char *src, size_t len); +char *strchr(const char *s, int c); +char *nullTrailingSpace(char *buf); + +#endif // string.h diff --git a/qemu/roms/seabios/src/system.c b/qemu/roms/seabios/src/system.c new file mode 100644 index 000000000..60a6fce58 --- /dev/null +++ b/qemu/roms/seabios/src/system.c @@ -0,0 +1,357 @@ +// Handler for int 0x15 "system" calls +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // struct bregs +#include "hw/pic.h" // pic_reset +#include "malloc.h" // LegacyRamSize +#include "memmap.h" // E820_RAM +#include "output.h" // debug_enter +#include "string.h" // memcpy_far +#include "util.h" // handle_1553 +#include "x86.h" // set_a20 + +static void +handle_152400(struct bregs *regs) +{ + set_a20(0); + set_code_success(regs); +} + +static void +handle_152401(struct bregs *regs) +{ + set_a20(1); + set_code_success(regs); +} + +static void +handle_152402(struct bregs *regs) +{ + regs->al = get_a20(); + set_code_success(regs); +} + +static void +handle_152403(struct bregs *regs) +{ + regs->bx = 3; + set_code_success(regs); +} + +static void +handle_1524XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_1524(struct bregs *regs) +{ + switch (regs->al) { + case 0x00: handle_152400(regs); break; + case 0x01: handle_152401(regs); break; + case 0x02: handle_152402(regs); break; + case 0x03: handle_152403(regs); break; + default: handle_1524XX(regs); break; + } +} + +// removable media eject +static void +handle_1552(struct bregs *regs) +{ + set_code_success(regs); +} + +static void +handle_1587(struct bregs *regs) +{ + // +++ should probably have descriptor checks + // +++ should have exception handlers + + u8 prev_a20_enable = set_a20(1); // enable A20 line + + // 128K max of transfer on 386+ ??? + // source == destination ??? + + // ES:SI points to descriptor table + // offset use initially comments + // ============================================== + // 00..07 Unused zeros Null descriptor + // 08..0f GDT zeros filled in by BIOS + // 10..17 source ssssssss source of data + // 18..1f dest dddddddd destination of data + // 20..27 CS zeros filled in by BIOS + // 28..2f SS zeros filled in by BIOS + +// check for access rights of source & dest here + + // Initialize GDT descriptor + u64 *gdt_far = (void*)(regs->si + 0); + u16 gdt_seg = regs->es; + u32 loc = (u32)MAKE_FLATPTR(gdt_seg, gdt_far); + SET_FARVAR(gdt_seg, gdt_far[1], GDT_DATA | GDT_LIMIT((6*sizeof(u64))-1) + | GDT_BASE(loc)); + // Initialize CS descriptor + u64 lim = GDT_LIMIT(0x0ffff); + if (in_post()) + lim = GDT_GRANLIMIT(0xffffffff); + SET_FARVAR(gdt_seg, gdt_far[4], GDT_CODE | lim | GDT_BASE(BUILD_BIOS_ADDR)); + // Initialize SS descriptor + loc = (u32)MAKE_FLATPTR(GET_SEG(SS), 0); + SET_FARVAR(gdt_seg, gdt_far[5], GDT_DATA | lim | GDT_BASE(loc)); + + SET_SEG(ES, gdt_seg); + u16 count = regs->cx, si = 0, di = 0; + asm volatile( + // Load new descriptor tables + " lgdtw %%es:(1<<3)(%%eax)\n" + " lidtw %%cs:pmode_IDT_info\n" + + // Enable protected mode + " movl %%cr0, %%eax\n" + " orl $" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to protected mode + " ljmpw $(4<<3), $1f\n" + + // GDT points to valid descriptor table, now load DS, ES + "1:movw $(2<<3), %%ax\n" // 2nd descriptor in table, TI=GDT, RPL=00 + " movw %%ax, %%ds\n" + " movw $(3<<3), %%ax\n" // 3rd descriptor in table, TI=GDT, RPL=00 + " movw %%ax, %%es\n" + + // memcpy CX words using 32bit memcpy if applicable + " testw $1, %%cx\n" + " jnz 3f\n" + " shrw $1, %%cx\n" + " rep movsl %%ds:(%%si), %%es:(%%di)\n" + + // Restore DS and ES segment limits to 0xffff + "2:movw $(5<<3), %%ax\n" // 5th descriptor in table (SS) + " movw %%ax, %%ds\n" + " movw %%ax, %%es\n" + + // Disable protected mode + " movl %%cr0, %%eax\n" + " andl $~" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to real mode + " ljmpw $" __stringify(SEG_BIOS) ", $4f\n" + + // Slower 16bit copy method + "3:rep movsw %%ds:(%%si), %%es:(%%di)\n" + " jmp 2b\n" + + // restore IDT to normal real-mode defaults + "4:lidtw %%cs:rmode_IDT_info\n" + + // Restore %ds (from %ss) + " movw %%ss, %%ax\n" + " movw %%ax, %%ds\n" + : "+a" (gdt_far), "+c"(count), "+m" (__segment_ES) + : "S" (si), "D" (di) + : "cc"); + + set_a20(prev_a20_enable); + + set_code_success(regs); +} + +// Get the amount of extended memory (above 1M) +static void +handle_1588(struct bregs *regs) +{ + u32 rs = GET_GLOBAL(LegacyRamSize); + + // According to Ralf Brown's interrupt the limit should be 15M, + // but real machines mostly return max. 63M. + if (rs > 64*1024*1024) + regs->ax = 63 * 1024; + else + regs->ax = (rs - 1*1024*1024) / 1024; + set_success(regs); +} + +// Switch to protected mode +void VISIBLE16 +handle_1589(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_15); + set_a20(1); + + pic_reset(regs->bl, regs->bh); + + u64 *gdt_far = (void*)(regs->si + 0); + u16 gdt_seg = regs->es; + SET_FARVAR(gdt_seg, gdt_far[7], GDT_CODE | GDT_LIMIT(BUILD_BIOS_SIZE-1) + | GDT_BASE(BUILD_BIOS_ADDR)); + + regs->ds = 3<<3; // 3rd gdt descriptor is %ds + regs->es = 4<<3; // 4th gdt descriptor is %es + regs->code.seg = 6<<3; // 6th gdt descriptor is %cs + + set_code_success(regs); + + asm volatile( + // Load new descriptor tables + " lgdtw %%es:(1<<3)(%%si)\n" + " lidtw %%es:(2<<3)(%%si)\n" + + // Enable protected mode + " movl %%cr0, %%eax\n" + " orl $" __stringify(CR0_PE) ", %%eax\n" + " movl %%eax, %%cr0\n" + + // far jump to flush CPU queue after transition to protected mode + " ljmpw $(7<<3), $1f\n" + + // GDT points to valid descriptor table, now load SS + "1:movw $(5<<3), %%ax\n" + " movw %%ax, %%ds\n" + " movw %%ax, %%ss\n" + : + : "S"(gdt_far), "m" (__segment_ES) + : "eax", "cc"); +} + +// Device busy interrupt. Called by Int 16h when no key available +static void +handle_1590(struct bregs *regs) +{ +} + +// Interrupt complete. Called by Int 16h when key becomes available +static void +handle_1591(struct bregs *regs) +{ +} + +// keyboard intercept +static void +handle_154f(struct bregs *regs) +{ + set_invalid_silent(regs); +} + +static void +handle_15c0(struct bregs *regs) +{ + regs->es = SEG_BIOS; + regs->bx = (u32)&BIOS_CONFIG_TABLE; + set_code_success(regs); +} + +static void +handle_15c1(struct bregs *regs) +{ + regs->es = get_ebda_seg(); + set_success(regs); +} + +static void +handle_15e801(struct bregs *regs) +{ + // my real system sets ax and bx to 0 + // this is confirmed by Ralph Brown list + // but syslinux v1.48 is known to behave + // strangely if ax is set to 0 + // regs.u.r16.ax = 0; + // regs.u.r16.bx = 0; + + u32 rs = GET_GLOBAL(LegacyRamSize); + + // Get the amount of extended memory (above 1M) + if (rs > 16*1024*1024) { + // limit to 15M + regs->cx = 15*1024; + // Get the amount of extended memory above 16M in 64k blocks + regs->dx = (rs - 16*1024*1024) / (64*1024); + } else { + regs->cx = (rs - 1*1024*1024) / 1024; + regs->dx = 0; + } + + // Set configured memory equal to extended memory + regs->ax = regs->cx; + regs->bx = regs->dx; + + set_success(regs); +} + +static void +handle_15e820(struct bregs *regs) +{ + int count = GET_GLOBAL(e820_count); + if (regs->edx != 0x534D4150 || regs->bx >= count + || regs->ecx < sizeof(e820_list[0])) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + + memcpy_far(regs->es, (void*)(regs->di+0) + , get_global_seg(), &e820_list[regs->bx] + , sizeof(e820_list[0])); + if (regs->bx == count-1) + regs->ebx = 0; + else + regs->ebx++; + regs->eax = 0x534D4150; + regs->ecx = sizeof(e820_list[0]); + set_success(regs); +} + +static void +handle_15e8XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_15e8(struct bregs *regs) +{ + switch (regs->al) { + case 0x01: handle_15e801(regs); break; + case 0x20: handle_15e820(regs); break; + default: handle_15e8XX(regs); break; + } +} + +static void +handle_15XX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +// INT 15h System Services Entry Point +void VISIBLE16 +handle_15(struct bregs *regs) +{ + debug_enter(regs, DEBUG_HDL_15); + switch (regs->ah) { + case 0x24: handle_1524(regs); break; + case 0x4f: handle_154f(regs); break; + case 0x52: handle_1552(regs); break; + case 0x53: handle_1553(regs); break; + case 0x5f: handle_155f(regs); break; + case 0x7f: handle_157f(regs); break; + case 0x83: handle_1583(regs); break; + case 0x86: handle_1586(regs); break; + case 0x87: handle_1587(regs); break; + case 0x88: handle_1588(regs); break; + case 0x90: handle_1590(regs); break; + case 0x91: handle_1591(regs); break; + case 0xc0: handle_15c0(regs); break; + case 0xc1: handle_15c1(regs); break; + case 0xc2: handle_15c2(regs); break; + case 0xe8: handle_15e8(regs); break; + default: handle_15XX(regs); break; + } +} diff --git a/qemu/roms/seabios/src/types.h b/qemu/roms/seabios/src/types.h new file mode 100644 index 000000000..097372cdb --- /dev/null +++ b/qemu/roms/seabios/src/types.h @@ -0,0 +1,156 @@ +// Basic type definitions for X86 cpus. +// +// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __TYPES_H +#define __TYPES_H + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; +typedef signed long long s64; +typedef u32 size_t; + +union u64_u32_u { + struct { u32 lo, hi; }; + u64 val; +}; + +// Definition for common 16bit segment/offset pointers. +struct segoff_s { + union { + struct { + u16 offset; + u16 seg; + }; + u32 segoff; + }; +}; + +#ifdef MANUAL_NO_JUMP_TABLE +# define default case 775324556: asm(""); default +#endif + +#ifdef WHOLE_PROGRAM +# define __VISIBLE __attribute__((externally_visible)) +#else +# define __VISIBLE +#endif + +#define UNIQSEC __FILE__ "." __stringify(__LINE__) + +#define __noreturn __attribute__((noreturn)) +extern void __force_link_error__only_in_32bit_flat(void) __noreturn; +extern void __force_link_error__only_in_32bit_segmented(void) __noreturn; +extern void __force_link_error__only_in_16bit(void) __noreturn; + +#define __ASM(code) asm(".section .text.asm." UNIQSEC "\n\t" code) + +#if MODE16 == 1 +// Notes a function as externally visible in the 16bit code chunk. +# define VISIBLE16 __VISIBLE +// Notes a function as externally visible in the 32bit flat code chunk. +# define VISIBLE32FLAT +// Notes a 32bit flat function that will only be called during init. +# define VISIBLE32INIT +// Notes a function as externally visible in the 32bit segmented code chunk. +# define VISIBLE32SEG +// Designate a variable as (only) visible to 16bit code. +# define VAR16 __section(".data16." UNIQSEC) +// Designate a variable as (only) visible to 32bit segmented code. +# define VAR32SEG __section(".discard.var32seg." UNIQSEC) +// Designate a variable as visible and located in the e-segment. +# define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak +// Designate a variable as visible and located in the f-segment. +# define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +// Designate a variable at a specific address in the f-segment. +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak +// Verify a variable is only accessable via 32bit "init" functions +# define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) +// Designate top-level assembler as 16bit only. +# define ASM16(code) __ASM(code) +// Designate top-level assembler as 32bit flat only. +# define ASM32FLAT(code) +// Compile time check for a given mode. +# define ASSERT16() do { } while (0) +# define ASSERT32SEG() __force_link_error__only_in_32bit_segmented() +# define ASSERT32FLAT() __force_link_error__only_in_32bit_flat() +#elif MODESEGMENT == 1 +# define VISIBLE16 +# define VISIBLE32FLAT +# define VISIBLE32INIT +# define VISIBLE32SEG __VISIBLE +# define VAR16 __section(".discard.var16." UNIQSEC) +# define VAR32SEG __section(".data32seg." UNIQSEC) +# define VARLOW __section(".discard.varlow." UNIQSEC) __VISIBLE __weak +# define VARFSEG __section(".discard.varfseg." UNIQSEC) __VISIBLE __weak +# define VARFSEGFIXED(addr) __section(".discard.varfixed." UNIQSEC) __VISIBLE __weak +# define VARVERIFY32INIT __section(".discard.varinit." UNIQSEC) +# define ASM16(code) +# define ASM32FLAT(code) +# define ASSERT16() __force_link_error__only_in_16bit() +# define ASSERT32SEG() do { } while (0) +# define ASSERT32FLAT() __force_link_error__only_in_32bit_flat() +#else +# define VISIBLE16 +# define VISIBLE32FLAT __section(".text.runtime." UNIQSEC) __VISIBLE +# define VISIBLE32INIT __section(".text.init." UNIQSEC) __VISIBLE +# define VISIBLE32SEG +# define VAR16 __section(".discard.var16." UNIQSEC) +# define VAR32SEG __section(".discard.var32seg." UNIQSEC) +# define VARLOW __section(".data.varlow." UNIQSEC) __VISIBLE __weak +# define VARFSEG __section(".data.varfseg." UNIQSEC) __VISIBLE +# define VARFSEGFIXED(addr) __section(".fixedaddr." __stringify(addr)) __VISIBLE __aligned(1) +# define VARVERIFY32INIT __section(".data.varinit." UNIQSEC) +# define ASM16(code) +# define ASM32FLAT(code) __ASM(code) +# define ASSERT16() __force_link_error__only_in_16bit() +# define ASSERT32SEG() __force_link_error__only_in_32bit_segmented() +# define ASSERT32FLAT() do { } while (0) +#endif + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define DIV_ROUND_CLOSEST(x, divisor)({ \ + typeof(divisor) __divisor = divisor; \ + (((x) + ((__divisor) / 2)) / (__divisor)); \ + }) +#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) +#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) +#define ALIGN_DOWN(x,a) ((x) & ~((typeof(x))(a)-1)) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#define container_of_or_null(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *___mptr = (ptr); \ + ___mptr ? container_of(___mptr, type, member) : NULL; }) + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define NULL ((void*)0) + +#define __weak __attribute__((weak)) +#define __section(S) __attribute__((section(S))) + +#define PACKED __attribute__((packed)) +#define __aligned(x) __attribute__((aligned(x))) + +#define barrier() __asm__ __volatile__("": : :"memory") + +#define noinline __attribute__((noinline)) +#define __always_inline inline __attribute__((always_inline)) +#define __malloc __attribute__((__malloc__)) +#define __attribute_const __attribute__((__const__)) + +#define __stringify_1(x) #x +#define __stringify(x) __stringify_1(x) + +#endif // types.h diff --git a/qemu/roms/seabios/src/util.h b/qemu/roms/seabios/src/util.h new file mode 100644 index 000000000..09bb8a9f3 --- /dev/null +++ b/qemu/roms/seabios/src/util.h @@ -0,0 +1,237 @@ +// Misc function and variable declarations. +#ifndef __UTIL_H +#define __UTIL_H + +#include "types.h" // u32 + +// apm.c +void apm_shutdown(void); +struct bregs; +void handle_1553(struct bregs *regs); + +// bmp.c +struct bmp_decdata *bmp_alloc(void); +int bmp_decode(struct bmp_decdata *bmp, unsigned char *data, int data_size); +void bmp_get_size(struct bmp_decdata *bmp, int *width, int *height); +int bmp_show(struct bmp_decdata *bmp, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest); + +// boot.c +void boot_init(void); +void boot_add_bev(u16 seg, u16 bev, u16 desc, int prio); +void boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio); +struct drive_s; +void boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_hd(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_cd(struct drive_s *drive_g, const char *desc, int prio); +void boot_add_cbfs(void *data, const char *desc, int prio); +void interactive_bootmenu(void); +void bcv_prepboot(void); +struct pci_device; +int bootprio_find_pci_device(struct pci_device *pci); +int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun); +int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave); +int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid); +int bootprio_find_pci_rom(struct pci_device *pci, int instance); +int bootprio_find_named_rom(const char *name, int instance); +struct usbdevice_s; +int bootprio_find_usb(struct usbdevice_s *usbdev, int lun); + +// bootsplash.c +void enable_vga_console(void); +void enable_bootsplash(void); +void disable_bootsplash(void); + +// cdrom.c +extern u8 CDRom_locks[]; +extern struct eltorito_s CDEmu; +extern struct drive_s *cdemu_drive_gf; +struct disk_op_s; +int process_cdemu_op(struct disk_op_s *op); +void cdrom_prepboot(void); +int cdrom_boot(struct drive_s *drive_g); + +// clock.c +void clock_setup(void); +void handle_1583(struct bregs *regs); +u32 irqtimer_calc_ticks(u32 count); +u32 irqtimer_calc(u32 msecs); +int irqtimer_check(u32 end); +void handle_1586(struct bregs *regs); + +// fw/acpi.c +void acpi_setup(void); + +// fw/biostable.c +void copy_pir(void *pos); +void copy_mptable(void *pos); +extern struct pir_header *PirAddr; +void copy_acpi_rsdp(void *pos); +extern struct rsdp_descriptor *RsdpAddr; +extern u32 acpi_pm1a_cnt; +extern u16 acpi_pm_base; +void *find_acpi_rsdp(void); +u32 find_resume_vector(void); +void acpi_reboot(void); +void find_acpi_features(void); +extern struct smbios_entry_point *SMBiosAddr; +void copy_smbios(void *pos); +void display_uuid(void); +void copy_table(void *pos); +void smbios_setup(void); + +// fw/coreboot.c +extern const char *CBvendor, *CBpart; +struct cbfs_file; +void coreboot_debug_putc(char c); +void cbfs_run_payload(struct cbfs_file *file); +void coreboot_platform_setup(void); +void cbfs_payload_setup(void); +void coreboot_preinit(void); +void coreboot_cbfs_init(void); +struct cb_header; +void *find_cb_subtable(struct cb_header *cbh, u32 tag); +struct cb_header *find_cb_table(void); + +// fw/csm.c +int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid); +int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave); +int csm_bootprio_pci(struct pci_device *pci); + +// fw/mptable.c +void mptable_setup(void); + +// fw/mtrr.c +void mtrr_setup(void); + +// fw/pciinit.c +extern const u8 pci_irqs[4]; +void pci_setup(void); +void pci_resume(void); + +// fw/pirtable.c +void pirtable_setup(void); + +// fw/shadow.c +void make_bios_writable(void); +void make_bios_readonly(void); +void qemu_prep_reset(void); + +// fw/smbios.c +void smbios_legacy_setup(void); + +// fw/smm.c +void smm_device_setup(void); +void smm_setup(void); + +// fw/smp.c +extern u32 MaxCountCPUs; +void wrmsr_smp(u32 index, u64 val); +void smp_setup(void); +int apic_id_is_present(u8 apic_id); + +// hw/dma.c +int dma_floppy(u32 addr, int count, int isWrite); +void dma_setup(void); + +// hw/floppy.c +extern struct floppy_ext_dbt_s diskette_param_table2; +void floppy_setup(void); +struct drive_s *init_floppy(int floppyid, int ftype); +int find_floppy_type(u32 size); +int process_floppy_op(struct disk_op_s *op); +void floppy_tick(void); + +// hw/ramdisk.c +void ramdisk_setup(void); +int process_ramdisk_op(struct disk_op_s *op); + +// hw/sdcard.c +int process_sdcard_op(struct disk_op_s *op); +void sdcard_setup(void); + +// hw/timer.c +void timer_setup(void); +void pmtimer_setup(u16 ioport); +u32 timer_calc(u32 msecs); +u32 timer_calc_usec(u32 usecs); +int timer_check(u32 end); +void ndelay(u32 count); +void udelay(u32 count); +void mdelay(u32 count); +void nsleep(u32 count); +void usleep(u32 count); +void msleep(u32 count); +u32 ticks_to_ms(u32 ticks); +u32 ticks_from_ms(u32 ms); +void pit_setup(void); + +// jpeg.c +struct jpeg_decdata *jpeg_alloc(void); +int jpeg_decode(struct jpeg_decdata *jpeg, unsigned char *buf); +void jpeg_get_size(struct jpeg_decdata *jpeg, int *width, int *height); +int jpeg_show(struct jpeg_decdata *jpeg, unsigned char *pic, int width + , int height, int depth, int bytes_per_line_dest); + +// kbd.c +void kbd_init(void); +void handle_15c2(struct bregs *regs); +void process_key(u8 key); + +// misc.c +extern struct bios_config_table_s BIOS_CONFIG_TABLE __aligned(1); +extern struct floppy_dbt_s diskette_param_table __aligned(1); +extern u8 BiosChecksum; +int in_post(void); +void mathcp_setup(void); + +// mouse.c +void mouse_init(void); +void process_mouse(u8 data); + +// optionroms.c +struct rom_header; +void callrom(struct rom_header *rom, u16 bdf); +void call_bcv(u16 seg, u16 ip); +int is_pci_vga(struct pci_device *pci); +void optionrom_setup(void); +void vgarom_setup(void); +void s3_resume_vga(void); +extern int ScreenAndDebug; + +// pcibios.c +void handle_1ab1(struct bregs *regs); +void bios32_init(void); + +// pmm.c +void pmm_init(void); +void pmm_prepboot(void); + +// pnpbios.c +u16 get_pnp_offset(void); +void pnp_init(void); + +// post.c +void interface_init(void); +void device_hardware_setup(void); +void prepareboot(void); +void startBoot(void); +void reloc_preinit(void *f, void *arg); + +// resume.c +extern int HaveRunPost; + +// serial.c +void serial_setup(void); +void lpt_setup(void); + +// vgahooks.c +void handle_155f(struct bregs *regs); +void handle_157f(struct bregs *regs); +void vgahook_setup(struct pci_device *pci); + + +// version (auto generated file out/version.c) +extern const char VERSION[]; + +#endif // util.h diff --git a/qemu/roms/seabios/src/vgahooks.c b/qemu/roms/seabios/src/vgahooks.c new file mode 100644 index 000000000..6a4acfeaf --- /dev/null +++ b/qemu/roms/seabios/src/vgahooks.c @@ -0,0 +1,354 @@ +// Hooks for via vgabios calls into main bios. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "bregs.h" // set_code_invalid +#include "config.h" // CONFIG_* +#include "hw/pci.h" // pci_find_device +#include "hw/pci_ids.h" // PCI_VENDOR_ID_VIA +#include "hw/pci_regs.h" // PCI_VENDOR_ID +#include "output.h" // dprintf +#include "string.h" // strcmp +#include "util.h" // handle_155f, handle_157f + +#define VH_VIA 1 +#define VH_INTEL 2 +#define VH_SMI 3 + +int VGAHookHandlerType VARFSEG; + +static void +handle_155fXX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +static void +handle_157fXX(struct bregs *regs) +{ + set_code_unimplemented(regs, RET_EUNSUPPORTED); +} + +/**************************************************************** + * Via hooks + ****************************************************************/ + +int ViaFBsize VARFSEG, ViaRamSpeed VARFSEG; + +static void +via_155f01(struct bregs *regs) +{ + regs->eax = 0x5f; + regs->cl = 2; // panel type = 2 = 1024 * 768 + set_success(regs); + dprintf(1, "Warning: VGA panel type is hardcoded\n"); +} + +static void +via_155f02(struct bregs *regs) +{ + regs->eax = 0x5f; + regs->bx = 2; + regs->cx = 0x401; // PAL + crt only + regs->dx = 0; // TV Layout - default + set_success(regs); + dprintf(1, "Warning: VGA TV/CRT output type is hardcoded\n"); +} + +static void +via_155f18(struct bregs *regs) +{ + int fbsize = GET_GLOBAL(ViaFBsize), ramspeed = GET_GLOBAL(ViaRamSpeed); + if (fbsize < 0 || ramspeed < 0) { + set_code_invalid(regs, RET_EUNSUPPORTED); + return; + } + regs->eax = 0x5f; + regs->ebx = 0x500 | (ramspeed << 4) | fbsize; + regs->ecx = 0x060; + set_success(regs); +} + +static void +via_155f19(struct bregs *regs) +{ + set_invalid_silent(regs); +} + +static void +via_155f(struct bregs *regs) +{ + switch (regs->al) { + case 0x01: via_155f01(regs); break; + case 0x02: via_155f02(regs); break; + case 0x18: via_155f18(regs); break; + case 0x19: via_155f19(regs); break; + default: handle_155fXX(regs); break; + } +} + +static int +getFBSize(struct pci_device *pci) +{ + /* FB config */ + u8 reg = pci_config_readb(pci->bdf, 0xa1); + + /* GFX disabled ? */ + if (!(reg & 0x80)) + return -1; + + static u8 mem_power[] = {0, 3, 4, 5, 6, 7, 8, 9}; + return mem_power[(reg >> 4) & 0x7]; +} + +static int +getViaRamSpeed(struct pci_device *pci) +{ + return (pci_config_readb(pci->bdf, 0x90) & 0x07) + 3; +} + +static int +getAMDRamSpeed(void) +{ + struct pci_device *pci = pci_find_device(PCI_VENDOR_ID_AMD + , PCI_DEVICE_ID_AMD_K8_NB_MEMCTL); + if (!pci) + return -1; + + /* mem clk 0 = DDR2 400 */ + return (pci_config_readb(pci->bdf, 0x94) & 0x7) + 6; +} + +/* int 0x15 - 5f18 + + ECX = unknown/dont care + EBX[3..0] Frame Buffer Size 2^N MiB + EBX[7..4] Memory speed: + 0: SDR 66Mhz + 1: SDR 100Mhz + 2: SDR 133Mhz + 3: DDR 100Mhz (PC1600 or DDR200) + 4: DDR 133Mhz (PC2100 or DDR266) + 5: DDR 166Mhz (PC2700 or DDR333) + 6: DDR 200Mhz (PC3200 or DDR400) + 7: DDR2 133Mhz (DDR2 533) + 8: DDR2 166Mhz (DDR2 667) + 9: DDR2 200Mhz (DDR2 800) + A: DDR2 233Mhz (DDR2 1066) + B: and above: Unknown + EBX[?..8] Total memory size? + EAX = 0x5f for success +*/ + +#define PCI_DEVICE_ID_VIA_K8M890CE_3 0x3336 +#define PCI_DEVICE_ID_VIA_VX855_MEMCTRL 0x3409 + +static void +via_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_VIA; + + struct pci_device *d = pci_find_device(PCI_VENDOR_ID_VIA + , PCI_DEVICE_ID_VIA_K8M890CE_3); + if (d) { + ViaFBsize = getFBSize(d); + ViaRamSpeed = getAMDRamSpeed(); + return; + } + d = pci_find_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855_MEMCTRL); + if (d) { + ViaFBsize = getFBSize(d); + ViaRamSpeed = getViaRamSpeed(d); + return; + } + + dprintf(1, "Warning: VGA memory size and speed is hardcoded\n"); + ViaFBsize = 5; // 32M frame buffer + ViaRamSpeed = 4; // MCLK = DDR266 +} + + +/**************************************************************** + * Intel VGA hooks + ****************************************************************/ + +u8 IntelDisplayType VARFSEG, IntelDisplayId VARFSEG; + +static void +intel_155f35(struct bregs *regs) +{ + regs->ax = 0x005f; + regs->cl = GET_GLOBAL(IntelDisplayType); + set_success(regs); +} + +static void +intel_155f40(struct bregs *regs) +{ + regs->ax = 0x005f; + regs->cl = GET_GLOBAL(IntelDisplayId); + set_success(regs); +} + +static void +intel_155f50(struct bregs *regs) +{ + /* Mandatory hook on some Dell laptops */ + regs->ax = 0x005f; + set_success(regs); +} + +static void +intel_155f(struct bregs *regs) +{ + switch (regs->al) { + case 0x35: intel_155f35(regs); break; + case 0x40: intel_155f40(regs); break; + case 0x50: intel_155f50(regs); break; + default: handle_155fXX(regs); break; + } +} + +#define BOOT_DISPLAY_DEFAULT (0) +#define BOOT_DISPLAY_CRT (1 << 0) +#define BOOT_DISPLAY_TV (1 << 1) +#define BOOT_DISPLAY_EFP (1 << 2) +#define BOOT_DISPLAY_LCD (1 << 3) +#define BOOT_DISPLAY_CRT2 (1 << 4) +#define BOOT_DISPLAY_TV2 (1 << 5) +#define BOOT_DISPLAY_EFP2 (1 << 6) +#define BOOT_DISPLAY_LCD2 (1 << 7) + +static void +intel_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + + IntelDisplayType = BOOT_DISPLAY_DEFAULT; + IntelDisplayId = 3; +} + +static void +roda_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + // IntelDisplayType = BOOT_DISPLAY_DEFAULT; + IntelDisplayType = BOOT_DISPLAY_LCD; + // IntelDisplayId = inb(0x60f) & 0x0f; // Correct according to Crete + IntelDisplayId = 3; // Correct according to empirical studies +} + +static void +kontron_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_INTEL; + IntelDisplayType = BOOT_DISPLAY_CRT; + IntelDisplayId = 3; +} + +static void +getac_setup(struct pci_device *pci) +{ +} + +/**************************************************************** + * Silicon Motion hooks + ****************************************************************/ + +u8 SmiBootDisplay VARFSEG; // 1: LCD, 2: CRT, 3: Both */ + +static void +smi_157f02(struct bregs *regs) +{ + /* Boot Display Device Override */ + regs->ax = 0x007f; + regs->bl = GET_GLOBAL(SmiBootDisplay); + set_success(regs); +} + +static void +smi_157f14(struct bregs *regs) +{ + /* ReduceOn support default status */ + regs->ax = 0x007f; + regs->bl = 0x00; + set_success(regs); +} + +static void +smi_157f(struct bregs *regs) +{ + switch (regs->al) { + case 0x02: smi_157f02(regs); break; + case 0x14: smi_157f14(regs); break; + default: handle_157fXX(regs); break; + } +} + +static void +winent_mb6047_setup(struct pci_device *pci) +{ + VGAHookHandlerType = VH_SMI; + SmiBootDisplay = 0x02; +} + +/**************************************************************** + * Entry and setup + ****************************************************************/ + +// Main 16bit entry point +void +handle_155f(struct bregs *regs) +{ + if (!CONFIG_VGAHOOKS) { + handle_155fXX(regs); + return; + } + + int htype = GET_GLOBAL(VGAHookHandlerType); + switch (htype) { + case VH_VIA: via_155f(regs); break; + case VH_INTEL: intel_155f(regs); break; + default: handle_155fXX(regs); break; + } +} + +// Main 16bit entry point +void +handle_157f(struct bregs *regs) +{ + if (!CONFIG_VGAHOOKS) { + handle_157fXX(regs); + return; + } + + int htype = GET_GLOBAL(VGAHookHandlerType); + switch (htype) { + case VH_SMI: smi_157f(regs); break; + default: handle_157fXX(regs); break; + } +} + +// Setup +void +vgahook_setup(struct pci_device *pci) +{ + if (!CONFIG_VGAHOOKS) + return; + + if (strcmp(CBvendor, "KONTRON") == 0 && strcmp(CBpart, "986LCD-M") == 0) + kontron_setup(pci); + else if (strcmp(CBvendor, "GETAC") == 0 && strcmp(CBpart, "P470") == 0) + getac_setup(pci); + else if (strcmp(CBvendor, "RODA") == 0 && strcmp(CBpart, "RK886EX") == 0) + roda_setup(pci); + else if (strcmp(CBvendor, "Win Enterprise") == 0 && strcmp(CBpart, "MB6047") == 0) + winent_mb6047_setup(pci); + else if (pci->vendor == PCI_VENDOR_ID_VIA) + via_setup(pci); + else if (pci->vendor == PCI_VENDOR_ID_INTEL) + intel_setup(pci); +} diff --git a/qemu/roms/seabios/src/x86.c b/qemu/roms/seabios/src/x86.c new file mode 100644 index 000000000..0fdf86f9c --- /dev/null +++ b/qemu/roms/seabios/src/x86.c @@ -0,0 +1,23 @@ +// X86 utility functions. +// +// Copyright (C) 2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "x86.h" // __cpuid + +void +cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) +{ + // Check for cpu id + u32 origflags = save_flags(); + restore_flags(origflags ^ F_ID); + u32 newflags = save_flags(); + restore_flags(origflags); + + if (((origflags ^ newflags) & F_ID) != F_ID) + // no cpuid + *eax = *ebx = *ecx = *edx = 0; + else + __cpuid(index, eax, ebx, ecx, edx); +} diff --git a/qemu/roms/seabios/src/x86.h b/qemu/roms/seabios/src/x86.h new file mode 100644 index 000000000..7798b1c17 --- /dev/null +++ b/qemu/roms/seabios/src/x86.h @@ -0,0 +1,247 @@ +// Basic x86 asm functions. +#ifndef __X86_H +#define __X86_H + +// CPU flag bitdefs +#define F_CF (1<<0) +#define F_ZF (1<<6) +#define F_IF (1<<9) +#define F_ID (1<<21) + +// CR0 flags +#define CR0_PG (1<<31) // Paging +#define CR0_CD (1<<30) // Cache disable +#define CR0_NW (1<<29) // Not Write-through +#define CR0_PE (1<<0) // Protection enable + +// PORT_A20 bitdefs +#define PORT_A20 0x0092 +#define A20_ENABLE_BIT 0x02 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u32 + +static inline void irq_disable(void) +{ + asm volatile("cli": : :"memory"); +} + +static inline void irq_enable(void) +{ + asm volatile("sti": : :"memory"); +} + +static inline u32 save_flags(void) +{ + u32 flags; + asm volatile("pushfl ; popl %0" : "=rm" (flags)); + return flags; +} + +static inline void restore_flags(u32 flags) +{ + asm volatile("pushl %0 ; popfl" : : "g" (flags) : "memory", "cc"); +} + +static inline void cpu_relax(void) +{ + asm volatile("rep ; nop": : :"memory"); +} + +static inline void nop(void) +{ + asm volatile("nop"); +} + +static inline void hlt(void) +{ + asm volatile("hlt": : :"memory"); +} + +static inline void wbinvd(void) +{ + asm volatile("wbinvd": : :"memory"); +} + +#define CPUID_TSC (1 << 4) +#define CPUID_MSR (1 << 5) +#define CPUID_APIC (1 << 9) +#define CPUID_MTRR (1 << 12) +static inline void __cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) +{ + asm("cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "0" (index)); +} + +static inline u32 getcr0(void) { + u32 cr0; + asm("movl %%cr0, %0" : "=r"(cr0)); + return cr0; +} +static inline void setcr0(u32 cr0) { + asm("movl %0, %%cr0" : : "r"(cr0)); +} + +static inline u64 rdmsr(u32 index) +{ + u64 ret; + asm ("rdmsr" : "=A"(ret) : "c"(index)); + return ret; +} + +static inline void wrmsr(u32 index, u64 val) +{ + asm volatile ("wrmsr" : : "c"(index), "A"(val)); +} + +static inline u64 rdtscll(void) +{ + u64 val; + asm volatile("rdtsc" : "=A" (val)); + return val; +} + +static inline u32 __ffs(u32 word) +{ + asm("bsf %1,%0" + : "=r" (word) + : "rm" (word)); + return word; +} +static inline u32 __fls(u32 word) +{ + asm("bsr %1,%0" + : "=r" (word) + : "rm" (word)); + return word; +} + +static inline u32 getesp(void) { + u32 esp; + asm("movl %%esp, %0" : "=rm"(esp)); + return esp; +} + +static inline void outb(u8 value, u16 port) { + __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port)); +} +static inline void outw(u16 value, u16 port) { + __asm__ __volatile__("outw %w0, %w1" : : "a"(value), "Nd"(port)); +} +static inline void outl(u32 value, u16 port) { + __asm__ __volatile__("outl %0, %w1" : : "a"(value), "Nd"(port)); +} +static inline u8 inb(u16 port) { + u8 value; + __asm__ __volatile__("inb %w1, %b0" : "=a"(value) : "Nd"(port)); + return value; +} +static inline u16 inw(u16 port) { + u16 value; + __asm__ __volatile__("inw %w1, %w0" : "=a"(value) : "Nd"(port)); + return value; +} +static inline u32 inl(u16 port) { + u32 value; + __asm__ __volatile__("inl %w1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void insb(u16 port, u8 *data, u32 count) { + asm volatile("rep insb (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +static inline void insw(u16 port, u16 *data, u32 count) { + asm volatile("rep insw (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +static inline void insl(u16 port, u32 *data, u32 count) { + asm volatile("rep insl (%%dx), %%es:(%%edi)" + : "+c"(count), "+D"(data) : "d"(port) : "memory"); +} +// XXX - outs not limited to es segment +static inline void outsb(u16 port, u8 *data, u32 count) { + asm volatile("rep outsb %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} +static inline void outsw(u16 port, u16 *data, u32 count) { + asm volatile("rep outsw %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} +static inline void outsl(u16 port, u32 *data, u32 count) { + asm volatile("rep outsl %%es:(%%esi), (%%dx)" + : "+c"(count), "+S"(data) : "d"(port) : "memory"); +} + +static inline void writel(void *addr, u32 val) { + barrier(); + *(volatile u32 *)addr = val; +} +static inline void writew(void *addr, u16 val) { + barrier(); + *(volatile u16 *)addr = val; +} +static inline void writeb(void *addr, u8 val) { + barrier(); + *(volatile u8 *)addr = val; +} +static inline u32 readl(const void *addr) { + u32 val = *(volatile const u32 *)addr; + barrier(); + return val; +} +static inline u16 readw(const void *addr) { + u16 val = *(volatile const u16 *)addr; + barrier(); + return val; +} +static inline u8 readb(const void *addr) { + u8 val = *(volatile const u8 *)addr; + barrier(); + return val; +} + +// GDT bits +#define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set +#define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set +#define GDT_B (0x1ULL << 54) // Big flag +#define GDT_G (0x1ULL << 55) // Granularity flag +// GDT bits for segment base +#define GDT_BASE(v) ((((u64)(v) & 0xff000000) << 32) \ + | (((u64)(v) & 0x00ffffff) << 16)) +// GDT bits for segment limit (0-1Meg) +#define GDT_LIMIT(v) ((((u64)(v) & 0x000f0000) << 32) \ + | (((u64)(v) & 0x0000ffff) << 0)) +// GDT bits for segment limit (0-4Gig in 4K chunks) +#define GDT_GRANLIMIT(v) (GDT_G | GDT_LIMIT((v) >> 12)) + +struct descloc_s { + u16 length; + u32 addr; +} PACKED; + +static inline void sgdt(struct descloc_s *desc) { + asm("sgdtl %0" : "=m"(*desc)); +} +static inline void lgdt(struct descloc_s *desc) { + asm("lgdtl %0" : : "m"(*desc) : "memory"); +} + +static inline u8 get_a20(void) { + return (inb(PORT_A20) & A20_ENABLE_BIT) != 0; +} + +static inline u8 set_a20(u8 cond) { + u8 val = inb(PORT_A20); + outb((val & ~A20_ENABLE_BIT) | (cond ? A20_ENABLE_BIT : 0), PORT_A20); + return (val & A20_ENABLE_BIT) != 0; +} + +// x86.c +void cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); + +#endif // !__ASSEMBLY__ + +#endif // x86.h |