From 307223a5a70770ad2e7610b0c7d257c96d333497 Mon Sep 17 00:00:00 2001 From: Meisaka Yukara Date: Sun, 20 Sep 2015 14:57:47 +0900 Subject: [PATCH] Add PCI scanning functions and busmastering functions. This commit is joint work by Meisaka Yukara and Jonas 'Sortie' Termansen . --- kernel/include/sortix/kernel/pci.h | 36 ++++- kernel/pci.cpp | 220 ++++++++++++++++++++++++++--- 2 files changed, 232 insertions(+), 24 deletions(-) diff --git a/kernel/include/sortix/kernel/pci.h b/kernel/include/sortix/kernel/pci.h index edc2d034..1fd767d0 100644 --- a/kernel/include/sortix/kernel/pci.h +++ b/kernel/include/sortix/kernel/pci.h @@ -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 diff --git a/kernel/pci.cpp b/kernel/pci.cpp index 758daa82..25687b4e 100644 --- a/kernel/pci.cpp +++ b/kernel/pci.cpp @@ -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 #include #include +#include #include 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() { }