/** @file
* Utility routines for calling the PCI BIOS.
*/
/*
* Copyright (C) 2011 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 <stdint.h>
#include <string.h>
#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
/* 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];
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];
/* 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];
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];
/* 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];
/**
* 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)
{
return pci_find_class((PCIBIOS_ID << 8) | PCIBIOS_FIND_CLASS_CODE, dev_class, 0);
}
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)
{
return pci_read_cfgd((PCIBIOS_ID << 8) | PCIBIOS_READ_CONFIG_DWORD, (bus << 8) | dev_fn, reg);
}
#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 */