/** @file * Utility routines for calling the PCI BIOS. */ /* * Copyright (C) 2011-2015 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #include #include #include "biosint.h" #include "inlines.h" /** PCI BIOS functions. */ #define PCIBIOS_ID 0xb1 #define PCIBIOS_PCI_BIOS_PRESENT 0x01 #define PCIBIOS_FIND_PCI_DEVICE 0x02 #define PCIBIOS_FIND_CLASS_CODE 0x03 #define PCIBIOS_GENERATE_SPECIAL_CYCLE 0x06 #define PCIBIOS_READ_CONFIG_BYTE 0x08 #define PCIBIOS_READ_CONFIG_WORD 0x09 #define PCIBIOS_READ_CONFIG_DWORD 0x0a #define PCIBIOS_WRITE_CONFIG_BYTE 0x0b #define PCIBIOS_WRITE_CONFIG_WORD 0x0c #define PCIBIOS_WRITE_CONFIG_DWORD 0x0d #define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0x0e #define PCIBIOS_SET_PCI_IRQ 0x0f /** Status codes. */ #define SUCCESSFUL 0x00 #define FUNC_NOT_SUPPORTED 0x81 #define BAD_VENDOR_ID 0x83 #define DEVICE_NOT_FOUND 0x86 #define BAD_REGISTER_NUMBER 0x87 #define SET_FAILED 0x88 #define BUFFER_TOO_SMALL 0x89 #if VBOX_BIOS_CPU >= 80386 /* Warning: Destroys high bits of ECX. */ uint16_t pci_find_class(uint16_t op, uint32_t dev_class, uint16_t start_bdf); # pragma aux pci_find_class = \ ".386" \ "shl ecx, 16" \ "mov cx, dx" \ "int 0x1a" \ "cmp ah, 0" \ "je found" \ "mov bx, 0xffff" \ "found:" \ parm [ax] [cx dx] [si] value [bx]; #endif uint16_t pci_find_dev(uint16_t op, uint16_t dev_id, uint16_t ven_id, uint16_t start_bdf); #pragma aux pci_find_dev = \ "int 0x1a" \ "cmp ah, 0" \ "je found" \ "mov bx, 0xffff" \ "found:" \ parm [ax] [cx] [dx] [si] value [bx]; uint8_t pci_read_cfgb(uint16_t op, uint16_t bus_dev_fn, uint16_t reg); #pragma aux pci_read_cfgb = \ "int 0x1a" \ parm [ax] [bx] [di] value [cl]; uint16_t pci_read_cfgw(uint16_t op, uint16_t bus_dev_fn, uint16_t reg); #pragma aux pci_read_cfgw = \ "int 0x1a" \ parm [ax] [bx] [di] value [cx]; #if VBOX_BIOS_CPU >= 80386 /* Warning: Destroys high bits of ECX. */ uint32_t pci_read_cfgd(uint16_t op, uint16_t bus_dev_fn, uint16_t reg); # pragma aux pci_read_cfgd = \ ".386" \ "int 0x1a" \ "mov ax, cx" \ "shr ecx, 16" \ parm [ax] [bx] [di] value [cx ax]; #endif uint8_t pci_write_cfgb(uint16_t op, uint16_t bus_dev_fn, uint16_t reg, uint8_t val); #pragma aux pci_write_cfgb = \ "int 0x1a" \ parm [ax] [bx] [di] [cl]; uint8_t pci_write_cfgw(uint16_t op, uint16_t bus_dev_fn, uint16_t reg, uint16_t val); #pragma aux pci_write_cfgw = \ "int 0x1a" \ parm [ax] [bx] [di] [cx]; #if VBOX_BIOS_CPU >= 80386 /* Warning: Destroys high bits of ECX. */ uint8_t pci_write_cfgd(uint16_t op, uint16_t bus_dev_fn, uint16_t reg, uint32_t val); # pragma aux pci_write_cfgd = \ ".386" \ "xchg cx, dx" \ "shl ecx, 16" \ "mov cx, dx" \ "int 0x1a" \ parm [ax] [bx] [di] [dx cx]; #endif /** * Returns the bus/device/function of a PCI device with * the given class code. * * @returns bus/device/fn in a 16-bit integer where * where the upper byte contains the bus number * and lower one the device and function number. * VBOX_AHCI_NO_DEVICE if no device was found. * @param dev_class The PCI class code to search for. */ uint16_t pci_find_classcode(uint32_t dev_class) { #if VBOX_BIOS_CPU >= 80386 return pci_find_class((PCIBIOS_ID << 8) | PCIBIOS_FIND_CLASS_CODE, dev_class, 0); #else return UINT16_C(0xffff); #endif } uint32_t pci_read_config_byte(uint8_t bus, uint8_t dev_fn, uint8_t reg) { return pci_read_cfgb((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_BYTE, (bus << 8) | dev_fn, reg); } uint32_t pci_read_config_word(uint8_t bus, uint8_t dev_fn, uint8_t reg) { return pci_read_cfgw((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_WORD, (bus << 8) | dev_fn, reg); } uint32_t pci_read_config_dword(uint8_t bus, uint8_t dev_fn, uint8_t reg) { #if VBOX_BIOS_CPU >= 80386 return pci_read_cfgd((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_DWORD, (bus << 8) | dev_fn, reg); #else return pci_read_cfgw((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_WORD, (bus << 8) | dev_fn, reg) || ((uint32_t)pci_read_cfgw((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_WORD, (bus << 8) | dev_fn, reg + 2) << 16); #endif } #if 0 /* Disabled to save space because they are not needed. Might become useful in the future. */ /** * Returns the bus/device/function of a PCI device with * the given vendor and device id. * * @returns bus/device/fn in one 16bit integer where * where the upper byte contains the bus number * and lower one the device and function number. * VBOX_AHCI_NO_DEVICE if no device was found. * @param u16Vendor The vendor ID. * @param u16Device The device ID. */ uint16_t pci_find_device(uint16_t v_id, uint16_t d_id) { return pci_find_dev((PCIBIOS_ID << 8) | PCIBIOS_FIND_PCI_DEVICE, v_id, d_id, 0); } void pci_write_config_byte(uint8_t bus, uint8_t dev_fn, uint8_t reg, uint8_t val) { pci_write_cfgb((PCIBIOS_ID << 8) | PCIBIOS_WRITE_CONFIG_BYTE, (bus << 8) | dev_fn, reg, val); } void pci_write_config_word(uint8_t bus, uint8_t dev_fn, uint8_t reg, uint16_t val) { pci_write_cfgw((PCIBIOS_ID << 8) | PCIBIOS_WRITE_CONFIG_WORD, (bus << 8) | dev_fn, reg, val); } void pci_write_config_dword(uint8_t bus, uint8_t dev_fn, uint8_t reg, uint32_t val) { pci_write_cfgd((PCIBIOS_ID << 8) | PCIBIOS_WRITE_CONFIG_DWORD, (bus << 8) | dev_fn, reg, val); } #endif /* 0 */