memtest86plus/system/pci.c
2021-12-05 14:15:37 +00:00

246 lines
6.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020-2021 Martin Whitaker.
//
// Derived from memtest86+ pci.c:
//
// MemTest86+ V5.00 Specific code (GPL V2.0)
// By Samuel DEMEULEMEESTER, sdemeule@memtest.org
// http://www.x86-secret.com - http://www.memtest.org
// ----------------------------------------------------
// pci.c - MemTest-86 Version 3.2
//
// Released under version 2 of the Gnu Public License.
// By Chris Brady
#include <stdbool.h>
#include <stdint.h>
#include "boot.h"
#include "bootparams.h"
#include "cpuid.h"
#include "io.h"
#include "pci.h"
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
#define PCI_CLASS_DEVICE 0x0a
#define PCI_CLASS_BRIDGE_HOST 0x0600
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
typedef enum {
PCI_CONFIG_TYPE_NONE = 0,
PCI_CONFIG_TYPE_1 = 1,
PCI_CONFIG_TYPE_2 = 2
} pci_config_type_t;
//------------------------------------------------------------------------------
// Private Variables
//------------------------------------------------------------------------------
static pci_config_type_t pci_config_type = PCI_CONFIG_TYPE_NONE;
//------------------------------------------------------------------------------
// Private Functions
//------------------------------------------------------------------------------
static bool pci_sanity_check(void)
{
// Do a trivial check to make certain we can see a host bridge.
// There are reportedly some buggy chipsets from Intel and
// Compaq where this test does not work, I will worry about
// that when we support them.
return pci_config_read16(0, 0, 0, PCI_CLASS_DEVICE) == PCI_CLASS_BRIDGE_HOST;
}
static void probe_config_type(void)
{
uint8_t tmpCFB;
uint32_t tmpCF8;
if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xf) {
pci_config_type = PCI_CONFIG_TYPE_1;
return;
}
// Check if configuration type 1 works.
pci_config_type = PCI_CONFIG_TYPE_1;
tmpCFB = inb(0xcfb);
outb(0x01, 0xcfb);
tmpCF8 = inl(0xcf8);
outl(0x80000000, 0xcf8);
if (inl(0xcf8) == 0x80000000 && pci_sanity_check()) {
outl(tmpCF8, 0xcf8);
outb(tmpCFB, 0xcfb);
return;
}
outl(tmpCF8, 0xcf8);
// Check if configuration type 2 works.
pci_config_type = PCI_CONFIG_TYPE_2;
outb(0x00, 0xcfb);
outb(0x00, 0xcf8);
outb(0x00, 0xcfa);
if (inb(0xcf8) == 0x00 && inb(0xcfa) == 0x00 && pci_sanity_check()) {
outb(tmpCFB, 0xcfb);
return;
}
outb(tmpCFB, 0xcfb);
// Nothing worked.
pci_config_type = PCI_CONFIG_TYPE_NONE;
}
static void set_pci_config1_addr(int bus, int dev, int func, int reg)
{
uint32_t addr = 0x80000000
| (reg & 0xf00) << 16
| (bus & 0xff) << 16
| (dev & 0x1f) << 11
| (func & 0x07) << 8
| (reg & 0xfc);
outl(addr, 0xcf8);
}
static void set_pci_config2_bus_func(int bus, int func)
{
outb(0xf0 | (func & 0x7) << 1, 0xcf8);
outb(bus, 0xcfa);
}
static int pci_config2_access_addr(int dev, int reg)
{
return 0xc000 | (dev & 0x1f) << 8 | (reg & 0xff);
}
//------------------------------------------------------------------------------
// Public Functions
//------------------------------------------------------------------------------
void pci_init(void)
{
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
if (boot_params->efi_info.loader_signature) {
// On UEFI systems we can assume configuration type 1.
pci_config_type = PCI_CONFIG_TYPE_1;
} else {
probe_config_type();
}
}
uint8_t pci_config_read8(int bus, int dev, int func, int reg)
{
uint8_t value;
switch (pci_config_type) {
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
return inb(0xcfc + (reg & 0x3));
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
value = inb(pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
return value;
default:
return 0xFF;
}
}
uint16_t pci_config_read16(int bus, int dev, int func, int reg)
{
uint16_t value;
switch (pci_config_type) {
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
return inw(0xcfc + (reg & 0x2));
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
value = inw(pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
return value;
default:
return 0xFFFF;
}
}
uint32_t pci_config_read32(int bus, int dev, int func, int reg)
{
uint32_t value;
switch (pci_config_type) {
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
return inl(0xcfc);
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
value = inl(pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
return value;
default:
return 0xFFFFFFFF;
}
}
void pci_config_write8(int bus, int dev, int func, int reg, uint8_t value)
{
switch (pci_config_type)
{
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
outb(value, 0xcfc + (reg & 0x3));
break;
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
outb(value, pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
break;
default:
break;
}
}
void pci_config_write16(int bus, int dev, int func, int reg, uint16_t value)
{
switch (pci_config_type)
{
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
outw(value, 0xcfc + (reg & 0x2));
break;
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
outw(value, pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
break;
default:
break;
}
}
void pci_config_write32(int bus, int dev, int func, int reg, uint32_t value)
{
switch (pci_config_type)
{
case PCI_CONFIG_TYPE_1:
set_pci_config1_addr(bus, dev, func, reg);
outl(value, 0xcfc);
break;
case PCI_CONFIG_TYPE_2:
set_pci_config2_bus_func(bus, func);
outl(value, pci_config2_access_addr(dev, reg));
outb(0, 0xcf8);
break;
default:
break;
}
}