251 lines
7.7 KiB
C
251 lines
7.7 KiB
C
#include <neobbo.h>
|
|
#include <kprint.h>
|
|
#include <string.h>
|
|
#include <lock.h>
|
|
#include "acpi.h"
|
|
#include "../mm/vmm.h"
|
|
#include "error.h"
|
|
#include "pci.h"
|
|
|
|
#define PCIE_CONF_SPACE_WIDTH 4096
|
|
#define PCI_CONF_SPACE_WIDTH 256
|
|
#define PCI_BUS_MAX_DEVICES 32
|
|
|
|
#define PCI_HEADER_TYPE_GENERAL 0x0 // General device
|
|
#define PCI_HEADER_TYPE_PCI2PCI 0x1 // PCI to PCI bridge
|
|
#define PCI_HEADER_TYPE_PCI2CB 0x2 // PCI to CardBus bridge
|
|
#define PCI_HEADER_TYPE_MULTI (1 << 7) // Multifunction device
|
|
|
|
#define PCI_FUNCTION_MAX 8 // There are always a max of 8 functions per device
|
|
|
|
#define PCI_DEVICE_BUS 8192 // Max number of devices in a
|
|
|
|
mcfg_t *mcfg;
|
|
|
|
uint64_t config_space_base_addr = 0;
|
|
uint8_t start_pci_num = 0;
|
|
uint8_t end_pci_num = 0;
|
|
|
|
pci_structure *pci_array;
|
|
|
|
extern uint64_t hhdmoffset;
|
|
extern uint64_t *kernel_page_map;
|
|
|
|
atomic_flag pci_array_lock = ATOMIC_FLAG_INIT;
|
|
|
|
void pci_add_device(pci_structure structure){
|
|
acquire_spinlock(&pci_array_lock);
|
|
int i = 0;
|
|
|
|
/* Find first unused space */
|
|
while(pci_array[i].func_addr[0] != 0){
|
|
if(i >= PCI_DEVICE_BUS){
|
|
klog(__func__, "No more space in the PCI array!");
|
|
kkill();
|
|
}
|
|
i++;
|
|
}
|
|
|
|
pci_array[i].segment = structure.segment;
|
|
pci_array[i].bus = structure.bus;
|
|
pci_array[i].device = structure.device;
|
|
|
|
for(int j = 0; j < PCI_FUNCTION_MAX; j++){
|
|
if(structure.func_addr[j] != 0){
|
|
pci_array[i].func_addr[j] = structure.func_addr[j];
|
|
}
|
|
}
|
|
|
|
free_spinlock(&pci_array_lock);
|
|
return;
|
|
}
|
|
|
|
pci_structure *pci_get_device(pci_structure structure){
|
|
acquire_spinlock(&pci_array_lock);
|
|
pci_structure ret = {0};
|
|
|
|
for(int i = 0; i < PCI_DEVICE_BUS; i++){
|
|
if( structure.segment == pci_array[i].segment &&
|
|
structure.bus == pci_array[i].bus &&
|
|
structure.device == pci_array[i].device){
|
|
free_spinlock(&pci_array_lock);
|
|
return &pci_array[i];
|
|
}
|
|
}
|
|
|
|
free_spinlock(&pci_array_lock);
|
|
return NULL;
|
|
}
|
|
|
|
void parse_conf_space(){
|
|
uint64_t num = (mcfg->header.length - sizeof(desc_header_t)) / sizeof(conf_space_t);
|
|
|
|
kprintf("addr: 0x{xn}", (uint64_t)mcfg);
|
|
kprintf("header length: {dn}", mcfg->header.length);
|
|
kprintf("num: {dn}", num);
|
|
|
|
for(uint64_t i = 0; i < num; i++){
|
|
conf_space_t header = mcfg->conf_spaces[i];
|
|
|
|
kprintf("header {dn}", i);
|
|
kprintf(" base addr: 0x{xn}", header.base_ecm);
|
|
kprintf(" PCI segment group: {dn}", header.pci_seg_group);
|
|
kprintf(" start pci bus number: 0x{xn}", header.start_pci_num);
|
|
kprintf(" end pci bus number: 0x{xn}", header.end_pci_num);
|
|
|
|
config_space_base_addr = header.base_ecm + hhdmoffset;
|
|
start_pci_num = header.start_pci_num;
|
|
end_pci_num = header.end_pci_num;
|
|
}
|
|
}
|
|
|
|
void enumerate_conf_space(){
|
|
l84_pci_function_return ret;
|
|
|
|
for(uint64_t i = 0; i < end_pci_num; i++){
|
|
for(uint64_t j = 0; j < 32; j++){
|
|
ret = check_device(i, j);
|
|
|
|
if(ret.func_addr[0] != 0){
|
|
pci_structure structure = {.segment = 1, .bus = i, .device = j};
|
|
memcpy(structure.func_addr, ret.func_addr, PCI_FUNCTION_MAX * sizeof(uint64_t));
|
|
pci_add_device(structure);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/* pci_header_t *pci_find_device(uint64_t class, int subclass)
|
|
|
|
- Parameters:
|
|
class - class code
|
|
subclass - subclass. if set to -1 then subclass is ignored
|
|
|
|
- Return:
|
|
Returns pointer to the requested PCI header. Returns NULL if not found
|
|
|
|
- TODO:
|
|
Handle returning multiple functions
|
|
|
|
*/
|
|
|
|
pci_header_t *pci_find_device(uint64_t class, int subclass){
|
|
l84_pci_function_return pci_function_return;
|
|
|
|
for(uint64_t i = 0; i < end_pci_num; i++){
|
|
for(uint64_t j = 0; j < 32; j++){
|
|
|
|
pci_function_return = check_device(i, j);
|
|
|
|
if(pci_function_return.func_addr[0] == 0){
|
|
continue;
|
|
}
|
|
|
|
/* Get the address of the first function */
|
|
pci_header_t *header = (pci_header_t*)pci_function_return.func_addr[0];
|
|
|
|
|
|
if(header->class_code == class){
|
|
|
|
/* If no subclass is wanted, then just return the header */
|
|
if(subclass == -1){
|
|
return header;
|
|
}
|
|
|
|
if(subclass == header->subclass){
|
|
return header;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Found nothing, return null */
|
|
return NULL;
|
|
}
|
|
|
|
void pci_init(){
|
|
mcfg = (mcfg_t*)find_acpi_table("MCFG");
|
|
|
|
if(!mcfg){
|
|
klog(__func__, "Failed to find MCFG table");
|
|
kprintf("pci: device: PCIe not supported! Halting");
|
|
kkill();
|
|
}
|
|
|
|
mcfg = (mcfg_t*)((uint64_t)mcfg + hhdmoffset);
|
|
|
|
parse_conf_space();
|
|
|
|
/* Map the config space */
|
|
kmap_pages((uint64_t*)(config_space_base_addr - hhdmoffset), (PCIE_CONF_SPACE_WIDTH * end_pci_num) / PAGE_SIZE, PTE_BIT_RW | PTE_BIT_NX);
|
|
|
|
/* Stores enough for an entire configuration space */
|
|
pci_array = kmalloc((256 * 32) * sizeof(pci_structure));
|
|
|
|
if(!pci_array){
|
|
klog(__func__, "Failed to allocate memory for PCI structures!");
|
|
kkill();
|
|
}
|
|
|
|
enumerate_conf_space();
|
|
|
|
}
|
|
|
|
l84_pci_function_return check_device(uint64_t bus, uint64_t device){
|
|
|
|
l84_pci_function_return ret = {0};
|
|
|
|
pci_header_t *header = (pci_header_t*)get_header(bus, device, 0);
|
|
|
|
vmm_map_page(kernel_page_map, (uint64_t)header, (uint64_t)header - hhdmoffset, PTE_BIT_PRESENT | PTE_BIT_RW | PTE_BIT_NX);
|
|
|
|
/* If vendor id is 0xffff, that means that this device is unimplemented */
|
|
if(header->vendor_id == 0xffff){
|
|
return (l84_pci_function_return){ 0, {0} };
|
|
}
|
|
|
|
if((header->header_type & PCI_HEADER_TYPE_MULTI) != 0){
|
|
/* Mask the 7th bit (multi-function bit) so we get the header type */
|
|
uint64_t multi_header_type = header->header_type & 0x3f;
|
|
|
|
ret.multi = true;
|
|
|
|
for(uint64_t function = 0; function < PCI_FUNCTION_MAX; function++){
|
|
uint64_t *addr = (uint64_t*)(config_space_base_addr + ((bus) << 20 | device << 15 | function << 12));
|
|
pci_header_t *func = (pci_header_t*)((uint64_t)addr);
|
|
|
|
vmm_map_page(kernel_page_map, (uint64_t)func, (uint64_t)func - hhdmoffset, PTE_BIT_PRESENT | PTE_BIT_RW | PTE_BIT_NX);
|
|
|
|
if(func->vendor_id != 0xffff){
|
|
//kprintf("pci multi: bus: 0x{x} device: 0x{x} type: 0x{x} class: 0x{x} subclass: 0x{x} vendorid: 0x{xn}", bus, func->device_id, multi_header_type, func->class_code, func->subclass, func->vendor_id);
|
|
//serial_kprintf("pci multi: bus: 0x{x} device: 0x{x} type: 0x{x} class: 0x{x} subclass: 0x{x} vendorid: 0x{xn}", bus, func->device_id, multi_header_type, func->class_code, func->subclass, func->vendor_id);
|
|
ret.func_addr[function] = (uint64_t)func;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret.multi = false;
|
|
ret.func_addr[0] = (uint64_t)header;
|
|
|
|
//kprintf("pci: bus: 0x{x} device: 0x{x} type: 0x{x} class: 0x{x} subclass: 0x{x} vendorid: 0x{xn}", bus, header->device_id, header->header_type, header->class_code, header->subclass, header->vendor_id);
|
|
//serial_kprintf("pci: bus: 0x{x} device: 0x{x} type: 0x{x} class: 0x{x} subclass: 0x{x} vendorid: 0x{xn}", bus, header->header_type, header->class_code, header->subclass, header->vendor_id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void check_bus(uint64_t bus){
|
|
|
|
for(uint64_t i = 0; i < PCI_BUS_MAX_DEVICES; i++){
|
|
check_device(bus, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint64_t get_header(uint64_t bus, uint64_t device, uint64_t function){
|
|
return config_space_base_addr + ((bus * 256) + (device * 8) + function) * PCIE_CONF_SPACE_WIDTH;
|
|
}
|
|
|