[dev][bus][pci] major refactor of the PCI bus driver
-Add a bus manager level, which is an object oriented walk of the pci busses to build a per device object for later manipulation. -Add features to enable MSI interrupts. -Extend generic interrupt api to allow the platform to allocate vectors for MSI interrupts. -Rearrange a bit of the pc platform for the platform api changes. -Add PC platform support for using the local apic to EOI MSI vectors. -Fix up a few existing PCI drivers for small API changes. -Add a few stubbed out routines for non PC platforms that use PCI.
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
#include <dev/bus/pci.h>
|
||||
#include <lk/trace.h>
|
||||
|
||||
#include "pci_priv.h"
|
||||
#include "../pci_priv.h"
|
||||
|
||||
#if ARCH_X86_32
|
||||
// Only actually supported on x86-32
|
||||
@@ -26,6 +26,19 @@
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
/*
|
||||
* PCI BIOS access return codes
|
||||
*/
|
||||
// TODO: write routine to convert these to LK errors
|
||||
#define _PCI_SUCCESSFUL 0x00
|
||||
#define _PCI_FUNC_NOT_SUPPORTED 0x81
|
||||
#define _PCI_BAD_VENDOR_ID 0x83
|
||||
#define _PCI_DEVICE_NOT_FOUND 0x86
|
||||
#define _PCI_BAD_REGISTER_NUMBER 0x87
|
||||
#define _PCI_SET_FAILED 0x88
|
||||
#define _PCI_BUFFER_TOO_SMALL 0x89
|
||||
|
||||
|
||||
#define PCIBIOS_PRESENT 0xB101
|
||||
#define PCIBIOS_FIND_PCI_DEVICE 0xB102
|
||||
#define PCIBIOS_FIND_PCI_CLASS_CODE 0xB103
|
||||
@@ -179,59 +192,12 @@ pci_bios32 *pci_bios32::detect() {
|
||||
return b32;
|
||||
}
|
||||
|
||||
int pci_bios32::find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
|
||||
int pci_bios32::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
__asm__(
|
||||
"lcall *(%%edi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
"xor %%ah,%%ah \n"
|
||||
"1:"
|
||||
: "=b"(bx),
|
||||
"=a"(ret)
|
||||
: "1"(PCIBIOS_FIND_PCI_DEVICE),
|
||||
"c"(device_id),
|
||||
"d"(vendor_id),
|
||||
"S"(index),
|
||||
"D"(&bios32_entry_)
|
||||
: "cc", "memory");
|
||||
|
||||
state->bus = bx >> 8;
|
||||
state->dev_fn = bx & 0xFF;
|
||||
|
||||
ret >>= 8;
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
__asm__(
|
||||
"lcall *(%%edi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
"xor %%ah,%%ah \n"
|
||||
"1:"
|
||||
: "=b"(bx),
|
||||
"=a"(ret)
|
||||
: "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
|
||||
"c"(class_code),
|
||||
"S"(index),
|
||||
"D"(&bios32_entry_)
|
||||
: "cc", "memory");
|
||||
|
||||
state->bus = bx >> 8;
|
||||
state->dev_fn = bx & 0xFF;
|
||||
|
||||
ret >>= 8;
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -248,12 +214,12 @@ int pci_bios32::read_config_byte(const pci_location_t *state, uint32_t reg, uint
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
|
||||
int pci_bios32::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -270,12 +236,12 @@ int pci_bios32::read_config_half(const pci_location_t *state, uint32_t reg, uint
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
|
||||
int pci_bios32::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -292,12 +258,12 @@ int pci_bios32::read_config_word(const pci_location_t *state, uint32_t reg, uint
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
|
||||
int pci_bios32::write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -314,12 +280,12 @@ int pci_bios32::write_config_byte(const pci_location_t *state, uint32_t reg, uin
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
|
||||
int pci_bios32::write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -336,12 +302,12 @@ int pci_bios32::write_config_half(const pci_location_t *state, uint32_t reg, uin
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
|
||||
int pci_bios32::write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
|
||||
uint32_t bx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx = state.bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
bx |= (state.dev << 3) | state.fn;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
@@ -358,47 +324,4 @@ int pci_bios32::write_config_word(const pci_location_t *state, uint32_t reg, uin
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
int pci_bios32::get_irq_routing_options(irq_routing_options_t *options, uint16_t *pci_irqs) {
|
||||
uint32_t ret;
|
||||
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
"xor %%ah,%%ah \n"
|
||||
"1:"
|
||||
: "=b"(*pci_irqs),
|
||||
"=a"(ret)
|
||||
: "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
|
||||
"b"(0),
|
||||
"D"(options),
|
||||
"S"(&bios32_entry_)
|
||||
: "cc", "memory");
|
||||
ret >>= 8;
|
||||
return ret & 0xff;
|
||||
}
|
||||
|
||||
int pci_bios32::set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
|
||||
uint32_t bx, cx, ret;
|
||||
|
||||
bx = state->bus;
|
||||
bx <<= 8;
|
||||
bx |= state->dev_fn;
|
||||
cx = irq;
|
||||
cx <<= 8;
|
||||
cx |= int_pin;
|
||||
__asm__(
|
||||
"lcall *(%%esi) \n\t"
|
||||
"jc 1f \n\t"
|
||||
"xor %%ah,%%ah \n"
|
||||
"1:"
|
||||
: "=a"(ret)
|
||||
: "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
|
||||
"b"(bx),
|
||||
"c"(cx),
|
||||
"S"(&bios32_entry_)
|
||||
: "cc", "memory");
|
||||
ret >>= 8;
|
||||
return ret & 0xFF;
|
||||
}
|
||||
|
||||
#endif
|
||||
51
dev/bus/pci/backend/bios32.h
Normal file
51
dev/bus/pci/backend/bios32.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "pci_backend.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class pci_bios32 final : public pci_backend {
|
||||
#if ARCH_X86_32
|
||||
public:
|
||||
virtual ~pci_bios32() = default;
|
||||
|
||||
// factory to detect and create an instance
|
||||
static pci_bios32 *detect();
|
||||
|
||||
// a few overridden methods
|
||||
virtual int read_config_byte(pci_location_t state, uint32_t reg, uint8_t *value) override;
|
||||
virtual int read_config_half(pci_location_t state, uint32_t reg, uint16_t *value) override;
|
||||
virtual int read_config_word(pci_location_t state, uint32_t reg, uint32_t *value) override;
|
||||
|
||||
virtual int write_config_byte(pci_location_t state, uint32_t reg, uint8_t value) override;
|
||||
virtual int write_config_half(pci_location_t state, uint32_t reg, uint16_t value) override;
|
||||
virtual int write_config_word(pci_location_t state, uint32_t reg, uint32_t value) override;
|
||||
|
||||
private:
|
||||
// far call structure used by BIOS32 routines
|
||||
struct bios32_entry {
|
||||
uint32_t offset;
|
||||
uint16_t selector;
|
||||
} __PACKED;
|
||||
|
||||
// only created via the detect() factory
|
||||
explicit pci_bios32(bios32_entry b32_entry) : bios32_entry_(b32_entry) {}
|
||||
|
||||
bios32_entry bios32_entry_ {};
|
||||
|
||||
#else // !ARCH_X86_32
|
||||
|
||||
// not present on anything but x86-32
|
||||
public:
|
||||
static pci_bios32 *detect() { return nullptr; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <kernel/vm.h>
|
||||
#endif
|
||||
|
||||
#include "pci_priv.h"
|
||||
#include "../pci_priv.h"
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
@@ -84,17 +84,22 @@ status_t pci_ecam::initialize() {
|
||||
}
|
||||
|
||||
// compute the offset into the ecam given the location and register offset
|
||||
inline size_t location_to_offset(const pci_location_t *state, uint32_t reg) {
|
||||
inline size_t location_to_offset(const pci_location_t state, uint32_t reg) {
|
||||
//
|
||||
// | 27 - 20 | 19 - 15 | 14 - 12 | 11 - 8 | 7 - 2 | 1 - 0 |
|
||||
// | Bus Nr | Dev Nr | Function Nr | Ext. Register Nr | Register Nr | Byte Enable |
|
||||
|
||||
// TODO: clamp or assert on invalid offset
|
||||
size_t offset = (size_t)state->bus << 20;
|
||||
offset += (size_t)state->dev_fn << 12;
|
||||
size_t offset = (size_t)state.bus << 20;
|
||||
offset += (size_t)state.dev << 15;
|
||||
offset += (size_t)state.fn << 12;
|
||||
offset += reg;
|
||||
return offset;
|
||||
}
|
||||
|
||||
// templatized routines to access the pci config space using a specific type
|
||||
template <typename T>
|
||||
inline int read_config(const pci_location_t *state, uint32_t reg, T *value, const uint8_t *ecam_ptr) {
|
||||
inline int read_config(const pci_location_t state, uint32_t reg, T *value, const uint8_t *ecam_ptr) {
|
||||
auto off = location_to_offset(state, reg);
|
||||
|
||||
*value = *reinterpret_cast<const volatile T *>(&ecam_ptr[off]);
|
||||
@@ -103,7 +108,7 @@ inline int read_config(const pci_location_t *state, uint32_t reg, T *value, cons
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline int write_config(const pci_location_t *state, uint32_t reg, T value, uint8_t *ecam_ptr) {
|
||||
inline int write_config(const pci_location_t state, uint32_t reg, T value, uint8_t *ecam_ptr) {
|
||||
auto off = location_to_offset(state, reg);
|
||||
|
||||
*reinterpret_cast<volatile T *>(&ecam_ptr[off]) = value;
|
||||
@@ -111,32 +116,32 @@ inline int write_config(const pci_location_t *state, uint32_t reg, T value, uint
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int pci_ecam::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return read_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
|
||||
int pci_ecam::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return read_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
|
||||
int pci_ecam::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return read_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
|
||||
int pci_ecam::write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return write_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
|
||||
int pci_ecam::write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return write_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
|
||||
int pci_ecam::write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
int pci_ecam::write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
return write_config(state, reg, value, ecam_ptr_);
|
||||
}
|
||||
@@ -17,12 +17,12 @@ public:
|
||||
static pci_ecam *detect(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus);
|
||||
|
||||
// a few overridden methods
|
||||
int read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) override;
|
||||
int read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) override;
|
||||
int read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) override;
|
||||
int write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) override;
|
||||
int write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) override;
|
||||
int write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) override;
|
||||
int read_config_byte(pci_location_t state, uint32_t reg, uint8_t *value) override;
|
||||
int read_config_half(pci_location_t state, uint32_t reg, uint16_t *value) override;
|
||||
int read_config_word(pci_location_t state, uint32_t reg, uint32_t *value) override;
|
||||
int write_config_byte(pci_location_t state, uint32_t reg, uint8_t value) override;
|
||||
int write_config_half(pci_location_t state, uint32_t reg, uint16_t value) override;
|
||||
int write_config_word(pci_location_t state, uint32_t reg, uint32_t value) override;
|
||||
|
||||
private:
|
||||
// only created via the detect() factory
|
||||
54
dev/bus/pci/backend/pci_backend.h
Normal file
54
dev/bus/pci/backend/pci_backend.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <lk/compiler.h>
|
||||
#include <lk/cpp.h>
|
||||
#include <lk/err.h>
|
||||
#include <dev/bus/pci.h>
|
||||
|
||||
// Default implementation of a particular PCI bus/config accessor method.
|
||||
// Intended to be subclassed and specialized based on specific method.
|
||||
|
||||
class pci_backend {
|
||||
public:
|
||||
constexpr pci_backend() = default;
|
||||
virtual ~pci_backend() = default;
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(pci_backend);
|
||||
|
||||
public:
|
||||
int get_last_bus() const { return last_bus_; }
|
||||
|
||||
// virtuals that a concrete implementation should override
|
||||
virtual int read_config_byte(pci_location_t state, uint32_t reg, uint8_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int read_config_half(pci_location_t state, uint32_t reg, uint16_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int read_config_word(pci_location_t state, uint32_t reg, uint32_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
virtual int write_config_byte(pci_location_t state, uint32_t reg, uint8_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int write_config_half(pci_location_t state, uint32_t reg, uint16_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int write_config_word(pci_location_t state, uint32_t reg, uint32_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
protected:
|
||||
// detection should find the last bus
|
||||
void set_last_bus(int last_bus) { last_bus_ = last_bus; }
|
||||
int last_bus_ = -1;
|
||||
};
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <dev/bus/pci.h>
|
||||
#include <lk/trace.h>
|
||||
|
||||
#include "pci_priv.h"
|
||||
#include "../pci_priv.h"
|
||||
|
||||
#if ARCH_X86
|
||||
// Only supported on x86
|
||||
@@ -80,21 +80,21 @@ pci_type1 *pci_type1::detect() {
|
||||
return t1;
|
||||
}
|
||||
|
||||
int pci_type1::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
|
||||
LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
*value = type1_read_half(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
|
||||
int pci_type1::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
*value = type1_read_half(state.bus, state.dev, state.fn, reg);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int pci_type1::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
|
||||
LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
*value = type1_read_half(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
|
||||
int pci_type1::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
*value = type1_read_half(state.bus, state.dev, state.fn, reg);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
int pci_type1::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
|
||||
LTRACEF("state bus %#hhx dev_fn %#hhx reg %#x\n", state->bus, state->dev_fn, reg);
|
||||
*value = type1_read_word(state->bus, state->dev_fn >> 3, state->dev_fn & 0x7, reg);
|
||||
int pci_type1::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
|
||||
LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
|
||||
*value = type1_read_word(state.bus, state.dev, state.fn, reg);
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ public:
|
||||
static pci_type1 *detect();
|
||||
|
||||
// a few overridden methods
|
||||
int read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) override;
|
||||
int read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) override;
|
||||
int read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) override;
|
||||
int read_config_byte(pci_location_t state, uint32_t reg, uint8_t *value) override;
|
||||
int read_config_half(pci_location_t state, uint32_t reg, uint16_t *value) override;
|
||||
int read_config_word(pci_location_t state, uint32_t reg, uint32_t *value) override;
|
||||
|
||||
private:
|
||||
// only created via the detect() factory
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "pci_backend.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class pci_bios32 final : public pci_backend {
|
||||
#if ARCH_X86_32
|
||||
public:
|
||||
virtual ~pci_bios32() = default;
|
||||
|
||||
// factory to detect and create an instance
|
||||
static pci_bios32 *detect();
|
||||
|
||||
// a few overridden methods
|
||||
virtual int find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) override;
|
||||
virtual int find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) override;
|
||||
|
||||
virtual int read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) override;
|
||||
virtual int read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) override;
|
||||
virtual int read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) override;
|
||||
|
||||
virtual int write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) override;
|
||||
virtual int write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) override;
|
||||
virtual int write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) override;
|
||||
|
||||
virtual int get_irq_routing_options(irq_routing_options_t *options, uint16_t *pci_irqs) override;
|
||||
virtual int set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) override;
|
||||
|
||||
private:
|
||||
// far call structure used by BIOS32 routines
|
||||
struct bios32_entry {
|
||||
uint32_t offset;
|
||||
uint16_t selector;
|
||||
} __PACKED;
|
||||
|
||||
// only created via the detect() factory
|
||||
explicit pci_bios32(bios32_entry b32_entry) : bios32_entry_(b32_entry) {}
|
||||
|
||||
bios32_entry bios32_entry_ {};
|
||||
|
||||
#else // !ARCH_X86_32
|
||||
|
||||
// not present on anything but x86-32
|
||||
public:
|
||||
static pci_bios32 *detect() { return nullptr; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
834
dev/bus/pci/bus_mgr.cpp
Normal file
834
dev/bus/pci/bus_mgr.cpp
Normal file
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiseblrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "bus_mgr.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <lk/cpp.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/list.h>
|
||||
#include <lk/trace.h>
|
||||
#include <dev/bus/pci.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <platform/interrupts.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
namespace pci {
|
||||
|
||||
// root of the pci bus
|
||||
class bus;
|
||||
bus *root = nullptr;
|
||||
list_node bus_list = LIST_INITIAL_VALUE(bus_list);
|
||||
|
||||
struct capability;
|
||||
|
||||
// generic pci device
|
||||
class device {
|
||||
public:
|
||||
device(pci_location_t loc, bus *bus) : loc_(loc), bus_(bus) {}
|
||||
virtual ~device();
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(device);
|
||||
|
||||
static status_t probe(pci_location_t loc, bus *bus, device **out_device, bool *out_multifunction);
|
||||
|
||||
status_t probe_capabilities();
|
||||
status_t init_msi_capability(capability *cap);
|
||||
status_t init_msix_capability(capability *cap);
|
||||
|
||||
status_t allocate_irq(uint *irq);
|
||||
status_t allocate_msi(size_t num_requested, uint *msi_base);
|
||||
|
||||
pci_location_t loc() const { return loc_; }
|
||||
const bus *get_bus() const { return bus_; }
|
||||
|
||||
uint16_t device_id() const { return config_.device_id; }
|
||||
uint16_t vendor_id() const { return config_.vendor_id; }
|
||||
uint8_t base_class() const { return config_.base_class; }
|
||||
uint8_t sub_class() const { return config_.sub_class; }
|
||||
uint8_t interface() const { return config_.program_interface; }
|
||||
uint8_t header_type() const { return config_.header_type & PCI_HEADER_TYPE_MASK; }
|
||||
|
||||
size_t read_bars(pci_bar_t bar[6]);
|
||||
|
||||
bool has_msi() const { return msi_cap_; }
|
||||
bool has_msix() const { return msix_cap_; }
|
||||
|
||||
virtual void dump(size_t indent = 0);
|
||||
|
||||
protected:
|
||||
// let the bus device directly manipulate our list node
|
||||
friend class bus;
|
||||
list_node node = LIST_INITIAL_CLEARED_VALUE;
|
||||
|
||||
pci_location_t loc_ = {};
|
||||
bus *bus_ = nullptr;
|
||||
|
||||
pci_config_t config_ = {};
|
||||
|
||||
// capability list
|
||||
list_node capability_list_ = LIST_INITIAL_VALUE(capability_list_);
|
||||
capability *msi_cap_ = nullptr;
|
||||
capability *msix_cap_ = nullptr;
|
||||
};
|
||||
|
||||
// bridge device, holds a list of busses that it is responsible for
|
||||
class bridge : public device {
|
||||
public:
|
||||
bridge(pci_location_t loc, bus *bus) : device(loc, bus) {}
|
||||
virtual ~bridge() = default;
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(bridge);
|
||||
|
||||
static status_t probe(pci_location_t loc, bus *bus, device **out_device);
|
||||
|
||||
void add_bus(bus *b) { secondary_bus_ = b; }
|
||||
|
||||
virtual void dump(size_t indent = 0);
|
||||
|
||||
private:
|
||||
bus *secondary_bus_ = nullptr;
|
||||
};
|
||||
|
||||
// bus device holds a list of devices and a reference to its bridge device
|
||||
class bus {
|
||||
public:
|
||||
explicit bus(pci_location_t loc, bridge *b) : loc_(loc), b_(b) {}
|
||||
virtual ~bus() = default;
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(bus);
|
||||
|
||||
static status_t probe(pci_location_t loc, bridge *bridge, bus **out_bus);
|
||||
|
||||
pci_location_t loc() const { return loc_; }
|
||||
uint bus_num() const { return loc().bus; }
|
||||
|
||||
void add_device(device *d);
|
||||
|
||||
void dump(size_t indent = 0);
|
||||
|
||||
list_node *list_node_ptr() { return &node; }
|
||||
|
||||
template <typename F>
|
||||
status_t for_every_device(F func);
|
||||
|
||||
// master list of busses for easy iteration
|
||||
list_node node = LIST_INITIAL_CLEARED_VALUE;
|
||||
|
||||
private:
|
||||
pci_location_t loc_ = {};
|
||||
bridge *b_ = nullptr;
|
||||
list_node child_devices_ = LIST_INITIAL_VALUE(child_devices_);
|
||||
};
|
||||
|
||||
struct capability {
|
||||
list_node node = LIST_INITIAL_CLEARED_VALUE;
|
||||
uint16_t config_offset = 0;
|
||||
uint16_t id = 0;
|
||||
|
||||
// simple accessors
|
||||
bool is_msi() const { return id == 0x5; }
|
||||
bool is_msix() const { return id == 0x11; }
|
||||
};
|
||||
|
||||
void bus::add_device(device *d) {
|
||||
// TODO: assert that no two devices have the same address
|
||||
list_add_tail(&child_devices_, &d->node);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// helper routines
|
||||
// iterate all devices on all busses with the functor
|
||||
template <typename F>
|
||||
status_t for_every_device_on_every_bus(F func) {
|
||||
status_t err = NO_ERROR;
|
||||
|
||||
bus *b;
|
||||
list_for_every_entry(&bus_list, b, bus, node) {
|
||||
err = b->for_every_device(func);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// return a pointer to the device that matches a particular location
|
||||
device *lookup_device_by_loc(pci_location_t loc) {
|
||||
device *ret = nullptr;
|
||||
|
||||
auto v = [&](device *d) -> status_t {
|
||||
if (d->loc() == loc) {
|
||||
ret = d;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
for_every_device_on_every_bus(v);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
device::~device() {
|
||||
LTRACE;
|
||||
|
||||
capability *cap;
|
||||
while ((cap = list_remove_head_type(&capability_list_, capability, node))) {
|
||||
delete cap;
|
||||
}
|
||||
}
|
||||
|
||||
// probe the device, return a new node and a bool if it's a multifunction device or not
|
||||
status_t device::probe(pci_location_t loc, bus *parent_bus, device **out_device, bool *out_multifunction) {
|
||||
status_t err;
|
||||
|
||||
*out_device = nullptr;
|
||||
*out_multifunction = false;
|
||||
|
||||
// read vendor id and see if this is a real device
|
||||
uint16_t vendor_id;
|
||||
err = pci_read_config_half(loc, PCI_CONFIG_VENDOR_ID, &vendor_id);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
if (vendor_id == 0xffff) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
char str[14];
|
||||
LTRACEF("something at %s\n", pci_loc_string(loc, str));
|
||||
|
||||
// read base and sub class
|
||||
uint8_t base_class;
|
||||
err = pci_read_config_byte(loc, PCI_CONFIG_CLASS_CODE_BASE, &base_class);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
uint8_t sub_class;
|
||||
err = pci_read_config_byte(loc, PCI_CONFIG_CLASS_CODE_SUB, &sub_class);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// read header type (0 or 1)
|
||||
uint8_t header_type;
|
||||
err = pci_read_config_byte(loc, PCI_CONFIG_HEADER_TYPE, &header_type);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// is it multifunction?
|
||||
bool possibly_multifunction = false;
|
||||
if (loc.fn == 0 && header_type & PCI_HEADER_TYPE_MULTI_FN) {
|
||||
possibly_multifunction = true;
|
||||
LTRACEF_LEVEL(2, "possibly multifunction\n");
|
||||
}
|
||||
|
||||
header_type &= PCI_HEADER_TYPE_MASK;
|
||||
|
||||
LTRACEF_LEVEL(2, "base:sub class %#hhx:%hhx\n", base_class, sub_class);
|
||||
|
||||
// if it's a bridge, probe that
|
||||
if (base_class == 0x6) { // XXX replace with #define
|
||||
// bridge
|
||||
if (sub_class == 0x4) { // PCI-PCI bridge, normal decode
|
||||
LTRACEF("found bridge, recursing\n");
|
||||
return bridge::probe(loc, parent_bus, out_device);
|
||||
}
|
||||
}
|
||||
|
||||
LTRACEF_LEVEL(2, "type %#hhx\n", header_type);
|
||||
|
||||
if (header_type != 0) {
|
||||
LTRACEF("type %d header on bridge we don't understand, skipping\n", header_type);
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// create a new device and pass it up
|
||||
device *d = new device(loc, parent_bus);
|
||||
|
||||
// try to read in the basic config space for this device
|
||||
err = pci_read_config(loc, &d->config_);
|
||||
if (err < 0) {
|
||||
delete d;
|
||||
return err;
|
||||
}
|
||||
|
||||
// probe the device's capabilities
|
||||
d->probe_capabilities();
|
||||
|
||||
// we know we're a device at this point, set multifunction or not
|
||||
*out_multifunction = possibly_multifunction;
|
||||
|
||||
// return the newly constructed device
|
||||
*out_device = d;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void device::dump(size_t indent) {
|
||||
for (size_t i = 0; i < indent; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
char str[14];
|
||||
printf("dev %s %04hx:%04hx\n", pci_loc_string(loc_, str), config_.vendor_id, config_.device_id);
|
||||
}
|
||||
|
||||
// walk the device's capability list, reading them in and creating sub objects per
|
||||
status_t device::probe_capabilities() {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc(), str));
|
||||
|
||||
// does this device have any capabilities?
|
||||
if ((config_.status & PCI_STATUS_NEW_CAPS) == 0) {
|
||||
// no capabilities, just move on
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t err;
|
||||
size_t cap_ptr = config_.type0.capabilities_ptr; // type 0 and 1 are at same offset
|
||||
for (;;) {
|
||||
if (cap_ptr == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// read the capability id
|
||||
uint8_t cap_id;
|
||||
err = pci_read_config_byte(loc(), cap_ptr, &cap_id);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LTRACEF("cap id %#x at offset %#zx\n", cap_id, cap_ptr);
|
||||
|
||||
// we only handle a few kinds of capabilities at the moment
|
||||
capability *cap = new capability;
|
||||
cap->id = cap_id;
|
||||
cap->config_offset = cap_ptr;
|
||||
|
||||
// add the cap to our list
|
||||
if (cap) {
|
||||
list_add_tail(&capability_list_, &cap->node);
|
||||
}
|
||||
|
||||
switch (cap_id) {
|
||||
case 0x5: { // MSI
|
||||
LTRACEF("MSI\n");
|
||||
if (init_msi_capability(cap) == NO_ERROR) {
|
||||
msi_cap_ = cap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x11: { // MSI-X
|
||||
LTRACEF("MSI-X\n");
|
||||
if (init_msix_capability(cap) == NO_ERROR) {
|
||||
msix_cap_ = cap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// read the next pointer
|
||||
uint8_t next_cap_ptr;
|
||||
err = pci_read_config_byte(loc(), cap_ptr + 1, &next_cap_ptr);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
cap_ptr = next_cap_ptr;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t device::init_msi_capability(capability *cap) {
|
||||
LTRACE_ENTRY;
|
||||
|
||||
DEBUG_ASSERT(cap->id == 0x5);
|
||||
|
||||
// plain MSI
|
||||
uint32_t cap_buf[6];
|
||||
pci_read_config_word(loc(), cap->config_offset, &cap_buf[0]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 4, &cap_buf[1]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 8, &cap_buf[2]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 12, &cap_buf[3]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 16, &cap_buf[4]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 20, &cap_buf[5]);
|
||||
//hexdump(cap_buf, sizeof(cap_buf));
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t device::init_msix_capability(capability *cap) {
|
||||
LTRACE_ENTRY;
|
||||
|
||||
DEBUG_ASSERT(cap->id == 0x11);
|
||||
|
||||
// MSI-X
|
||||
uint32_t cap_buf[3];
|
||||
pci_read_config_word(loc(), cap->config_offset, &cap_buf[0]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 4, &cap_buf[1]);
|
||||
pci_read_config_word(loc(), cap->config_offset + 8, &cap_buf[2]);
|
||||
//hexdump(cap_buf, sizeof(cap_buf));
|
||||
|
||||
// TODO: we dont really support msi-x right now
|
||||
return ERR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
status_t device::allocate_irq(uint *irq) {
|
||||
LTRACE_ENTRY;
|
||||
|
||||
uint8_t interrupt_line;
|
||||
status_t err = pci_read_config_byte(loc(), PCI_CONFIG_INTERRUPT_LINE, &interrupt_line);
|
||||
if (err != NO_ERROR) return err;
|
||||
|
||||
if (interrupt_line == 0) {
|
||||
return ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
// map the irq number in config space to platform vector space
|
||||
err = platform_pci_int_to_vector(interrupt_line, irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t device::allocate_msi(size_t num_requested, uint *msi_base) {
|
||||
LTRACE_ENTRY;
|
||||
|
||||
DEBUG_ASSERT(num_requested == 1);
|
||||
|
||||
if (!has_msi()) {
|
||||
return ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
DEBUG_ASSERT(msi_cap_ && msi_cap_->is_msi());
|
||||
|
||||
// ask the platform for interrupts
|
||||
uint vector_base;
|
||||
status_t err = platform_allocate_interrupts(num_requested, 1, &vector_base);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// compute the MSI message to construct
|
||||
uint64_t msi_address = 0;
|
||||
uint16_t msi_data = 0;
|
||||
#if ARCH_X86
|
||||
msi_data = (vector_base & 0xff) | (0<<15); // edge triggered
|
||||
msi_address = 0xfee0'0000 | (0 << 12); // cpu 0
|
||||
#else
|
||||
return ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
|
||||
// program it into the capability
|
||||
const uint16_t cap_offset = msi_cap_->config_offset;
|
||||
|
||||
uint16_t control;
|
||||
pci_read_config_half(loc(), cap_offset + 2, &control);
|
||||
pci_write_config_half(loc(), cap_offset + 2, control & ~(0x1)); // disable MSI
|
||||
pci_write_config_word(loc(), cap_offset + 4, msi_address & 0xffff'ffff); // lower 32bits
|
||||
if (control & (1<<7)) {
|
||||
// 64bit
|
||||
pci_write_config_word(loc(), cap_offset + 8, msi_address >> 32); // upper 32bits
|
||||
pci_write_config_half(loc(), cap_offset + 0xc, msi_data);
|
||||
} else {
|
||||
pci_write_config_half(loc(), cap_offset + 8, msi_data);
|
||||
}
|
||||
|
||||
// set up the control register and enable it
|
||||
control = 1; // NME/NMI = 1, no per vector masking, keep 64bit flag, enable
|
||||
pci_write_config_half(loc(), cap_offset + 2, control);
|
||||
|
||||
// write it back to the pci config in the interrupt line offset
|
||||
pci_write_config_byte(loc(), PCI_CONFIG_INTERRUPT_LINE, vector_base);
|
||||
|
||||
// pass back the allocated irq to the caller
|
||||
*msi_base = vector_base;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
size_t device::read_bars(pci_bar_t bar[6]) {
|
||||
size_t count = 0;
|
||||
|
||||
if (header_type() == 0) {
|
||||
for (int i=0; i < 6; i++) {
|
||||
bar[i].valid = false;
|
||||
uint64_t bar_addr = config_.type0.base_addresses[i];
|
||||
if (bar_addr & 0x1) {
|
||||
// io address
|
||||
bar[i].io = true;
|
||||
bar[i].addr = bar_addr & ~0x3;
|
||||
bar[i].size = 0; // XXX
|
||||
bar[i].valid = (bar[i].addr != 0);
|
||||
} else if ((bar_addr & 0b110) == 0) {
|
||||
// 32bit memory address
|
||||
bar[i].io = false;
|
||||
bar[i].addr = bar_addr & ~0xf;
|
||||
bar[i].size = 0; // XXX
|
||||
bar[i].valid = (bar[i].addr != 0);
|
||||
} else if ((bar_addr & 0b110) == 2) {
|
||||
// 64bit memory address
|
||||
if (i % 2) {
|
||||
// root of 64bit memory range can only be on 0, 2, 4 slot
|
||||
continue;
|
||||
}
|
||||
bar[i].io = false;
|
||||
bar[i].addr = bar_addr & ~0xf;
|
||||
bar[i].addr |= (uint64_t)config_.type0.base_addresses[i + 1] << 32;
|
||||
bar[i].size = 0; // XXX
|
||||
bar[i].valid = true;
|
||||
// mark the next entry as invalid
|
||||
i++;
|
||||
bar[i].valid = false;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
} else if (header_type() == 1) {
|
||||
PANIC_UNIMPLEMENTED;
|
||||
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// examine the bridge device, figuring out the bus range it controls and recurse
|
||||
status_t bridge::probe(pci_location_t loc, bus *parent_bus, device **out_device) {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc, str));
|
||||
|
||||
// read vendor id and see if this is a real device
|
||||
uint16_t vendor_id;
|
||||
status_t err = pci_read_config_half(loc, PCI_CONFIG_VENDOR_ID, &vendor_id);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
if (vendor_id == 0xffff) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// read header type (0 or 1)
|
||||
uint8_t header_type;
|
||||
err = pci_read_config_byte(loc, PCI_CONFIG_HEADER_TYPE, &header_type);
|
||||
if (err != NO_ERROR) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// we are a bridge to a new set of busses
|
||||
bridge *br = new bridge(loc, parent_bus);
|
||||
|
||||
// we only grok type 1 headers here
|
||||
err = pci_read_config(loc, &br->config_);
|
||||
if (err < 0) {
|
||||
delete br;
|
||||
return err;
|
||||
}
|
||||
|
||||
LTRACEF("primary bus %hhd secondary %hhd subordinate %hhd\n",
|
||||
br->config_.type1.primary_bus, br->config_.type1.secondary_bus,
|
||||
br->config_.type1.subordinate_bus);
|
||||
|
||||
// probe the bridge's capabilities
|
||||
br->probe_capabilities();
|
||||
|
||||
*out_device = br;
|
||||
|
||||
// start a scan of the secondary bus downstream of this.
|
||||
// via bridge devices on this bus, should find all of the subordinate busses.
|
||||
bus *new_bus;
|
||||
pci_location_t bus_location = {};
|
||||
bus_location.segment = loc.segment;
|
||||
bus_location.bus = br->config_.type1.secondary_bus;
|
||||
err = bus::probe(bus_location, br, &new_bus);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// add the bus to our list of children
|
||||
DEBUG_ASSERT(new_bus);
|
||||
br->add_bus(new_bus);
|
||||
|
||||
// add the bus to the global bus list
|
||||
list_add_tail(&bus_list, new_bus->list_node_ptr());
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void bridge::dump(size_t indent) {
|
||||
for (size_t i = 0; i < indent; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
char str[14];
|
||||
printf("bridge %s %04hx:%04hx child busses [%d..%d]\n", pci_loc_string(loc_, str),
|
||||
config_.vendor_id, config_.device_id,
|
||||
config_.type1.secondary_bus, config_.type1.subordinate_bus);
|
||||
|
||||
if (secondary_bus_) {
|
||||
secondary_bus_->dump(indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
// walk all devices on a bus recursively walking into any bridge and scanning those busses
|
||||
status_t bus::probe(pci_location_t loc, bridge *bridge, bus **out_bus) {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc, str));
|
||||
|
||||
status_t err;
|
||||
|
||||
// create a bus to hold any devices we find
|
||||
bus *b = new bus(loc, bridge);
|
||||
|
||||
// probe all functions on all 32 devices on this bus.
|
||||
// add any devices found to this bus
|
||||
for (uint dev = 0; dev < 32; dev++) {
|
||||
loc.dev = dev;
|
||||
for (uint fn = 0; fn < 8; fn++) {
|
||||
loc.fn = fn;
|
||||
|
||||
device *d;
|
||||
bool multifunction;
|
||||
err = device::probe(loc, b, &d, &multifunction);
|
||||
if (err < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
b->add_device(d);
|
||||
|
||||
// move on to the next device
|
||||
if (fn == 0 && !multifunction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_bus = b;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void bus::dump(size_t indent) {
|
||||
for (size_t i = 0; i < indent; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("bus %d\n", loc().bus);
|
||||
device *d;
|
||||
list_for_every_entry(&child_devices_, d, device, node) {
|
||||
d->dump(indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
// call the provided functor on every device in this bus
|
||||
template <typename F>
|
||||
status_t bus::for_every_device(F func) {
|
||||
status_t err = NO_ERROR;
|
||||
|
||||
device *d;
|
||||
list_for_every_entry(&child_devices_, d, device, node) {
|
||||
err = func(d);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
} // namespace pci
|
||||
|
||||
// C api, so outside of the namespace
|
||||
using namespace pci;
|
||||
|
||||
status_t pci_bus_mgr_init() {
|
||||
LTRACE_ENTRY;
|
||||
|
||||
// start drilling into the pci bus tree
|
||||
pci_location_t loc;
|
||||
|
||||
loc = {}; // start at 0:0:0.0
|
||||
|
||||
bus *b;
|
||||
// TODO: deal with root bus not having reference to bridge device
|
||||
status_t err = bus::probe(loc, nullptr, &b);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// if we found anything there should be at least an empty bus device
|
||||
DEBUG_ASSERT(b);
|
||||
root = b;
|
||||
list_add_tail(&bus_list, b->list_node_ptr());
|
||||
|
||||
// iterate over all the devices found
|
||||
printf("PCI dump:\n");
|
||||
root->dump(2);
|
||||
|
||||
#if 0
|
||||
printf("visit all devices\n");
|
||||
pci_bus_mgr_visit_devices([](pci_location_t _loc) {
|
||||
char str[14];
|
||||
printf("%s\n", pci_loc_string(_loc, str));
|
||||
});
|
||||
#endif
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// for every bus in the system, pass the visit routine to the device
|
||||
status_t pci_bus_mgr_visit_devices(pci_visit_routine routine, void *cookie) {
|
||||
auto v = [&](device *d) -> status_t {
|
||||
routine(d->loc(), cookie);
|
||||
return NO_ERROR;
|
||||
};
|
||||
|
||||
return for_every_device_on_every_bus(v);
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_find_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, size_t index) {
|
||||
LTRACEF("device_id dev %#hx vendor %#hx index %zu\n", device_id, vendor_id, index);
|
||||
|
||||
if (device_id == 0xffff && vendor_id == 0xffff) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
auto v = [&](device *d) -> status_t {
|
||||
|
||||
if (device_id != 0xffff && device_id != d->device_id())
|
||||
return NO_ERROR;
|
||||
if (vendor_id != 0xffff && vendor_id != d->vendor_id())
|
||||
return NO_ERROR;
|
||||
|
||||
if (index-- == 0) {
|
||||
char str[14];
|
||||
LTRACEF_LEVEL(2, "match at loc %s: device id %#hx vendor id %#hx\n", pci_loc_string(d->loc(), str), d->device_id(), d->vendor_id());
|
||||
*state = d->loc();
|
||||
return 1; // signals stop
|
||||
}
|
||||
return NO_ERROR;
|
||||
};
|
||||
|
||||
status_t err = for_every_device_on_every_bus(v);
|
||||
return (err > 0) ? NO_ERROR : ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_find_device_by_class(pci_location_t *state, uint8_t base_class, uint8_t sub_class, uint8_t interface, size_t index) {
|
||||
LTRACEF("class %#x sub %#x interface %#x index %zu\n", base_class, sub_class, interface, index);
|
||||
|
||||
if (sub_class == 0xff && interface == 0xff) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
auto v = [&](device *d) -> status_t {
|
||||
//LTRACEF_LEVEL(2, "class %#x\n", d->base_class());
|
||||
|
||||
if (base_class != d->base_class())
|
||||
return NO_ERROR;
|
||||
if (sub_class != 0xff && sub_class != d->sub_class())
|
||||
return NO_ERROR;
|
||||
if (interface != 0xff && interface != d->interface())
|
||||
return NO_ERROR;
|
||||
|
||||
if (index-- == 0) {
|
||||
char str[14];
|
||||
LTRACEF_LEVEL(2, "match at loc %s: class %#hhx sub %#hhx interface %hhx\n",
|
||||
pci_loc_string(d->loc(), str), d->base_class(), d->sub_class(), d->interface());
|
||||
*state = d->loc();
|
||||
return 1; // signals stop
|
||||
}
|
||||
return NO_ERROR;
|
||||
};
|
||||
|
||||
status_t err = for_every_device_on_every_bus(v);
|
||||
return (err > 0) ? NO_ERROR : ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_enable_device(const pci_location_t loc) {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc, str));
|
||||
|
||||
device *d = lookup_device_by_loc(loc);
|
||||
if (!d) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint16_t command;
|
||||
status_t err = pci_read_config_half(loc, PCI_CONFIG_COMMAND, &command);
|
||||
if (err != NO_ERROR) return err;
|
||||
LTRACEF("command reg %#x\n", command);
|
||||
command |= PCI_COMMAND_IO_EN | PCI_COMMAND_MEM_EN | PCI_COMMAND_BUS_MASTER_EN;
|
||||
err = pci_write_config_half(loc, PCI_CONFIG_COMMAND, command);
|
||||
if (err != NO_ERROR) return err;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_read_bars(const pci_location_t loc, pci_bar_t bar[6], size_t *count) {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc, str));
|
||||
|
||||
*count = 0;
|
||||
|
||||
device *d = lookup_device_by_loc(loc);
|
||||
if (!d) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
*count = d->read_bars(bar);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_allocate_msi(const pci_location_t loc, size_t num_requested, uint *irqbase) {
|
||||
char str[14];
|
||||
LTRACEF("%s num_request %zu\n", pci_loc_string(loc, str), num_requested);
|
||||
|
||||
*irqbase = 0;
|
||||
|
||||
device *d = lookup_device_by_loc(loc);
|
||||
if (!d) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!d->has_msi()) {
|
||||
return ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
return d->allocate_msi(num_requested, irqbase);
|
||||
}
|
||||
|
||||
status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase) {
|
||||
char str[14];
|
||||
LTRACEF("%s\n", pci_loc_string(loc, str));
|
||||
|
||||
*irqbase = 0;
|
||||
|
||||
device *d = lookup_device_by_loc(loc);
|
||||
if (!d) {
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return d->allocate_irq(irqbase);
|
||||
}
|
||||
|
||||
void pci_dump_bars(pci_bar_t bar[6], size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (bar[i].valid) {
|
||||
printf("BAR %zu: addr %#16llx size %#16zx io %d\n",
|
||||
i, bar[i].addr, bar[i].size, bar[i].io);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *pci_loc_string(pci_location_t loc, char out_str[14]) {
|
||||
snprintf(out_str, 14, "%04x:%02x:%02x.%1x", loc.segment, loc.bus, loc.dev, loc.fn);
|
||||
return out_str;
|
||||
}
|
||||
|
||||
11
dev/bus/pci/bus_mgr.h
Normal file
11
dev/bus/pci/bus_mgr.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiseblrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dev/bus/pci.h>
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lk/compiler.h>
|
||||
#include <lk/err.h>
|
||||
#include <platform.h>
|
||||
#include <dev/bus/pci.h>
|
||||
#include <lk/console_cmd.h>
|
||||
@@ -21,67 +22,57 @@
|
||||
*/
|
||||
static void pci_list(void) {
|
||||
pci_location_t state;
|
||||
uint16_t device_id, vendor_id;
|
||||
uint8_t header_type;
|
||||
uint8_t base_class, sub_class, interface;
|
||||
int busses = 0, devices = 0, lines = 0, devfn, ret;
|
||||
int c;
|
||||
int busses = 0, devices = 0, lines = 0, ret;
|
||||
|
||||
printf("Scanning...\n");
|
||||
printf("Scanning (brute force)...\n");
|
||||
|
||||
for (int bus = 0; bus <= (int)pci_get_last_bus(); bus++) {
|
||||
busses++;
|
||||
for (int segment = 0; segment <= (int)pci_get_last_segment(); segment++) {
|
||||
state.segment = segment;
|
||||
|
||||
state.bus = bus;
|
||||
for (int bus = 0; bus <= (int)pci_get_last_bus(); bus++) {
|
||||
state.bus = bus;
|
||||
busses++;
|
||||
|
||||
for (devfn = 0; devfn < 256; devfn++) {
|
||||
state.dev_fn = devfn;
|
||||
for (int dev = 0; dev < 32; dev++) {
|
||||
state.dev = dev;
|
||||
|
||||
ret = pci_read_config_half(&state, PCI_CONFIG_VENDOR_ID, &vendor_id);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
for (int fn = 0; fn < 8; fn++) {
|
||||
state.fn = fn;
|
||||
|
||||
ret = pci_read_config_half(&state, PCI_CONFIG_DEVICE_ID, &device_id);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
uint16_t vendor_id;
|
||||
ret = pci_read_config_half(state, PCI_CONFIG_VENDOR_ID, &vendor_id);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
if (vendor_id == 0xffff && fn == 0) {
|
||||
// skip this device now before bothering to read the rest of the
|
||||
// configuration in.
|
||||
break;
|
||||
}
|
||||
|
||||
ret = pci_read_config_byte(&state, PCI_CONFIG_HEADER_TYPE, &header_type);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
pci_config_t config;
|
||||
ret = pci_read_config(state, &config);
|
||||
if (ret != NO_ERROR) continue;
|
||||
|
||||
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_BASE, &base_class);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
if (config.vendor_id != 0xffff) {
|
||||
printf("%04x:%02x:%02x.%0x vendor_id=%04x device_id=%04x, header_type=%02x "
|
||||
"base_class=%02x, sub_class=%02x, interface=%02x, irq=%u\n",
|
||||
state.segment, state.bus, state.dev, state.fn,
|
||||
config.vendor_id, config.device_id, config.header_type, config.base_class,
|
||||
config.sub_class, config.program_interface, config.type0.interrupt_line);
|
||||
devices++;
|
||||
lines++;
|
||||
}
|
||||
|
||||
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_SUB, &sub_class);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
|
||||
ret = pci_read_config_byte(&state, PCI_CONFIG_CLASS_CODE_INTR, &interface);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
|
||||
if (vendor_id != 0xffff) {
|
||||
printf("%02x:%02x.%0x vendor_id=%04x device_id=%04x, header_type=%02x "
|
||||
"base_class=%02x, sub_class=%02x, interface=%02x\n",
|
||||
state.bus, state.dev_fn >> 3, state.dev_fn & 7,
|
||||
vendor_id, device_id, header_type, base_class, sub_class, interface);
|
||||
devices++;
|
||||
lines++;
|
||||
}
|
||||
|
||||
if (((devfn & 7) == 0) && ~header_type & PCI_HEADER_TYPE_MULTI_FN) {
|
||||
// this is not a multi-function device, so advance to the next device
|
||||
// only check when looking at function 0 of a device
|
||||
devfn |= 7;
|
||||
}
|
||||
|
||||
if (lines == 23) {
|
||||
printf("... press any key to continue, q to quit ...");
|
||||
while ((c = getchar()) < 0);
|
||||
printf("\n");
|
||||
lines = 0;
|
||||
|
||||
if (c == 'q' || c == 'Q') goto quit;
|
||||
if ((fn == 0) && ~config.header_type & PCI_HEADER_TYPE_MULTI_FN) {
|
||||
// this is not a multi-function device, so advance to the next device
|
||||
// only check when looking at function 0 of a device
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("... done. Scanned %d busses, %d device/functions\n", busses, devices);
|
||||
printf("... done. Scanned %d busses, found %d device/functions\n", busses, devices);
|
||||
quit:
|
||||
return;
|
||||
|
||||
@@ -100,100 +91,159 @@ static int pci_config(int argc, const console_cmd_args *argv) {
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (argc < 5) {
|
||||
if (argc < 6) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[2].str, "dump")) {
|
||||
loc.segment = 0;
|
||||
loc.bus = atoui(argv[3].str);
|
||||
loc.dev_fn = atoui(argv[4].str);
|
||||
loc.dev = atoui(argv[4].str);
|
||||
loc.fn = atoui(argv[5].str);
|
||||
|
||||
for (i=0; i < sizeof(pci_config_t); i++) {
|
||||
ret = pci_read_config_byte(&loc, i, (uint8_t *) &config + i);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
}
|
||||
|
||||
printf("Device at %02x:%02x.%1x vendor id=%04x device id=%04x\n", loc.bus,
|
||||
loc.dev_fn >> 3, loc.dev_fn & 7, config.vendor_id, config.device_id);
|
||||
printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x\n",
|
||||
config.command, config.status, config.program_interface,
|
||||
config.sub_class, config.base_class);
|
||||
|
||||
for (i=0; i < 6; i+=2) {
|
||||
printf("bar%d=%08x bar%d=%08x\n", i, config.base_addresses[i],
|
||||
i+1, config.base_addresses[i+1]);
|
||||
}
|
||||
} else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) {
|
||||
if (argc != 6) {
|
||||
if (pci_read_config(loc, &config) != NO_ERROR) {
|
||||
printf("error reading configuration\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Device at %04x:%02x:%02x.%1x vendor id=%04x device id=%04x\n", loc.segment, loc.bus,
|
||||
loc.dev, loc.fn, config.vendor_id, config.device_id);
|
||||
printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x htype=%02x\n",
|
||||
config.command, config.status, config.program_interface,
|
||||
config.sub_class, config.base_class, config.header_type & PCI_HEADER_TYPE_MASK);
|
||||
printf("int pin %u int line %u\n", config.type0.interrupt_pin, config.type0.interrupt_line);
|
||||
|
||||
uint8_t header_type = config.header_type & PCI_HEADER_TYPE_MASK;
|
||||
if (header_type == PCI_HEADER_TYPE_STANDARD) { // type 0
|
||||
for (i=0; i < 6; i+=2) {
|
||||
printf("bar%d=%08x bar%d=%08x\n", i, config.type0.base_addresses[i],
|
||||
i+1, config.type0.base_addresses[i+1]);
|
||||
}
|
||||
} else if (header_type == PCI_HEADER_TYPE_PCI_BRIDGE) { // type 1
|
||||
printf("primary bus=%02x secondary=%02x subordinate=%02x\n",
|
||||
config.type1.primary_bus, config.type1.secondary_bus, config.type1.subordinate_bus);
|
||||
for (i=0; i < 2; i+=2) {
|
||||
printf("bar%d=%08x bar%d=%08x\n", i, config.type1.base_addresses[i],
|
||||
i+1, config.type1.base_addresses[i+1]);
|
||||
}
|
||||
printf("mem base=%08x mem limit=%08x\n", config.type1.memory_base << 16,
|
||||
(config.type1.memory_limit << 16) | 0xf'ffff);
|
||||
switch ((config.type1.io_base & 0xf)) {
|
||||
case 0: // 16 bit io addressing
|
||||
printf("io base=%04x io limit=%04x\n", (config.type1.io_base & 0xf0) << 8,
|
||||
((config.type1.io_limit & 0xf0) << 8) | 0xfff );
|
||||
break;
|
||||
case 1: // 32 bit io addressing
|
||||
printf("io base=%08x io limit=%08x\n",
|
||||
(config.type1.io_base % 0xf0) << 8 | config.type1.io_base_upper << 16,
|
||||
((config.type1.io_limit & 0xf0) << 8) | 0xfff | config.type1.io_limit_upper << 16);
|
||||
break;
|
||||
}
|
||||
switch ((config.type1.prefetchable_memory_base & 0xf)) {
|
||||
case 0: // 32 bit prefetchable addressing
|
||||
printf("prefetchable base=%08x prefetchable limit=%08x\n",
|
||||
(config.type1.prefetchable_memory_base & 0xfff0) << 16,
|
||||
(config.type1.prefetchable_memory_limit & 0xfff0) << 16 | 0xf'ffff);
|
||||
break;
|
||||
case 1: // 64 bit prefetchable addressing
|
||||
printf("prefetchable base=%llx prefetchable limit=%llx\n",
|
||||
((uint64_t)config.type1.prefetchable_memory_base & 0xfff0) << 16 |
|
||||
((uint64_t)config.type1.prefetchable_base_upper << 32),
|
||||
((uint64_t)config.type1.prefetchable_memory_limit & 0xfff0) << 16 | 0xf'ffff |
|
||||
((uint64_t)config.type1.prefetchable_limit_upper << 32));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
hexdump8_ex(&config, sizeof(config), 0);
|
||||
} else if (!strcmp(argv[2].str, "hexdump")) {
|
||||
loc.segment = 0;
|
||||
loc.bus = atoui(argv[3].str);
|
||||
loc.dev_fn = atoui(argv[4].str);
|
||||
offset = atoui(argv[5].str);
|
||||
loc.dev = atoui(argv[4].str);
|
||||
loc.fn = atoui(argv[5].str);
|
||||
|
||||
if (pci_read_config(loc, &config) != NO_ERROR) {
|
||||
printf("error reading configuration\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hexdump8_ex(&config, sizeof(config), 0);
|
||||
} else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) {
|
||||
if (argc != 7) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
loc.segment = 0;
|
||||
loc.bus = atoui(argv[3].str);
|
||||
loc.dev = atoui(argv[4].str);
|
||||
loc.fn = atoui(argv[5].str);
|
||||
offset = atoui(argv[6].str);
|
||||
|
||||
switch (argv[2].str[1]) {
|
||||
case 'b': {
|
||||
uint8_t value;
|
||||
ret = pci_read_config_byte(&loc, offset, &value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
ret = pci_read_config_byte(loc, offset, &value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("byte at device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
printf("byte at device %04x:%02x:%02x.%1x config offset %04x: %02x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h': {
|
||||
uint16_t value;
|
||||
ret = pci_read_config_half(&loc, offset, &value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
ret = pci_read_config_half(loc, offset, &value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("half at device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
printf("half at device %04x:%02x:%02x.%1x config offset %04x: %04x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w': {
|
||||
uint32_t value;
|
||||
ret = pci_read_config_word(&loc, offset, &value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
ret = pci_read_config_word(loc, offset, &value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("word at device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
printf("word at device %04x:%02x:%02x.%1x config offset %04x: %08x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (!strcmp(argv[2].str, "mb") || !strcmp(argv[2].str, "mh") || !strcmp(argv[2].str, "mw")) {
|
||||
if (argc != 7) {
|
||||
if (argc != 8) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
loc.segment = 0;
|
||||
loc.bus = atoui(argv[3].str);
|
||||
loc.dev_fn = atoui(argv[4].str);
|
||||
offset = atoui(argv[5].str);
|
||||
loc.dev = atoui(argv[4].str);
|
||||
loc.fn = atoui(argv[5].str);
|
||||
offset = atoui(argv[6].str);
|
||||
|
||||
switch (argv[2].str[1]) {
|
||||
case 'b': {
|
||||
uint8_t value = atoui(argv[6].str);
|
||||
ret = pci_write_config_byte(&loc, offset, value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
uint8_t value = atoui(argv[7].str);
|
||||
ret = pci_write_config_byte(loc, offset, value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("byte to device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
printf("byte to device %04x:%02x:%02x.%1x config offset %04x: %02x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h': {
|
||||
uint16_t value = atoui(argv[6].str);
|
||||
ret = pci_write_config_half(&loc, offset, value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
uint16_t value = atoui(argv[7].str);
|
||||
ret = pci_write_config_half(loc, offset, value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("half to device %04x:%02x:%02x.%1x config offset %04x: %04x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
|
||||
printf("half to device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w': {
|
||||
uint32_t value = atoui(argv[6].str);
|
||||
ret = pci_write_config_word(&loc, offset, value);
|
||||
if (ret != _PCI_SUCCESSFUL) goto error;
|
||||
uint32_t value = atoui(argv[7].str);
|
||||
ret = pci_write_config_word(loc, offset, value);
|
||||
if (ret != NO_ERROR) goto error;
|
||||
|
||||
printf("word to device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
|
||||
printf("word to device %04x:%02x:%02x.%1x config offset %04x: %08x\n", loc.segment, loc.bus, loc.dev, loc.fn, offset, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -213,9 +263,10 @@ static int pci_cmd(int argc, const console_cmd_args *argv) {
|
||||
printf("pci commands:\n");
|
||||
usage:
|
||||
printf("%s list\n", argv[0].str);
|
||||
printf("%s config dump <bus> <devfn>\n", argv[0].str);
|
||||
printf("%s config <rb|rh|rw> <bus> <devfn> <offset>\n", argv[0].str);
|
||||
printf("%s config <mb|mh|mw> <bus> <devfn> <offset> <value>\n", argv[0].str);
|
||||
printf("%s config dump <bus> <dev> <fn>\n", argv[0].str);
|
||||
printf("%s config hexdump <bus> <dev> <fn>\n", argv[0].str);
|
||||
printf("%s config <rb|rh|rw> <bus> <dev> <fn> <offset>\n", argv[0].str);
|
||||
printf("%s config <mb|mh|mw> <bus> <dev> <fn> <offset> <value>\n", argv[0].str);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,125 +8,32 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <lk/compiler.h>
|
||||
|
||||
// pci level structures and defines
|
||||
#include <hw/pci.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
/*
|
||||
* PCI access return codes
|
||||
*/
|
||||
#define _PCI_SUCCESSFUL 0x00
|
||||
#define _PCI_FUNC_NOT_SUPPORTED 0x81
|
||||
#define _PCI_BAD_VENDOR_ID 0x83
|
||||
#define _PCI_DEVICE_NOT_FOUND 0x86
|
||||
#define _PCI_BAD_REGISTER_NUMBER 0x87
|
||||
#define _PCI_SET_FAILED 0x88
|
||||
#define _PCI_BUFFER_TOO_SMALL 0x89
|
||||
|
||||
/*
|
||||
* PCI configuration space offsets
|
||||
*/
|
||||
#define PCI_CONFIG_VENDOR_ID 0x00
|
||||
#define PCI_CONFIG_DEVICE_ID 0x02
|
||||
#define PCI_CONFIG_COMMAND 0x04
|
||||
#define PCI_CONFIG_STATUS 0x06
|
||||
#define PCI_CONFIG_REVISION_ID 0x08
|
||||
#define PCI_CONFIG_CLASS_CODE 0x09
|
||||
#define PCI_CONFIG_CLASS_CODE_INTR 0x09
|
||||
#define PCI_CONFIG_CLASS_CODE_SUB 0x0a
|
||||
#define PCI_CONFIG_CLASS_CODE_BASE 0x0b
|
||||
#define PCI_CONFIG_CACHE_LINE_SIZE 0x0c
|
||||
#define PCI_CONFIG_LATENCY_TIMER 0x0d
|
||||
#define PCI_CONFIG_HEADER_TYPE 0x0e
|
||||
#define PCI_CONFIG_BIST 0x0f
|
||||
#define PCI_CONFIG_BASE_ADDRESSES 0x10
|
||||
#define PCI_CONFIG_CARDBUS_CIS_PTR 0x28
|
||||
#define PCI_CONFIG_SUBSYS_VENDOR_ID 0x2c
|
||||
#define PCI_CONFIG_SUBSYS_ID 0x2e
|
||||
#define PCI_CONFIG_EXP_ROM_ADDRESS 0x30
|
||||
#define PCI_CONFIG_CAPABILITIES 0x34
|
||||
#define PCI_CONFIG_INTERRUPT_LINE 0x3c
|
||||
#define PCI_CONFIG_INTERRUPT_PIN 0x3d
|
||||
#define PCI_CONFIG_MIN_GRANT 0x3e
|
||||
#define PCI_CONFIG_MAX_LATENCY 0x3f
|
||||
|
||||
/*
|
||||
* PCI header type register bits
|
||||
*/
|
||||
#define PCI_HEADER_TYPE_MASK 0x7f
|
||||
#define PCI_HEADER_TYPE_MULTI_FN 0x80
|
||||
|
||||
/*
|
||||
* PCI header types
|
||||
*/
|
||||
#define PCI_HEADER_TYPE_STANDARD 0x00
|
||||
#define PCI_HEADER_TYPE_PCI_BRIDGE 0x01
|
||||
#define PCI_HEADER_TYPE_CARD_BUS 0x02
|
||||
|
||||
/*
|
||||
* PCI command register bits
|
||||
*/
|
||||
#define PCI_COMMAND_IO_EN 0x0001
|
||||
#define PCI_COMMAND_MEM_EN 0x0002
|
||||
#define PCI_COMMAND_BUS_MASTER_EN 0x0004
|
||||
#define PCI_COMMAND_SPECIAL_EN 0x0008
|
||||
#define PCI_COMMAND_MEM_WR_INV_EN 0x0010
|
||||
#define PCI_COMMAND_PAL_SNOOP_EN 0x0020
|
||||
#define PCI_COMMAND_PERR_RESP_EN 0x0040
|
||||
#define PCI_COMMAND_AD_STEP_EN 0x0080
|
||||
#define PCI_COMMAND_SERR_EN 0x0100
|
||||
#define PCI_COMMAND_FAST_B2B_EN 0x0200
|
||||
|
||||
/*
|
||||
* PCI status register bits
|
||||
*/
|
||||
#define PCI_STATUS_NEW_CAPS 0x0010
|
||||
#define PCI_STATUS_66_MHZ 0x0020
|
||||
#define PCI_STATUS_FAST_B2B 0x0080
|
||||
#define PCI_STATUS_MSTR_PERR 0x0100
|
||||
#define PCI_STATUS_DEVSEL_MASK 0x0600
|
||||
#define PCI_STATUS_TARG_ABORT_SIG 0x0800
|
||||
#define PCI_STATUS_TARG_ABORT_RCV 0x1000
|
||||
#define PCI_STATUS_MSTR_ABORT_RCV 0x2000
|
||||
#define PCI_STATUS_SERR_SIG 0x4000
|
||||
#define PCI_STATUS_PERR 0x8000
|
||||
|
||||
typedef struct {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint8_t revision_id_0;
|
||||
uint8_t program_interface;
|
||||
uint8_t sub_class;
|
||||
uint8_t base_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t latency_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t bist;
|
||||
uint32_t base_addresses[6];
|
||||
uint32_t cardbus_cis_ptr;
|
||||
uint16_t subsystem_vendor_id;
|
||||
uint16_t subsystem_id;
|
||||
uint32_t expansion_rom_address;
|
||||
uint8_t capabilities_ptr;
|
||||
uint8_t reserved_0[3];
|
||||
uint32_t reserved_1;
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint8_t min_grant;
|
||||
uint8_t max_latency;
|
||||
} __PACKED pci_config_t;
|
||||
|
||||
/*
|
||||
* PCI address structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t segment;
|
||||
uint8_t bus;
|
||||
uint8_t dev_fn;
|
||||
uint8_t dev;
|
||||
uint8_t fn;
|
||||
} pci_location_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t addr;
|
||||
size_t size;
|
||||
bool io;
|
||||
bool valid;
|
||||
} pci_bar_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t id;
|
||||
uint8_t next;
|
||||
@@ -147,6 +54,7 @@ typedef struct {
|
||||
uint8_t reserved;
|
||||
} __PACKED irq_routing_entry;
|
||||
|
||||
|
||||
// only use one of these two:
|
||||
// try to detect PCI based on legacy PC PCI accessor methods
|
||||
status_t pci_init_legacy(void);
|
||||
@@ -154,20 +62,82 @@ status_t pci_init_legacy(void);
|
||||
// try to detect PCI based on a known ecam base.
|
||||
status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, uint8_t end_bus);
|
||||
|
||||
// user facing C api
|
||||
int pci_get_last_bus(void);
|
||||
int pci_get_last_segment(void);
|
||||
|
||||
status_t pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
|
||||
status_t pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index);
|
||||
status_t pci_read_config(pci_location_t loc, pci_config_t *config);
|
||||
|
||||
status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value);
|
||||
status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value);
|
||||
status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value);
|
||||
status_t pci_read_config_byte(pci_location_t state, uint32_t reg, uint8_t *value);
|
||||
status_t pci_read_config_half(pci_location_t state, uint32_t reg, uint16_t *value);
|
||||
status_t pci_read_config_word(pci_location_t state, uint32_t reg, uint32_t *value);
|
||||
|
||||
status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value);
|
||||
status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value);
|
||||
status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value);
|
||||
status_t pci_write_config_byte(pci_location_t state, uint32_t reg, uint8_t value);
|
||||
status_t pci_write_config_half(pci_location_t state, uint32_t reg, uint16_t value);
|
||||
status_t pci_write_config_word(pci_location_t state, uint32_t reg, uint32_t value);
|
||||
|
||||
status_t pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs);
|
||||
status_t pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
|
||||
// pci bus manager
|
||||
// builds a list of devices and allows for various operations on the list
|
||||
|
||||
// C level visitor routine
|
||||
typedef void(*pci_visit_routine)(pci_location_t loc, void *cookie);
|
||||
status_t pci_bus_mgr_visit_devices(pci_visit_routine routine, void *cookie);
|
||||
|
||||
// must be called after pci_init_*();
|
||||
status_t pci_bus_mgr_init(void);
|
||||
|
||||
// Look for the Nth match of device id and vendor id.
|
||||
// Either device or vendor is skipped if set to 0xffff.
|
||||
// Error if both is set to 0xffff.
|
||||
status_t pci_bus_mgr_find_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, size_t index);
|
||||
|
||||
// Look for the Nth match of combination of base, subclass, and interface.
|
||||
// interface and subclass may be set to 0xff in which case it will skip.
|
||||
status_t pci_bus_mgr_find_device_by_class(pci_location_t *state, uint8_t base_class, uint8_t subclass, uint8_t interface, size_t index);
|
||||
|
||||
// set io and mem enable on the device
|
||||
status_t pci_bus_mgr_enable_device(const pci_location_t loc);
|
||||
|
||||
// read a list of up to 6 bars out of the device. each is marked with a valid bit
|
||||
status_t pci_bus_mgr_read_bars(const pci_location_t loc, pci_bar_t bar[6], size_t *count);
|
||||
|
||||
// try to allocate one or more msi vectors for this device
|
||||
status_t pci_bus_mgr_allocate_msi(const pci_location_t loc, size_t num_requested, uint *irqbase);
|
||||
|
||||
// allocate a regular irq for this device and return it in irqbase
|
||||
status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase);
|
||||
|
||||
// return a pointer to a formatted string
|
||||
const char *pci_loc_string(pci_location_t loc, char out_str[14]);
|
||||
void pci_dump_bars(pci_bar_t bar[6], size_t count);
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
#if __cplusplus
|
||||
|
||||
// C++ helper routine for pci_bus_mgr_visit_devices
|
||||
// Wrapper to convert lambdas and other function like things to the C api
|
||||
template <typename T>
|
||||
void pci_bus_mgr_visit_devices(T routine) {
|
||||
struct vdata {
|
||||
T &routine;
|
||||
};
|
||||
|
||||
auto v = [](pci_location_t loc, void *cookie) {
|
||||
vdata *data = static_cast<vdata *>(cookie);
|
||||
data->routine(loc);
|
||||
};
|
||||
|
||||
vdata data = { routine };
|
||||
pci_bus_mgr_visit_devices(v, &data);
|
||||
}
|
||||
|
||||
inline bool operator==(pci_location_t a, pci_location_t b) {
|
||||
return a.segment == b.segment &&
|
||||
a.bus == b.bus &&
|
||||
a.dev == b.dev &&
|
||||
a.fn == b.fn;
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ SpinLock lock;
|
||||
pci_backend *pcib = nullptr;
|
||||
} // namespace
|
||||
|
||||
/* user facing routines */
|
||||
int pci_get_last_bus() {
|
||||
if (!pcib) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
@@ -35,32 +36,12 @@ int pci_get_last_bus() {
|
||||
return pcib->get_last_bus();
|
||||
}
|
||||
|
||||
/* user facing routines */
|
||||
status_t pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
|
||||
LTRACEF("device_id dev %#hx vendor %#hx index %#hx\n", device_id, vendor_id, index);
|
||||
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
|
||||
int res = pcib->find_pci_device(state, device_id, vendor_id, index);
|
||||
|
||||
return res;
|
||||
int pci_get_last_segment() {
|
||||
// currently hard coded to 1 segment
|
||||
return 0;
|
||||
}
|
||||
|
||||
status_t pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
|
||||
LTRACEF("device_id class %#x index %#hx\n", class_code, index);
|
||||
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
|
||||
int res = pcib->find_pci_class_code(state, class_code, index);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
|
||||
status_t pci_read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -69,7 +50,7 @@ status_t pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t
|
||||
|
||||
return res;
|
||||
}
|
||||
status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
|
||||
status_t pci_read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -79,7 +60,7 @@ status_t pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
|
||||
status_t pci_read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -89,7 +70,7 @@ status_t pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
|
||||
status_t pci_write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -99,7 +80,7 @@ status_t pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
|
||||
status_t pci_write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -109,7 +90,7 @@ status_t pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
|
||||
status_t pci_write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
@@ -119,35 +100,159 @@ status_t pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
status_t pci_read_config(const pci_location_t loc, pci_config_t *config) {
|
||||
status_t err;
|
||||
|
||||
// TODO: highly bios32 specific, abstract this differently
|
||||
pci_backend::irq_routing_options_t options;
|
||||
options.size = sizeof(irq_routing_entry) * *count;
|
||||
options.selector = 0x10; // XXX actually DATA_SELECTOR
|
||||
options.offset = entries;
|
||||
*config = {};
|
||||
|
||||
int res;
|
||||
{
|
||||
AutoSpinLock guard(&lock);
|
||||
// TODO: handle endian swapping (if necessary)
|
||||
|
||||
res = pcib->get_irq_routing_options(&options, pci_irqs);
|
||||
// define some helper routines to read config offsets in the proper unit
|
||||
size_t next_index;
|
||||
auto read_byte = [&]() -> uint8_t {
|
||||
uint8_t val;
|
||||
err = pci_read_config_byte(loc, next_index, &val);
|
||||
next_index++;
|
||||
if (err < 0) return 0;
|
||||
return val;
|
||||
};
|
||||
|
||||
auto read_half = [&]() -> uint16_t {
|
||||
uint16_t val;
|
||||
err = pci_read_config_half(loc, next_index, &val);
|
||||
next_index += 2;
|
||||
if (err < 0) return 0;
|
||||
return val;
|
||||
};
|
||||
|
||||
auto read_word = [&]() -> uint32_t {
|
||||
uint32_t val;
|
||||
err = pci_read_config_word(loc, next_index, &val);
|
||||
next_index += 4;
|
||||
if (err < 0) return 0;
|
||||
return val;
|
||||
};
|
||||
|
||||
// shared, standard part of the pci config space
|
||||
/*
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint8_t revision_id_0;
|
||||
uint8_t program_interface;
|
||||
uint8_t sub_class;
|
||||
uint8_t base_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t latency_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t bist;
|
||||
*/
|
||||
next_index = 0;
|
||||
config->vendor_id = read_half(); if (err < 0) return err;
|
||||
config->device_id = read_half(); if (err < 0) return err;
|
||||
config->command = read_half(); if (err < 0) return err;
|
||||
config->status = read_half(); if (err < 0) return err;
|
||||
config->revision_id_0 = read_byte(); if (err < 0) return err;
|
||||
config->program_interface = read_byte(); if (err < 0) return err;
|
||||
config->sub_class = read_byte(); if (err < 0) return err;
|
||||
config->base_class = read_byte(); if (err < 0) return err;
|
||||
config->cache_line_size = read_byte(); if (err < 0) return err;
|
||||
config->latency_timer = read_byte(); if (err < 0) return err;
|
||||
config->header_type = read_byte(); if (err < 0) return err;
|
||||
config->bist = read_byte(); if (err < 0) return err;
|
||||
DEBUG_ASSERT(next_index == 0x10); // should have read this many bytes at this point
|
||||
|
||||
// based on the type field, read two different types
|
||||
const uint8_t type = config->header_type & 0x7f;
|
||||
if (type == 0) {
|
||||
/*
|
||||
uint32_t base_addresses[6];
|
||||
uint32_t cardbus_cis_ptr;
|
||||
uint16_t subsystem_vendor_id;
|
||||
uint16_t subsystem_id;
|
||||
uint32_t expansion_rom_address;
|
||||
uint8_t capabilities_ptr;
|
||||
uint8_t reserved_0[3];
|
||||
uint32_t reserved_1;
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint8_t min_grant;
|
||||
uint8_t max_latency;
|
||||
*/
|
||||
config->type0.base_addresses[0] = read_word(); if (err < 0) return err;
|
||||
config->type0.base_addresses[1] = read_word(); if (err < 0) return err;
|
||||
config->type0.base_addresses[2] = read_word(); if (err < 0) return err;
|
||||
config->type0.base_addresses[3] = read_word(); if (err < 0) return err;
|
||||
config->type0.base_addresses[4] = read_word(); if (err < 0) return err;
|
||||
config->type0.base_addresses[5] = read_word(); if (err < 0) return err;
|
||||
config->type0.cardbus_cis_ptr = read_word(); if (err < 0) return err;
|
||||
config->type0.subsystem_vendor_id = read_half(); if (err < 0) return err;
|
||||
config->type0.subsystem_id = read_half(); if (err < 0) return err;
|
||||
config->type0.expansion_rom_address = read_word(); if (err < 0) return err;
|
||||
config->type0.capabilities_ptr = read_byte(); if (err < 0) return err;
|
||||
next_index += 3 + 4; // 7 bytes of reserved space
|
||||
config->type0.interrupt_line = read_byte(); if (err < 0) return err;
|
||||
config->type0.interrupt_pin = read_byte(); if (err < 0) return err;
|
||||
config->type0.min_grant = read_byte(); if (err < 0) return err;
|
||||
config->type0.max_latency = read_byte(); if (err < 0) return err;
|
||||
DEBUG_ASSERT(next_index == 0x40); // should have read this many bytes at this point
|
||||
} else if (type == 1) {
|
||||
/*
|
||||
uint32_t base_addresses[2];
|
||||
uint8_t primary_bus;
|
||||
uint8_t secondary_bus;
|
||||
uint8_t subordinate_bus;
|
||||
uint8_t secondary_latency_timer;
|
||||
uint8_t io_base;
|
||||
uint8_t io_limit;
|
||||
uint16_t secondary_status;
|
||||
uint16_t memory_base;
|
||||
uint16_t memory_limit;
|
||||
uint16_t prefetchable_memory_base;
|
||||
uint16_t prefetchable_memory_limit;
|
||||
uint32_t prefetchable_base_upper;
|
||||
uint32_t prefetchable_limit_upper;
|
||||
uint16_t io_base_upper;
|
||||
uint16_t io_limit_upper;
|
||||
uint8_t capabilities_ptr;
|
||||
uint8_t reserved_0[3];
|
||||
uint32_t expansion_rom_address;
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint16_t bridge_control;
|
||||
*/
|
||||
config->type1.base_addresses[0] = read_word(); if (err < 0) return err;
|
||||
config->type1.base_addresses[1] = read_word(); if (err < 0) return err;
|
||||
config->type1.primary_bus = read_byte(); if (err < 0) return err;
|
||||
config->type1.secondary_bus = read_byte(); if (err < 0) return err;
|
||||
config->type1.subordinate_bus = read_byte(); if (err < 0) return err;
|
||||
config->type1.secondary_latency_timer = read_byte(); if (err < 0) return err;
|
||||
config->type1.io_base = read_byte(); if (err < 0) return err;
|
||||
config->type1.io_limit = read_byte(); if (err < 0) return err;
|
||||
config->type1.secondary_status = read_half(); if (err < 0) return err;
|
||||
config->type1.memory_base = read_half(); if (err < 0) return err;
|
||||
config->type1.memory_limit = read_half(); if (err < 0) return err;
|
||||
config->type1.prefetchable_memory_base = read_half(); if (err < 0) return err;
|
||||
config->type1.prefetchable_memory_limit = read_half(); if (err < 0) return err;
|
||||
config->type1.prefetchable_base_upper = read_word(); if (err < 0) return err;
|
||||
config->type1.prefetchable_limit_upper = read_word(); if (err < 0) return err;
|
||||
config->type1.io_base_upper = read_half(); if (err < 0) return err;
|
||||
config->type1.io_limit_upper = read_half(); if (err < 0) return err;
|
||||
config->type1.capabilities_ptr = read_byte(); if (err < 0) return err;
|
||||
next_index += 3; // 3 reserved bytes
|
||||
config->type1.expansion_rom_address = read_word(); if (err < 0) return err;
|
||||
config->type1.interrupt_line = read_byte(); if (err < 0) return err;
|
||||
config->type1.interrupt_pin = read_byte(); if (err < 0) return err;
|
||||
config->type1.bridge_control = read_half(); if (err < 0) return err;
|
||||
|
||||
DEBUG_ASSERT(next_index == 0x40); // should have read this many bytes at this point
|
||||
} else {
|
||||
// cant handle other types
|
||||
return ERR_NOT_VALID;
|
||||
}
|
||||
|
||||
*count = options.size / sizeof(irq_routing_entry);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
|
||||
if (!pcib) return ERR_NOT_CONFIGURED;
|
||||
|
||||
AutoSpinLock guard(&lock);
|
||||
|
||||
int res = pcib->set_irq_hw_int(state, int_pin, irq);
|
||||
|
||||
return res;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t pci_init_legacy() {
|
||||
@@ -161,6 +266,7 @@ status_t pci_init_legacy() {
|
||||
if ((pcib = pci_bios32::detect())) {
|
||||
dprintf(INFO, "PCI: pci bios functions installed\n");
|
||||
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
|
||||
pci_bus_mgr_init();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -168,6 +274,7 @@ status_t pci_init_legacy() {
|
||||
if ((pcib = pci_type1::detect())) {
|
||||
dprintf(INFO, "PCI: pci type1 functions installed\n");
|
||||
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
|
||||
pci_bus_mgr_init();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -183,6 +290,7 @@ status_t pci_init_ecam(paddr_t ecam_base, uint16_t segment, uint8_t start_bus, u
|
||||
if ((pcib = pci_ecam::detect(ecam_base, segment, start_bus, end_bus))) {
|
||||
dprintf(INFO, "PCI: pci ecam functions installed\n");
|
||||
dprintf(INFO, "PCI: last pci bus is %d\n", pcib->get_last_bus());
|
||||
pci_bus_mgr_init();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <lk/compiler.h>
|
||||
#include <lk/cpp.h>
|
||||
#include <lk/err.h>
|
||||
#include <dev/bus/pci.h>
|
||||
|
||||
// Default implementation of a particular PCI bus/config accessor method.
|
||||
// Intended to be subclassed and specialized based on specific method.
|
||||
|
||||
class pci_backend {
|
||||
public:
|
||||
constexpr pci_backend() = default;
|
||||
virtual ~pci_backend() = default;
|
||||
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(pci_backend);
|
||||
|
||||
public:
|
||||
int get_last_bus() const { return last_bus_; }
|
||||
|
||||
// virtuals that a concrete implementation should override
|
||||
virtual int find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
virtual int find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
virtual int read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
virtual int write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
// TODO: highly bios32 specific
|
||||
struct irq_routing_options_t {
|
||||
uint16_t size;
|
||||
void *offset;
|
||||
uint16_t selector;
|
||||
} __PACKED;
|
||||
|
||||
virtual int get_irq_routing_options(irq_routing_options_t *options, uint16_t *pci_irqs) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
virtual int set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
protected:
|
||||
// detection should find the last bus
|
||||
void set_last_bus(int last_bus) { last_bus_ = last_bus; }
|
||||
int last_bus_ = -1;
|
||||
};
|
||||
@@ -12,8 +12,9 @@
|
||||
#include <lk/compiler.h>
|
||||
#include <dev/bus/pci.h>
|
||||
|
||||
#include "bios32.h"
|
||||
#include "ecam.h"
|
||||
#include "type1.h"
|
||||
#include "backend/bios32.h"
|
||||
#include "backend/ecam.h"
|
||||
#include "backend/type1.h"
|
||||
|
||||
#include "bus_mgr.h"
|
||||
|
||||
|
||||
@@ -3,12 +3,17 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/bios32.cpp \
|
||||
$(LOCAL_DIR)/bus_mgr.cpp \
|
||||
$(LOCAL_DIR)/debug.cpp \
|
||||
$(LOCAL_DIR)/ecam.cpp \
|
||||
$(LOCAL_DIR)/pci.cpp \
|
||||
$(LOCAL_DIR)/type1.cpp \
|
||||
\
|
||||
$(LOCAL_DIR)/backend/ecam.cpp \
|
||||
$(LOCAL_DIR)/backend/bios32.cpp \
|
||||
$(LOCAL_DIR)/backend/type1.cpp \
|
||||
|
||||
MODULE_DEPS += lib/libcpp
|
||||
|
||||
MODULE_CPPFLAGS += -Wno-invalid-offsetof
|
||||
MODULE_COMPILEFLAGS += -Wmissing-declarations
|
||||
|
||||
include make/module.mk
|
||||
|
||||
212
dev/include/hw/pci.h
Normal file
212
dev/include/hw/pci.h
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Corey Tabaka
|
||||
* Copyright (c) 2021 Travis Geiseblrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <lk/compiler.h>
|
||||
|
||||
// This file contains defines and structures for the PCI bus independent
|
||||
// of any devices or drivers that may use them.
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
/*
|
||||
* PCI configuration space offsets
|
||||
*/
|
||||
#define PCI_CONFIG_VENDOR_ID 0x00
|
||||
#define PCI_CONFIG_DEVICE_ID 0x02
|
||||
#define PCI_CONFIG_COMMAND 0x04
|
||||
#define PCI_CONFIG_STATUS 0x06
|
||||
#define PCI_CONFIG_REVISION_ID 0x08
|
||||
#define PCI_CONFIG_CLASS_CODE 0x09
|
||||
#define PCI_CONFIG_CLASS_CODE_INTR 0x09
|
||||
#define PCI_CONFIG_CLASS_CODE_SUB 0x0a
|
||||
#define PCI_CONFIG_CLASS_CODE_BASE 0x0b
|
||||
#define PCI_CONFIG_CACHE_LINE_SIZE 0x0c
|
||||
#define PCI_CONFIG_LATENCY_TIMER 0x0d
|
||||
#define PCI_CONFIG_HEADER_TYPE 0x0e
|
||||
#define PCI_CONFIG_BIST 0x0f
|
||||
/* Type 0 */
|
||||
#define PCI_CONFIG_BASE_ADDRESSES 0x10
|
||||
#define PCI_CONFIG_CARDBUS_CIS_PTR 0x28
|
||||
#define PCI_CONFIG_SUBSYS_VENDOR_ID 0x2c
|
||||
#define PCI_CONFIG_SUBSYS_ID 0x2e
|
||||
#define PCI_CONFIG_EXP_ROM_ADDRESS 0x30
|
||||
#define PCI_CONFIG_CAPABILITIES 0x34
|
||||
#define PCI_CONFIG_INTERRUPT_LINE 0x3c
|
||||
#define PCI_CONFIG_INTERRUPT_PIN 0x3d
|
||||
#define PCI_CONFIG_MIN_GRANT 0x3e
|
||||
#define PCI_CONFIG_MAX_LATENCY 0x3f
|
||||
|
||||
/*
|
||||
* PCI header type register bits
|
||||
*/
|
||||
#define PCI_HEADER_TYPE_MASK 0x7f
|
||||
#define PCI_HEADER_TYPE_MULTI_FN 0x80
|
||||
|
||||
/*
|
||||
* PCI header types
|
||||
*/
|
||||
#define PCI_HEADER_TYPE_STANDARD 0x00
|
||||
#define PCI_HEADER_TYPE_PCI_BRIDGE 0x01
|
||||
#define PCI_HEADER_TYPE_CARD_BUS 0x02
|
||||
|
||||
/*
|
||||
* PCI command register bits
|
||||
*/
|
||||
#define PCI_COMMAND_IO_EN 0x0001
|
||||
#define PCI_COMMAND_MEM_EN 0x0002
|
||||
#define PCI_COMMAND_BUS_MASTER_EN 0x0004
|
||||
#define PCI_COMMAND_SPECIAL_EN 0x0008
|
||||
#define PCI_COMMAND_MEM_WR_INV_EN 0x0010
|
||||
#define PCI_COMMAND_PAL_SNOOP_EN 0x0020
|
||||
#define PCI_COMMAND_PERR_RESP_EN 0x0040
|
||||
#define PCI_COMMAND_AD_STEP_EN 0x0080
|
||||
#define PCI_COMMAND_SERR_EN 0x0100
|
||||
#define PCI_COMMAND_FAST_B2B_EN 0x0200
|
||||
|
||||
/*
|
||||
* PCI status register bits
|
||||
*/
|
||||
#define PCI_STATUS_NEW_CAPS 0x0010
|
||||
#define PCI_STATUS_66_MHZ 0x0020
|
||||
#define PCI_STATUS_FAST_B2B 0x0080
|
||||
#define PCI_STATUS_MSTR_PERR 0x0100
|
||||
#define PCI_STATUS_DEVSEL_MASK 0x0600
|
||||
#define PCI_STATUS_TARG_ABORT_SIG 0x0800
|
||||
#define PCI_STATUS_TARG_ABORT_RCV 0x1000
|
||||
#define PCI_STATUS_MSTR_ABORT_RCV 0x2000
|
||||
#define PCI_STATUS_SERR_SIG 0x4000
|
||||
#define PCI_STATUS_PERR 0x8000
|
||||
|
||||
/* structure version of the standard pci config space */
|
||||
typedef struct {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint8_t revision_id_0;
|
||||
uint8_t program_interface;
|
||||
uint8_t sub_class;
|
||||
uint8_t base_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t latency_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t bist;
|
||||
/* offset 0x10 */
|
||||
union {
|
||||
struct {
|
||||
uint32_t base_addresses[6];
|
||||
uint32_t cardbus_cis_ptr;
|
||||
uint16_t subsystem_vendor_id;
|
||||
uint16_t subsystem_id;
|
||||
uint32_t expansion_rom_address;
|
||||
uint8_t capabilities_ptr;
|
||||
uint8_t reserved_0[3];
|
||||
uint32_t reserved_1;
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint8_t min_grant;
|
||||
uint8_t max_latency;
|
||||
} type0; // configuration for normal devices
|
||||
struct {
|
||||
uint32_t base_addresses[2];
|
||||
uint8_t primary_bus;
|
||||
uint8_t secondary_bus;
|
||||
uint8_t subordinate_bus;
|
||||
uint8_t secondary_latency_timer;
|
||||
uint8_t io_base;
|
||||
uint8_t io_limit;
|
||||
uint16_t secondary_status;
|
||||
uint16_t memory_base;
|
||||
uint16_t memory_limit;
|
||||
uint16_t prefetchable_memory_base;
|
||||
uint16_t prefetchable_memory_limit;
|
||||
uint32_t prefetchable_base_upper;
|
||||
uint32_t prefetchable_limit_upper;
|
||||
uint16_t io_base_upper;
|
||||
uint16_t io_limit_upper;
|
||||
uint8_t capabilities_ptr;
|
||||
uint8_t reserved_0[3];
|
||||
uint32_t expansion_rom_address;
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint16_t bridge_control;
|
||||
} type1; // configuration for bridge devices
|
||||
};
|
||||
} __PACKED pci_config_t;
|
||||
static_assert(sizeof(pci_config_t) == 0x40, "");
|
||||
|
||||
/* Class/subclass codes (incomplete) */
|
||||
#define PCI_SUBCLASS_OTHER 0x80 // common 'other' in many of the subclasses
|
||||
|
||||
#define PCI_CLASS_UNCLASSIFIED 0x0
|
||||
#define PCI_SUBCLASS_NON_VGA_UNCLASSIFIED 0x0
|
||||
#define PCI_SUBCLASS_VGA_UNCLASSIFIED 0x1
|
||||
|
||||
#define PCI_CLASS_MASS_STORAGE 0x1
|
||||
#define PCI_SUBCLASS_SCSI 0x0
|
||||
#define PCI_SUBCLASS_IDE 0x1
|
||||
#define PCI_SUBCLASS_FLOPPY 0x2
|
||||
#define PCI_SUBCLASS_IPI 0x3
|
||||
#define PCI_SUBCLASS_RAID 0x4
|
||||
#define PCI_SUBCLASS_ATA 0x5
|
||||
#define PCI_SUBCLASS_SERIAL_ATA 0x6
|
||||
#define PCI_SUBCLASS_SAS 0x7
|
||||
#define PCI_SUBCLASS_NON_VOLATILE 0x8
|
||||
|
||||
#define PCI_CLASS_NETWORK 0x2
|
||||
#define PCI_SUBCLASS_ETHERNET 0x0
|
||||
#define PCI_SUBCLASS_TOKEN_RING 0x1
|
||||
#define PCI_SUBCLASS_FDDI 0x2
|
||||
#define PCI_SUBCLASS_ATM 0x3
|
||||
#define PCI_SUBCLASS_ISDN 0x4
|
||||
#define PCI_SUBCLASS_WORLDFIP 0x5
|
||||
#define PCI_SUBCLASS_PICMG 0x6
|
||||
#define PCI_SUBCLASS_INFINIBAND 0x7
|
||||
|
||||
#define PCI_CLASS_DISPLAY 0x3
|
||||
#define PCI_SUBCLASS_VGA 0x0
|
||||
#define PCI_SUBCLASS_XGA 0x1
|
||||
#define PCI_SUBCLASS_3D 0x2
|
||||
|
||||
#define PCI_CLASS_MULTIMEDIA 0x4
|
||||
#define PCI_CLASS_MEMORY 0x5
|
||||
|
||||
#define PCI_CLASS_BRIDGE 0x6
|
||||
#define PCI_SUBCLASS_HOST_BRIDGE 0x0
|
||||
#define PCI_SUBCLASS_ISA_BRIDGE 0x1
|
||||
#define PCI_SUBCLASS_EISA_BRIDGE 0x2
|
||||
#define PCI_SUBCLASS_MCA_BRIDGE 0x3
|
||||
#define PCI_SUBCLASS_PCI_PCI_BRIDGE 0x4
|
||||
#define PCI_SUBCLASS_PCMCIA_BRIDGE 0x5
|
||||
#define PCI_SUBCLASS_NUBUS_BRIDGE 0x6
|
||||
#define PCI_SUBCLASS_CARDBUS_BRIDGE 0x7
|
||||
#define PCI_SUBCLASS_RACEWAY_BRIDGE 0x8
|
||||
#define PCI_SUBCLASS_PCI_PCI_BRIDGE2 0x9
|
||||
#define PCI_SUBCLASS_INFINIBAND_TO_PCI_BRIDGE 0xa
|
||||
|
||||
#define PCI_CLASS_SIMPLE_COMMS 0x7
|
||||
#define PCI_CLASS_BASE_PERIPH 0x8
|
||||
#define PCI_CLASS_INPUT 0x9
|
||||
#define PCI_CLASS_DOCKING_STATION 0xa
|
||||
#define PCI_CLASS_PROCESSOR 0xb
|
||||
#define PCI_CLASS_SERIAL_BUS 0xc
|
||||
#define PCI_CLASS_WIRELESS 0xd
|
||||
#define PCI_CLASS_INTELLIGENT_CONTROLLER 0xe
|
||||
#define PCI_CLASS_SATELLITE 0xf
|
||||
#define PCI_CLASS_ENCRYPTION 0x10
|
||||
#define PCI_CLASS_SIGNAL_PROCESSING 0x11
|
||||
#define PCI_CLASS_PROCESSING_ACCEL 0x12
|
||||
#define PCI_CLASS_COPROCESSOR 0x40
|
||||
#define PCI_CLASS_UNASSIGNED 0xff
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
@@ -70,7 +70,7 @@ struct pcnet_state {
|
||||
};
|
||||
|
||||
static status_t pcnet_init(struct device *dev);
|
||||
static status_t pcnet_read_pci_config(struct device *dev, pci_location_t *loc);
|
||||
static status_t pcnet_read_pci_config(struct device *dev, pci_location_t loc);
|
||||
|
||||
static enum handler_return pcnet_irq_handler(void *arg);
|
||||
|
||||
@@ -136,7 +136,7 @@ static status_t pcnet_init(struct device *dev) {
|
||||
if (!config)
|
||||
return ERR_NOT_CONFIGURED;
|
||||
|
||||
if (pci_find_pci_device(&loc, config->device_id, config->vendor_id, config->index) != _PCI_SUCCESSFUL) {
|
||||
if (pci_bus_mgr_find_device(&loc, config->device_id, config->vendor_id, config->index) != _PCI_SUCCESSFUL) {
|
||||
TRACEF("device not found\n");
|
||||
return ERR_NOT_FOUND;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ static status_t pcnet_init(struct device *dev) {
|
||||
|
||||
dev->state = state;
|
||||
|
||||
res = pcnet_read_pci_config(dev, &loc);
|
||||
res = pcnet_read_pci_config(dev, loc);
|
||||
if (res)
|
||||
goto error;
|
||||
|
||||
@@ -270,26 +270,24 @@ error:
|
||||
return res;
|
||||
}
|
||||
|
||||
static status_t pcnet_read_pci_config(struct device *dev, pci_location_t *loc) {
|
||||
static status_t pcnet_read_pci_config(struct device *dev, pci_location_t loc) {
|
||||
status_t res = NO_ERROR;
|
||||
pci_config_t config;
|
||||
uint8_t *buf = (uint8_t *) &config;
|
||||
unsigned i;
|
||||
|
||||
DEBUG_ASSERT(dev->state);
|
||||
|
||||
struct pcnet_state *state = dev->state;
|
||||
|
||||
for (i=0; i < sizeof(config); i++)
|
||||
pci_read_config_byte(loc, i, buf + i);
|
||||
pci_read_config(loc, &config);
|
||||
|
||||
LTRACEF("Resources:\n");
|
||||
|
||||
for (i=0; i < countof(config.base_addresses); i++) {
|
||||
if (config.base_addresses[i] & 0x1) {
|
||||
LTRACEF(" BAR %d I/O REG: %04x\n", i, config.base_addresses[i] & ~0x3);
|
||||
for (i=0; i < countof(config.type0.base_addresses); i++) {
|
||||
if (config.type0.base_addresses[i] & 0x1) {
|
||||
LTRACEF(" BAR %d I/O REG: %04x\n", i, config.type0.base_addresses[i] & ~0x3);
|
||||
|
||||
state->base = config.base_addresses[i] & ~0x3;
|
||||
state->base = config.type0.base_addresses[i] & ~0x3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -299,10 +297,10 @@ static status_t pcnet_read_pci_config(struct device *dev, pci_location_t *loc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (config.interrupt_line != 0xff) {
|
||||
LTRACEF(" IRQ %u\n", config.interrupt_line);
|
||||
if (config.type0.interrupt_line != 0xff) {
|
||||
LTRACEF(" IRQ %u\n", config.type0.interrupt_line);
|
||||
|
||||
state->irq = config.interrupt_line + INT_BASE;
|
||||
state->irq = config.type0.interrupt_line + INT_BASE;
|
||||
} else {
|
||||
res = ERR_NOT_CONFIGURED;
|
||||
goto error;
|
||||
|
||||
@@ -21,7 +21,21 @@ status_t mask_interrupt(unsigned int vector);
|
||||
status_t unmask_interrupt(unsigned int vector);
|
||||
|
||||
typedef enum handler_return (*int_handler)(void *arg);
|
||||
|
||||
void register_int_handler(unsigned int vector, int_handler handler, void *arg);
|
||||
|
||||
/* Register a MSI interrupt handler. Basically the same as register_int_handler, except
|
||||
* interrupt controller may have additional setup.
|
||||
*/
|
||||
void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge);
|
||||
|
||||
/* Allocate a run of interrupts with alignment log2 in a platform specific way.
|
||||
* Used for PCI MSI and possibly other use cases.
|
||||
*/
|
||||
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector);
|
||||
|
||||
/* Map the incoming interrupt line number from the pci bus config to raw
|
||||
* vector number, usable in the above apis.
|
||||
*/
|
||||
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector);
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
#include <platform/ide.h>
|
||||
#include <platform/pc.h>
|
||||
#include <platform.h>
|
||||
#include <dev/bus/pci.h>
|
||||
#include <dev/driver.h>
|
||||
#include <dev/class/block.h>
|
||||
#include <kernel/event.h>
|
||||
|
||||
#if WITH_DEV_BUS_PCI
|
||||
#include <dev/bus/pci.h>
|
||||
#endif
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
// status register bits
|
||||
@@ -200,8 +203,6 @@ static void ide_lba_setup(struct device *dev, uint32_t addr, int index);
|
||||
|
||||
static status_t ide_init(struct device *dev) {
|
||||
status_t res = NO_ERROR;
|
||||
uint32_t i;
|
||||
int err;
|
||||
|
||||
if (!dev)
|
||||
return ERR_INVALID_ARGS;
|
||||
@@ -221,37 +222,42 @@ static status_t ide_init(struct device *dev) {
|
||||
|
||||
// attempt pci detection
|
||||
if (config->legacy_index == 0x80 || config->legacy_index == 0x81) {
|
||||
#if WITH_DEV_BUS_PCI
|
||||
pci_location_t loc;
|
||||
pci_config_t pci_config;
|
||||
|
||||
err = pci_find_pci_class_code(&loc, 0x010180, 0);
|
||||
if (err != _PCI_SUCCESSFUL) {
|
||||
status_t err = pci_bus_mgr_find_device_by_class(&loc, PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_IDE, 0x80, 0);
|
||||
if (err != NO_ERROR) {
|
||||
LTRACEF("Failed to find PCI IDE device\n");
|
||||
res = ERR_NOT_FOUND;
|
||||
goto err;
|
||||
}
|
||||
|
||||
LTRACEF("Found PCI IDE device at %02x:%02x\n", loc.bus, loc.dev_fn);
|
||||
LTRACEF("Found PCI IDE device at %04x:%02x:%02x.%02x\n", loc.segment, loc.bus, loc.dev, loc.fn);
|
||||
|
||||
for (i=0; i < sizeof(pci_config) / sizeof(uint32_t); i++) {
|
||||
for (size_t i=0; i < sizeof(pci_config) / sizeof(uint32_t); i++) {
|
||||
uint32_t reg = sizeof(uint32_t) * i;
|
||||
|
||||
err = pci_read_config_word(&loc, reg, ((uint32_t *) &pci_config) + i);
|
||||
if (err != _PCI_SUCCESSFUL) {
|
||||
err = pci_read_config_word(loc, reg, ((uint32_t *) &pci_config) + i);
|
||||
if (err != NO_ERROR) {
|
||||
LTRACEF("Failed to read config reg %d: 0x%02x\n", reg, err);
|
||||
res = ERR_NOT_CONFIGURED;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i < 6; i++) {
|
||||
LTRACEF("BAR[%d]: 0x%08x\n", i, pci_config.base_addresses[i]);
|
||||
for (int i=0; i < 6; i++) {
|
||||
LTRACEF("BAR[%d]: 0x%08x\n", i, pci_config.type0.base_addresses[i]);
|
||||
}
|
||||
|
||||
// TODO: fill this in from the bars
|
||||
state->irq = ide_device_irqs[config->legacy_index & 0x7f];
|
||||
state->regs = ide_device_regs[config->legacy_index & 0x7f];
|
||||
state->type[0] = state->type[1] = TYPE_NONE;
|
||||
#else
|
||||
res = ERR_NOT_CONFIGURED;
|
||||
goto err;
|
||||
#endif// PCI
|
||||
} else {
|
||||
// legacy isa
|
||||
DEBUG_ASSERT(config->legacy_index < 2);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
/* NOTE: keep arch/x86/crt0.S in sync with these definitions */
|
||||
|
||||
/* interrupts */
|
||||
#define INT_VECTORS 0x31
|
||||
#define INT_VECTORS 256
|
||||
|
||||
/* defined interrupts */
|
||||
#define INT_BASE 0x20
|
||||
@@ -30,10 +30,14 @@
|
||||
#define INT_IDE0 0x2e
|
||||
#define INT_IDE1 0x2f
|
||||
|
||||
/* dynamic interrupts are allocated in this range */
|
||||
#define INT_DYNAMIC_START 0x30
|
||||
#define INT_DYNAMIC_END 0xef
|
||||
|
||||
/* APIC vectors */
|
||||
#define INT_APIC_TIMER 0x22
|
||||
#define INT_APIC_TIMER 0xf0
|
||||
|
||||
/* PIC remap bases */
|
||||
#define PIC1_BASE 0x20
|
||||
#define PIC2_BASE 0x28
|
||||
#define INT_PIC1_BASE 0x20
|
||||
#define INT_PIC2_BASE 0x28
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/reg.h>
|
||||
#include <lk/trace.h>
|
||||
#include <assert.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <platform/interrupts.h>
|
||||
@@ -19,130 +20,55 @@
|
||||
#include "platform_p.h"
|
||||
#include <platform/pc.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
static spin_lock_t lock;
|
||||
|
||||
#define PIC1 0x20
|
||||
#define PIC2 0xA0
|
||||
#define INTC_TYPE_INTERNAL 0
|
||||
#define INTC_TYPE_PIC 1
|
||||
#define INTC_TYPE_MSI 2
|
||||
|
||||
#define ICW1 0x11
|
||||
#define ICW4 0x01
|
||||
|
||||
struct int_handler_struct {
|
||||
struct int_vector {
|
||||
int_handler handler;
|
||||
void *arg;
|
||||
struct {
|
||||
uint allocated : 1;
|
||||
uint type : 2; // INTC_TYPE
|
||||
uint edge : 1; // edge vs level
|
||||
} flags;
|
||||
};
|
||||
|
||||
static struct int_handler_struct int_handler_table[INT_VECTORS];
|
||||
|
||||
/*
|
||||
* Cached IRQ mask (enabled/disabled)
|
||||
*/
|
||||
static uint8_t irqMask[2];
|
||||
|
||||
/*
|
||||
* init the PICs and remap them
|
||||
*/
|
||||
static void map(uint32_t pic1, uint32_t pic2) {
|
||||
/* send ICW1 */
|
||||
outp(PIC1, ICW1);
|
||||
outp(PIC2, ICW1);
|
||||
|
||||
/* send ICW2 */
|
||||
outp(PIC1 + 1, pic1); /* remap */
|
||||
outp(PIC2 + 1, pic2); /* pics */
|
||||
|
||||
/* send ICW3 */
|
||||
outp(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
|
||||
outp(PIC2 + 1, 2);
|
||||
|
||||
/* send ICW4 */
|
||||
outp(PIC1 + 1, 5);
|
||||
outp(PIC2 + 1, 1);
|
||||
|
||||
/* disable all IRQs */
|
||||
outp(PIC1 + 1, 0xff);
|
||||
outp(PIC2 + 1, 0xff);
|
||||
|
||||
irqMask[0] = 0xff;
|
||||
irqMask[1] = 0xff;
|
||||
}
|
||||
|
||||
static void enable(unsigned int vector, bool enable) {
|
||||
if (vector >= PIC1_BASE && vector < PIC1_BASE + 8) {
|
||||
vector -= PIC1_BASE;
|
||||
|
||||
uint8_t bit = 1 << vector;
|
||||
|
||||
if (enable && (irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] &= ~bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
} else if (!enable && !(irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] |= bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
}
|
||||
} else if (vector >= PIC2_BASE && vector < PIC2_BASE + 8) {
|
||||
vector -= PIC2_BASE;
|
||||
|
||||
uint8_t bit = 1 << vector;
|
||||
|
||||
if (enable && (irqMask[1] & bit)) {
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
irqMask[1] &= ~bit;
|
||||
outp(PIC2 + 1, irqMask[1]);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
} else if (!enable && !(irqMask[1] & bit)) {
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
irqMask[1] |= bit;
|
||||
outp(PIC2 + 1, irqMask[1]);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
}
|
||||
|
||||
bit = 1 << (INT_PIC2 - PIC1_BASE);
|
||||
|
||||
if (irqMask[1] != 0xff && (irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] &= ~bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
} else if (irqMask[1] == 0 && !(irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] |= bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
}
|
||||
} else {
|
||||
//dprintf(DEBUG, "Invalid PIC interrupt: %02x\n", vector);
|
||||
}
|
||||
}
|
||||
|
||||
static void issueEOI(unsigned int vector) {
|
||||
if (vector >= PIC1_BASE && vector <= PIC1_BASE + 7) {
|
||||
outp(PIC1, 0x20);
|
||||
} else if (vector >= PIC2_BASE && vector <= PIC2_BASE + 7) {
|
||||
outp(PIC2, 0x20);
|
||||
outp(PIC1, 0x20); // must issue both for the second PIC
|
||||
}
|
||||
}
|
||||
static struct int_vector int_table[INT_VECTORS];
|
||||
|
||||
void platform_init_interrupts(void) {
|
||||
// rebase the PIC out of the way of processor exceptions
|
||||
map(PIC1_BASE, PIC2_BASE);
|
||||
pic_init();
|
||||
lapic_init();
|
||||
|
||||
// initialize all of the vectors
|
||||
for (int i = 0; i < INT_VECTORS; i++) {
|
||||
if (i >= INT_PIC1_BASE && i <= INT_PIC2_BASE + 8) {
|
||||
int_table[i].flags.type = INTC_TYPE_PIC;
|
||||
}
|
||||
if (i >= INT_DYNAMIC_START && i <= INT_DYNAMIC_END) {
|
||||
int_table[i].flags.allocated = false;
|
||||
} else {
|
||||
int_table[i].flags.allocated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status_t mask_interrupt(unsigned int vector) {
|
||||
if (vector >= INT_VECTORS)
|
||||
return ERR_INVALID_ARGS;
|
||||
|
||||
// dprintf(DEBUG, "%s: vector %d\n", __PRETTY_FUNCTION__, vector);
|
||||
LTRACEF("vector %#x\n", vector);
|
||||
|
||||
spin_lock_saved_state_t state;
|
||||
spin_lock_irqsave(&lock, state);
|
||||
|
||||
enable(vector, false);
|
||||
if (int_table[vector].flags.type == INTC_TYPE_PIC) {
|
||||
pic_enable(vector, false);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lock, state);
|
||||
|
||||
@@ -150,27 +76,18 @@ status_t mask_interrupt(unsigned int vector) {
|
||||
}
|
||||
|
||||
|
||||
static void platform_mask_irqs(void) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
|
||||
outp(PIC1 + 1, 0xff);
|
||||
outp(PIC2 + 1, 0xff);
|
||||
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
}
|
||||
|
||||
status_t unmask_interrupt(unsigned int vector) {
|
||||
if (vector >= INT_VECTORS)
|
||||
return ERR_INVALID_ARGS;
|
||||
|
||||
// dprintf("%s: vector %d\n", __PRETTY_FUNCTION__, vector);
|
||||
LTRACEF("vector %#x\n", vector);
|
||||
|
||||
spin_lock_saved_state_t state;
|
||||
spin_lock_irqsave(&lock, state);
|
||||
|
||||
enable(vector, true);
|
||||
if (int_table[vector].flags.type == INTC_TYPE_PIC) {
|
||||
pic_enable(vector, true);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lock, state);
|
||||
|
||||
@@ -184,27 +101,98 @@ enum handler_return platform_irq(x86_iframe_t *frame) {
|
||||
|
||||
DEBUG_ASSERT(vector >= 0x20);
|
||||
|
||||
// deliver the interrupt
|
||||
struct int_vector *handler = &int_table[vector];
|
||||
|
||||
// edge triggered interrupts are acked beforehand
|
||||
if (handler->flags.edge) {
|
||||
if (handler->flags.type == INTC_TYPE_MSI) {
|
||||
lapic_eoi(vector);
|
||||
} else {
|
||||
pic_eoi(vector);
|
||||
}
|
||||
}
|
||||
|
||||
// call the registered interrupt handler
|
||||
enum handler_return ret = INT_NO_RESCHEDULE;
|
||||
if (handler->handler) {
|
||||
ret = handler->handler(handler->arg);
|
||||
}
|
||||
|
||||
if (int_handler_table[vector].handler)
|
||||
ret = int_handler_table[vector].handler(int_handler_table[vector].arg);
|
||||
|
||||
// ack the interrupt
|
||||
issueEOI(vector);
|
||||
// level triggered ack
|
||||
if (!handler->flags.edge) {
|
||||
if (handler->flags.type == INTC_TYPE_MSI) {
|
||||
lapic_eoi(vector);
|
||||
} else {
|
||||
pic_eoi(vector);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
|
||||
if (vector >= INT_VECTORS)
|
||||
panic("register_int_handler: vector out of range %d\n", vector);
|
||||
static void register_int_handler_etc(unsigned int vector, int_handler handler, void *arg, bool edge, uint type) {
|
||||
ASSERT(vector < INT_VECTORS);
|
||||
|
||||
spin_lock_saved_state_t state;
|
||||
spin_lock_irqsave(&lock, state);
|
||||
|
||||
int_handler_table[vector].arg = arg;
|
||||
int_handler_table[vector].handler = handler;
|
||||
int_table[vector].arg = arg;
|
||||
int_table[vector].handler = handler;
|
||||
int_table[vector].flags.allocated = true;
|
||||
int_table[vector].flags.edge = edge;
|
||||
int_table[vector].flags.type = type;
|
||||
|
||||
spin_unlock_irqrestore(&lock, state);
|
||||
}
|
||||
|
||||
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
|
||||
register_int_handler_etc(vector, handler, arg, false, INTC_TYPE_PIC);
|
||||
}
|
||||
|
||||
void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge) {
|
||||
register_int_handler_etc(vector, handler, arg, edge, INTC_TYPE_MSI);
|
||||
}
|
||||
|
||||
void platform_mask_irqs(void) {
|
||||
pic_mask_interrupts();
|
||||
}
|
||||
|
||||
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
|
||||
LTRACEF("pci_int %u\n", pci_int);
|
||||
|
||||
// pci interrupts are relative to PIC style irq #s so simply add INT_BASE to it
|
||||
uint out_vector = pci_int + INT_BASE;
|
||||
if (out_vector > INT_VECTORS) {
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*vector = out_vector;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
|
||||
LTRACEF("count %zu, align %u\n", count, align_log2);
|
||||
if (align_log2 > 1) {
|
||||
PANIC_UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
spin_lock_saved_state_t state;
|
||||
spin_lock_irqsave(&lock, state);
|
||||
|
||||
// find a free interrupt
|
||||
status_t err = ERR_NOT_FOUND;
|
||||
for (unsigned int i = 0; i < INT_VECTORS; i++) {
|
||||
if (!int_table[i].flags.allocated) {
|
||||
int_table[i].flags.allocated = true;
|
||||
*vector = i;
|
||||
LTRACEF("found irq %#x\n", i);
|
||||
err = NO_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lock, state);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
72
platform/pc/lapic.c
Normal file
72
platform/pc/lapic.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/reg.h>
|
||||
#include <lk/trace.h>
|
||||
#include <lk/init.h>
|
||||
#include <assert.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <platform/interrupts.h>
|
||||
#include <arch/ops.h>
|
||||
#include <arch/x86.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include "platform_p.h"
|
||||
#include <platform/pc.h>
|
||||
#include <kernel/vm.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
static bool lapic_present = false;
|
||||
static uint8_t *lapic_mmio;
|
||||
|
||||
void lapic_init(void) {
|
||||
// discover the presence of the local apic and map it
|
||||
LTRACE_ENTRY;
|
||||
|
||||
// check feature bit 9 in edx of leaf 1 for presence of lapic
|
||||
uint32_t a, b, c, d;
|
||||
cpuid(0x1, &a, &b, &c, &d);
|
||||
LTRACEF("%#x %#x %#x %#x\n", a, b, c, d);
|
||||
|
||||
if ((d & (1 << 9)) == 0) {
|
||||
// no lapic detected
|
||||
return;
|
||||
}
|
||||
lapic_present = true;
|
||||
}
|
||||
|
||||
void lapic_init_postvm(uint level) {
|
||||
if (!lapic_present)
|
||||
return;
|
||||
|
||||
// IA32_APIC_BASE_MSR
|
||||
uint64_t apic_base = read_msr(0x1b);
|
||||
LTRACEF("apic base %#llx\n", apic_base);
|
||||
|
||||
// TODO: assert that it's enabled
|
||||
|
||||
apic_base &= ~0xfff;
|
||||
dprintf(INFO, "LAPIC: physical address %#llx\n", apic_base);
|
||||
|
||||
// map the lapic into the kernel since it's not guaranteed that the physmap covers it
|
||||
status_t err = vmm_alloc_physical(vmm_get_kernel_aspace(), "lapic", PAGE_SIZE, (void **)&lapic_mmio, 0,
|
||||
apic_base & ~0xfff, /* vmm_flags */ 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
|
||||
ASSERT(err == NO_ERROR);
|
||||
}
|
||||
|
||||
LK_INIT_HOOK(lapic, lapic_init_postvm, LK_INIT_LEVEL_VM);
|
||||
|
||||
void lapic_eoi(unsigned int vector) {
|
||||
LTRACEF("vector %#x\n", vector);
|
||||
if (lapic_present) {
|
||||
*REG32(lapic_mmio + 0xb0) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
138
platform/pc/pic.c
Normal file
138
platform/pc/pic.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Corey Tabaka
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/reg.h>
|
||||
#include <lk/trace.h>
|
||||
#include <assert.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <platform/interrupts.h>
|
||||
#include <arch/ops.h>
|
||||
#include <arch/x86.h>
|
||||
#include <kernel/spinlock.h>
|
||||
#include "platform_p.h"
|
||||
#include <platform/pc.h>
|
||||
|
||||
#define LOCAL_TRACE 1
|
||||
|
||||
/* PIC information */
|
||||
/*
|
||||
* Cached IRQ mask (enabled/disabled)
|
||||
*/
|
||||
static uint8_t irqMask[2];
|
||||
|
||||
#define PIC1 0x20
|
||||
#define PIC2 0xA0
|
||||
|
||||
#define ICW1 0x11
|
||||
#define ICW4 0x01
|
||||
|
||||
/*
|
||||
* init the PICs and remap them
|
||||
*/
|
||||
static void map(uint32_t pic1, uint32_t pic2) {
|
||||
/* send ICW1 */
|
||||
outp(PIC1, ICW1);
|
||||
outp(PIC2, ICW1);
|
||||
|
||||
/* send ICW2 */
|
||||
outp(PIC1 + 1, pic1); /* remap */
|
||||
outp(PIC2 + 1, pic2); /* pics */
|
||||
|
||||
/* send ICW3 */
|
||||
outp(PIC1 + 1, 4); /* IRQ2 -> connection to slave */
|
||||
outp(PIC2 + 1, 2);
|
||||
|
||||
/* send ICW4 */
|
||||
outp(PIC1 + 1, 5);
|
||||
outp(PIC2 + 1, 1);
|
||||
|
||||
/* disable all IRQs */
|
||||
outp(PIC1 + 1, 0xff);
|
||||
outp(PIC2 + 1, 0xff);
|
||||
|
||||
irqMask[0] = 0xff;
|
||||
irqMask[1] = 0xff;
|
||||
}
|
||||
|
||||
void pic_init(void) {
|
||||
// rebase the PIC out of the way of processor exceptions
|
||||
map(INT_PIC1_BASE, INT_PIC2_BASE);
|
||||
}
|
||||
|
||||
void pic_enable(unsigned int vector, bool enable) {
|
||||
if (vector >= INT_PIC1_BASE && vector < INT_PIC1_BASE + 8) {
|
||||
vector -= INT_PIC1_BASE;
|
||||
|
||||
uint8_t bit = 1 << vector;
|
||||
|
||||
if (enable && (irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] &= ~bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
} else if (!enable && !(irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] |= bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
}
|
||||
} else if (vector >= INT_PIC2_BASE && vector < INT_PIC2_BASE + 8) {
|
||||
vector -= INT_PIC2_BASE;
|
||||
|
||||
uint8_t bit = 1 << vector;
|
||||
|
||||
if (enable && (irqMask[1] & bit)) {
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
irqMask[1] &= ~bit;
|
||||
outp(PIC2 + 1, irqMask[1]);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
} else if (!enable && !(irqMask[1] & bit)) {
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
irqMask[1] |= bit;
|
||||
outp(PIC2 + 1, irqMask[1]);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
}
|
||||
|
||||
bit = 1 << (INT_PIC2 - INT_PIC1_BASE);
|
||||
|
||||
if (irqMask[1] != 0xff && (irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] &= ~bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
} else if (irqMask[1] == 0 && !(irqMask[0] & bit)) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[0] |= bit;
|
||||
outp(PIC1 + 1, irqMask[0]);
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pic_eoi(unsigned int vector) {
|
||||
if (vector >= INT_PIC1_BASE && vector <= INT_PIC1_BASE + 7) {
|
||||
outp(PIC1, 0x20);
|
||||
} else if (vector >= INT_PIC2_BASE && vector <= INT_PIC2_BASE + 7) {
|
||||
outp(PIC2, 0x20);
|
||||
outp(PIC1, 0x20); // must issue both for the second PIC
|
||||
}
|
||||
}
|
||||
|
||||
void pic_mask_interrupts(void) {
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
|
||||
outp(PIC1 + 1, 0xff);
|
||||
outp(PIC2 + 1, 0xff);
|
||||
|
||||
irqMask[0] = inp(PIC1 + 1);
|
||||
irqMask[1] = inp(PIC2 + 1);
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <platform/multiboot.h>
|
||||
#include <platform/console.h>
|
||||
#include <platform/keyboard.h>
|
||||
#include <dev/bus/pci.h>
|
||||
#include <dev/uart.h>
|
||||
#include <arch/x86.h>
|
||||
#include <arch/mmu.h>
|
||||
@@ -27,6 +26,13 @@
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/acpi_lite.h>
|
||||
|
||||
#if WITH_DEV_BUS_PCI
|
||||
#include <dev/bus/pci.h>
|
||||
#endif
|
||||
#if WITH_LIB_MINIP
|
||||
#include <lib/minip.h>
|
||||
#endif
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
/* multiboot information passed in, if present */
|
||||
@@ -253,6 +259,7 @@ void platform_init(void) {
|
||||
|
||||
platform_init_keyboard(&console_input_buf);
|
||||
|
||||
#if WITH_DEV_BUS_PCI
|
||||
bool pci_initted = false;
|
||||
if (acpi_lite_init(0) == NO_ERROR) {
|
||||
if (LOCAL_TRACE) {
|
||||
@@ -286,6 +293,7 @@ void platform_init(void) {
|
||||
if (!pci_initted) {
|
||||
pci_init_legacy();
|
||||
}
|
||||
#endif
|
||||
|
||||
platform_init_mmu_mappings();
|
||||
}
|
||||
|
||||
@@ -16,3 +16,13 @@ void platform_init_debug(void);
|
||||
void platform_init_interrupts(void);
|
||||
void platform_init_timer(void);
|
||||
|
||||
// legacy programmable interrupt controller
|
||||
void pic_init(void);
|
||||
void pic_enable(unsigned int vector, bool enable);
|
||||
void pic_eoi(unsigned int vector);
|
||||
void pic_mask_interrupts(void);
|
||||
|
||||
// local apic
|
||||
void lapic_init(void);
|
||||
void lapic_eoi(unsigned int vector);
|
||||
|
||||
|
||||
@@ -2,24 +2,29 @@ LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
# two implementations, generic and legacy
|
||||
# two implementations, modern and legacy
|
||||
# legacy implies older hardware, pre pentium, pre pci
|
||||
CPU ?= generic
|
||||
CPU ?= modern
|
||||
|
||||
MODULE_DEPS += \
|
||||
dev/bus/pci \
|
||||
lib/acpi_lite \
|
||||
lib/bio \
|
||||
lib/cbuf
|
||||
|
||||
ifneq ($(CPU),legacy)
|
||||
MODULE_DEPS += dev/bus/pci
|
||||
endif
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/console.c \
|
||||
$(LOCAL_DIR)/debug.c \
|
||||
$(LOCAL_DIR)/ide.c \
|
||||
$(LOCAL_DIR)/interrupts.c \
|
||||
$(LOCAL_DIR)/keyboard.c \
|
||||
$(LOCAL_DIR)/lapic.c \
|
||||
$(LOCAL_DIR)/pic.c \
|
||||
$(LOCAL_DIR)/platform.c \
|
||||
$(LOCAL_DIR)/timer.c \
|
||||
$(LOCAL_DIR)/debug.c \
|
||||
$(LOCAL_DIR)/console.c \
|
||||
$(LOCAL_DIR)/keyboard.c \
|
||||
$(LOCAL_DIR)/ide.c \
|
||||
$(LOCAL_DIR)/uart.c \
|
||||
|
||||
LK_HEAP_IMPLEMENTATION ?= dlmalloc
|
||||
|
||||
@@ -222,3 +222,13 @@ void platform_init(void) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
|
||||
// at the moment there's no translation between PCI IRQs and native irqs
|
||||
*vector = pci_int;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
|
||||
return ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
@@ -92,3 +92,12 @@ enum handler_return riscv_platform_irq(void) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
|
||||
// at the moment there's no translation between PCI IRQs and native irqs
|
||||
*vector = pci_int;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t platform_allocate_interrupts(size_t count, uint align_log2, unsigned int *vector) {
|
||||
return ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user