Add PCI scanning functions and busmastering functions.

This commit is joint work by Meisaka Yukara <Meisaka.Yukara@gmail.com> and
Jonas 'Sortie' Termansen <sortie@maxsi.org>.
This commit is contained in:
Meisaka Yukara 2015-09-20 14:57:47 +09:00 committed by Jonas 'Sortie' Termansen
parent 970bf0c5e3
commit 307223a5a7
2 changed files with 232 additions and 24 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -40,15 +40,28 @@ typedef struct
} pcitype_t;
// memset(&pcifind, 255, sizeof(pcifind)) and fill out rest.
typedef struct
struct pcifind_t
{
uint16_t deviceid;
public:
pcifind_t() {}
constexpr pcifind_t(void* context_, uint16_t vendorid_, uint16_t deviceid_,
uint8_t classid_ = 0xff, uint8_t subclassid_ = 0xff,
uint8_t progif_ = 0xff, uint8_t revid_ = 0xff) :
context(context_), vendorid(vendorid_), deviceid(deviceid_),
classid(classid_), subclassid(subclassid_), progif(progif_),
revid(revid_)
{
}
public:
void* context;
uint16_t vendorid;
uint16_t deviceid;
uint8_t classid;
uint8_t subclassid;
uint8_t progif;
uint8_t revid;
} pcifind_t;
};
static const uint8_t PCIBAR_TYPE_IOSPACE = 0x0 << 1 | 0x1 << 0;
static const uint8_t PCIBAR_TYPE_16BIT = 0x1 << 1 | 0x0 << 0;
@ -146,6 +159,14 @@ void Write32(uint32_t devaddr, uint8_t off, uint32_t val); // Host endian
void WriteRaw32(uint32_t devaddr, uint8_t off, uint32_t val); // PCI endian
pciid_t GetDeviceId(uint32_t devaddr);
pcitype_t GetDeviceType(uint32_t devaddr);
void Search(bool (*callback)(uint32_t devaddr,
const pciid_t* id,
const pcitype_t* type,
void* context,
void* pattern_context),
void* context,
const pcifind_t* patterns,
size_t pattern_count);
uint32_t SearchForDevices(pcifind_t pcifind, uint32_t last = 0);
pcibar_t GetBAR(uint32_t devaddr, uint8_t bar);
pcibar_t GetExpansionROM(uint32_t devaddr);
@ -153,6 +174,13 @@ void EnableExpansionROM(uint32_t devaddr);
void DisableExpansionROM(uint32_t devaddr);
bool IsExpansionROMEnabled(uint32_t devaddr);
uint8_t SetupInterruptLine(uint32_t devaddr);
void EnableBusMaster(uint32_t devaddr);
void DisableBusMaster(uint32_t devaddr);
void EnableMemoryWrite(uint32_t devaddr);
void DisableMemoryWrite(uint32_t devaddr);
void EnableInterruptLine(uint32_t devaddr);
void DisableInterruptLine(uint32_t devaddr);
uint8_t GetInterruptIndex(uint32_t devaddr);
} // namespace PCI
} // namespace Sortix

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
* Copyright (c) 2011-2017 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -25,6 +25,7 @@
#include <sortix/kernel/ioport.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/pci.h>
namespace Sortix {
@ -135,28 +136,155 @@ pcitype_t GetDeviceType(uint32_t devaddr)
return ret;
}
static bool MatchesSearchCriteria(uint32_t devaddr, pcifind_t pcifind)
static void MakeCoarsePattern(pcifind_t* coarse,
const pcifind_t* patterns,
size_t pattern_count)
{
if ( pattern_count < 1 )
{
memset(coarse, 255, sizeof(*coarse));
return;
}
const pcifind_t* first = patterns;
coarse->vendorid = first->vendorid;
coarse->deviceid = first->deviceid;
coarse->classid = first->classid;
coarse->subclassid = first->subclassid;
coarse->progif = first->progif;
coarse->revid = first->revid;
for ( size_t i = 1; i < pattern_count; i++ )
{
const pcifind_t* pattern = patterns + i;
if ( coarse->vendorid != pattern->vendorid )
coarse->vendorid = 0xffff;
if ( coarse->deviceid != pattern->deviceid )
coarse->deviceid = 0xffff;
if ( coarse->classid != pattern->classid )
coarse->classid = 0xff;
if ( coarse->subclassid != pattern->subclassid )
coarse->subclassid = 0xff;
if ( coarse->progif != pattern->progif )
coarse->progif = 0xff;
if ( coarse->revid != pattern->revid )
coarse->revid = 0xff;
}
}
static bool MatchesPattern(const pciid_t* id,
const pcitype_t* type,
const pcifind_t* pattern)
{
if ( id->vendorid == 0xFFFF && id->deviceid == 0xFFFF )
return false;
if ( pattern->vendorid != 0xFFFF && id->vendorid != pattern->vendorid )
return false;
if ( pattern->deviceid != 0xFFFF && id->deviceid != pattern->deviceid )
return false;
if ( pattern->classid != 0xFF && type->classid != pattern->classid )
return false;
if ( pattern->subclassid != 0xFF &&
type->subclassid != pattern->subclassid )
return false;
if ( pattern->progif != 0xFF && type->progif != pattern->progif )
return false;
if ( pattern->revid != 0xFF && type->revid != pattern->revid )
return false;
return true;
}
static const pcifind_t* MatchesPatterns(const pciid_t* id,
const pcitype_t* type,
const pcifind_t* patterns,
size_t pattern_count)
{
if ( id->vendorid == 0xFFFF || id->deviceid == 0xFFFF )
return NULL;
for ( size_t i = 0; i < pattern_count; i++ )
{
const pcifind_t* pattern = &patterns[i];
if ( MatchesPattern(id, type, pattern) )
return pattern;
}
return NULL;
}
static bool SearchBus(bool (*callback)(uint32_t,
const pciid_t*,
const pcitype_t*,
void*,
void*),
void* context,
const pcifind_t* coarse_pattern,
const pcifind_t* patterns,
size_t pattern_count,
uint8_t bus)
{
for ( unsigned int slot = 0; slot < 32; slot++ )
{
unsigned int num_functions = 1;
for ( unsigned int function = 0; function < num_functions; function++ )
{
uint32_t devaddr = MakeDevAddr(bus, slot, function);
pciid_t id = GetDeviceId(devaddr);
pcitype_t type = GetDeviceType(devaddr);
uint8_t header = Read8(devaddr, PCIFIELD_HEADER_TYPE);
if ( header & 0x80 ) // Multi function device.
num_functions = 8;
if ( (header & 0x7F) == 0x01 ) // PCI to PCI bus.
{
uint8_t subbusid = Read8(devaddr, PCIFIELD_SECONDARY_BUS_NUMBER);
bool search = SearchBus(callback, context, coarse_pattern,
patterns, pattern_count, subbusid);
if ( !search )
return false;
}
// Do a coarse pattern before the more detailed one to save time.
if ( 1 < pattern_count &&
!MatchesPattern(&id, &type, coarse_pattern) )
continue;
const pcifind_t* pattern =
MatchesPatterns(&id, &type, patterns, pattern_count);
if ( !pattern )
continue;
// Unlock PCI in this scope to allow the callback to lock and change
// settings. Stop the search if the callback fails.
kthread_mutex_unlock(&pci_lock);
bool continue_search =
callback(devaddr, &id, &type, context, pattern->context);
kthread_mutex_lock(&pci_lock);
if ( !continue_search )
return false;
}
}
return true;
}
void Search(bool (*callback)(uint32_t,
const pciid_t*,
const pcitype_t*,
void*,
void*),
void* context,
const pcifind_t* patterns,
size_t pattern_count)
{
pcifind_t coarse_pattern;
MakeCoarsePattern(&coarse_pattern, patterns, pattern_count);
ScopedLock lock(&pci_lock);
SearchBus(callback, context, &coarse_pattern, patterns, pattern_count, 0);
}
static bool MatchesPatternByDevAddr(uint32_t devaddr, const pcifind_t* pcifind)
{
pciid_t id = GetDeviceId(devaddr);
if ( id.vendorid == 0xFFFF && id.deviceid == 0xFFFF )
return false;
pcitype_t type = GetDeviceType(devaddr);
if ( pcifind.vendorid != 0xFFFF && id.vendorid != pcifind.vendorid )
return false;
if ( pcifind.deviceid != 0xFFFF && id.deviceid != pcifind.deviceid )
return false;
if ( pcifind.classid != 0xFF && type.classid != pcifind.classid )
return false;
if ( pcifind.subclassid != 0xFF && type.subclassid != pcifind.subclassid )
return false;
if ( pcifind.progif != 0xFF && type.progif != pcifind.progif )
return false;
if ( pcifind.revid != 0xFF && type.revid != pcifind.revid )
return false;
return true;
return MatchesPattern(&id, &type, pcifind);
}
// TODO: This iterates the whole PCI device tree on each call!
// TODO: This iterates the whole PCI device tree on each call! Transition the
// callers to use the new callback API and delete this API.
static uint32_t SearchForDevicesOnBus(uint8_t bus, pcifind_t pcifind, uint32_t last = 0)
{
bool found_any_device = false;
@ -170,7 +298,7 @@ static uint32_t SearchForDevicesOnBus(uint8_t bus, pcifind_t pcifind, uint32_t l
uint32_t devaddr = MakeDevAddr(bus, slot, function);
if ( last < devaddr &&
(!found_any_device || devaddr < next_device) &&
MatchesSearchCriteria(devaddr, pcifind) )
MatchesPatternByDevAddr(devaddr, &pcifind) )
next_device = devaddr, found_any_device = true;
uint8_t header = Read8(devaddr, PCIFIELD_HEADER_TYPE);
if ( header & 0x80 ) // Multi function device.
@ -262,21 +390,18 @@ pcibar_t GetExpansionROM(uint32_t devaddr)
void EnableExpansionROM(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
PCI::Write32(devaddr, 0x30, PCI::Read32(devaddr, 0x30) | 0x1);
}
void DisableExpansionROM(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
PCI::Write32(devaddr, 0x30, PCI::Read32(devaddr, 0x30) & ~UINT32_C(0x1));
}
bool IsExpansionROMEnabled(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
return PCI::Read32(devaddr, 0x30) & 0x1;
}
@ -300,6 +425,61 @@ uint8_t SetupInterruptLine(uint32_t devaddr)
return Interrupt::IRQ0 + line;
}
void EnableBusMaster(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command | PCIFIELD_COMMAND_BUS_MASTER);
}
void DisableBusMaster(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command & ~PCIFIELD_COMMAND_BUS_MASTER);
}
void EnableMemoryWrite(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command | PCIFIELD_COMMAND_MEMORY_WRITE_AND_INVALIDATE);
}
void DisableMemoryWrite(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command & ~PCIFIELD_COMMAND_MEMORY_WRITE_AND_INVALIDATE);
}
void EnableInterruptLine(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command & ~PCIFIELD_COMMAND_INTERRUPT_DISABLE);
}
void DisableInterruptLine(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint16_t command = PCI::Read16(devaddr, PCIFIELD_COMMAND);
PCI::Write16(devaddr, PCIFIELD_COMMAND,
command | PCIFIELD_COMMAND_INTERRUPT_DISABLE);
}
uint8_t GetInterruptIndex(uint32_t devaddr)
{
ScopedLock lock(&pci_lock);
uint32_t line = PCI::Read8(devaddr, PCIFIELD_INTERRUPT_LINE) & 0xf;
return Interrupt::IRQ0 + line;
}
void Init()
{
}