Mathieu Serandour 1 year ago
parent
commit
888c651559
  1. 13
      kernel/acpi/acpi.c
  2. 6
      kernel/acpi/acpitables.h
  3. 252
      kernel/drivers/pcie.c
  4. 67
      kernel/drivers/pcie.h
  5. 18
      kernel/drivers/pcie/pcie.c
  6. 86
      kernel/drivers/pcie/pcie.h
  7. 325
      kernel/drivers/pcie/scan.c
  8. 36
      kernel/drivers/pcie/scan.h
  9. 5
      kernel/entry.c
  10. 2
      kernel/memory/heap.c

13
kernel/acpi/acpi.c

@ -4,21 +4,24 @@
#include "acpi.h"
#include "acpitables.h"
#include "../lib/common.h"
#include "../lib/assert.h"
#include "../lib/dump.h"
#include "../lib/sprintf.h"
#include "../lib/string.h"
#include "acpitables.h"
#include "../int/apic.h"
#include "../lib/logging.h"
#include "../memory/vmap.h"
#include "../memory/paging.h"
#include "../drivers/pcie.h"
#include "../lib/logging.h"
#include "../drivers/pcie/scan.h"
static void* apic_config_base, *hpet_config_space;
// defined in pcie.c
// defined in pcie/scan.c
extern struct PCIE_Descriptor pcie_descriptor;
static const struct XSDT* xsdt;
@ -187,7 +190,7 @@ static void parse_pcie(const struct PCIETable* table) {
// fill the pcie driver's descriptor
size_t size = (table->header.length-sizeof(struct ACPISDTHeader)-8);
pcie_descriptor.size = size / sizeof(struct PCIE_segment_group_descriptor);
pcie_descriptor.size = size / sizeof(struct PCIE_busgroup);
assert(pcie_descriptor.size < PCIE_SUPPORTED_SEGMENT_GROUPS);

6
kernel/acpi/acpitables.h

@ -4,7 +4,7 @@
#include <stdint.h>
#include "../lib/common.h"
#include "../drivers/pcie.h"
#include "../drivers/pcie/scan.h"
struct RSDPDescriptor {
uint8_t signature[8];
@ -141,13 +141,13 @@ struct MADT {
static_assert(
sizeof(struct PCIE_segment_group_descriptor) == 16);
sizeof(struct PCIE_busgroup) == 16);
struct PCIETable {
struct ACPISDTHeader header;
uint64_t reserved0;
struct PCIE_segment_group_descriptor segments[];
struct PCIE_busgroup segments[];
} __packed;

252
kernel/drivers/pcie.c

@ -1,252 +0,0 @@
#include "pcie.h"
#include "../lib/logging.h"
#include "../lib/dump.h"
#include "../lib/assert.h"
#include "../memory/heap.h"
#include "../memory/paging.h"
#include "../lib/string.h"
struct PCIE_configuration_space {
volatile uint16_t vendorID;
volatile uint16_t deviceID;
uint16_t unused0;
uint16_t unused1;
volatile uint8_t revID;
volatile uint8_t progIF;
volatile uint8_t subclasscode;
volatile uint8_t classcode;
volatile uint32_t infos;
volatile uint64_t bar[3];
volatile uint32_t cardbud_cis_ptr;
volatile uint16_t subsystemID;
volatile uint16_t subsystem_vendorID;
volatile uint32_t expansion_base;
volatile uint8_t capabilities;
uint8_t reserved0[3];
uint32_t reserved1;
volatile uint8_t interrupt_line;
volatile uint8_t interrupt_pin;
uint16_t reserved2[2];
};
// higher bus number: could be 0xff
// if there is only one bus group
static size_t max_bus = 0;
struct PCIE_Descriptor pcie_descriptor = {0};
// 0 | group | bus | 0 | slot | 0 | func |
// | 2 | 8 | 1 | 3 | 1 | 3 |
// 16 8
static struct PCIE_segment_group_descriptor* get_segment_group(unsigned bus) {
struct PCIE_segment_group_descriptor* group = &pcie_descriptor.array[0];
unsigned group_number = 0;
unsigned start_bus, end_bus;
// go through all segment groups
// if the bus is not in the range,
// it is not the right segment
while((start_bus = group->start_bus) > bus
|| (end_bus = group->end_bus) < bus) {
assert(group_number != pcie_descriptor.size);
group_number++;
group++;
}
return group;
}
static struct PCIE_configuration_space* __attribute__((pure)) get_config_space_base(
unsigned bus,
unsigned device,
unsigned func
) {
struct PCIE_segment_group_descriptor* group_desc = get_segment_group(bus);
return group_desc->address + (
(bus - group_desc->start_bus) << 20
| device << 15
| func << 12);
}
static int __attribute__((pure)) get_vendorID(unsigned bus, unsigned device, unsigned func) {
struct PCIE_configuration_space* config_space = get_config_space_base(bus,device,func);
return config_space->vendorID;
}
struct pcie_device_descriptor {
uint16_t bus, device, function;
struct PCIE_configuration_space* config_space;
};
// array of all installed devices' functions
static struct pcie_device_descriptor* found_devices = NULL;
static size_t n_found_devices = 0;
/**
* constructs the found_devices array
*/
static void scan_devices(void) {
// first create a linked list
// as we dont know how many devices are present
struct device_desc_node {
struct pcie_device_descriptor e;
struct device_desc_node* next;
};
// head of the list
struct device_desc_node ghost_node = {.next = NULL};
struct device_desc_node* current = &ghost_node;
// count the number of devices
void insert(
unsigned bus,
unsigned device,
unsigned func,
struct PCIE_configuration_space* config_space
)
{
current->next = malloc(sizeof(struct device_desc_node));
current = current->next;
current->next = NULL;
// fill the new node
current->e.bus = bus;
current->e.device = device;
current->e.function = func;
current->e.config_space = config_space;
n_found_devices++;
}
for(unsigned bus = 0; bus < max_bus; bus++) {
for(unsigned device = 0; device < 32; device++) {
// if the first function doesn't exist, so does
// the device
uint16_t vendorID = get_vendorID(bus,device, 0);
if(vendorID == 0xFFFF)
continue;
insert(bus,
device,
0,
get_config_space_base(bus,device,0)
);
for(unsigned func = 1; func < 8; func++) {
if(get_vendorID(bus,device, func) != vendorID)
continue;
insert(bus,
device,
func,
get_config_space_base(bus,device,func)
);
}
}
}
// now create the final array
found_devices = malloc(
n_found_devices
* sizeof(struct pcie_device_descriptor));
// now fill it and free the devices structure
struct pcie_device_descriptor* ptr = found_devices;
for(struct device_desc_node* device = ghost_node.next;
device != NULL;
) {
memcpy(ptr++, &device->e, sizeof(struct pcie_device_descriptor));
struct device_desc_node* next = device->next;
free(device);
device = next;
}
}
// identity map every page that might be a config space
static void identity_map_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
// identity map the corresponding pages
map_pages(
(uint64_t)pcie_descriptor.array[i].address, // phys
(uint64_t)pcie_descriptor.array[i].address, // virt
256 * // busses
32 * // devices
8, // functions
PRESENT_ENTRY | PCD | PL_XD
// no cache, execute disable
);
}
}
static void identity_unmap_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++)
unmap_pages((uint64_t)pcie_descriptor.array[i].address, 256 * 32 * 8);
}
/**
* during the init,
* we identity map the pcie configuration spaces
*
*/
void pcie_init(void) {
log_debug("init pcie...");
// calculate the highest bus number
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
// map the corresponding pages
if(pcie_descriptor.array[i].end_bus > max_bus)
max_bus = pcie_descriptor.array[i].end_bus;
}
log_debug("%u PCIE bus groups found", pcie_descriptor.size);
identity_map_possible_config_spaces();
scan_devices();
for(unsigned i = 0; i < n_found_devices; i++) {
log_warn("%2x:%2x:%2x: %2x:%2x:%2x, rev %2x, "
"vendor 0x%4x",
found_devices[i].bus,
found_devices[i].device,
found_devices[i].function,
found_devices[i].config_space->classcode,
found_devices[i].config_space->subclasscode,
found_devices[i].config_space->progIF,
found_devices[i].config_space->revID,
found_devices[i].config_space->vendorID
);
}
identity_unmap_possible_config_spaces();
log_info("found %u PCI Express devices", n_found_devices);
}
void pcie_init_devices(void) {
}

67
kernel/drivers/pcie.h

@ -1,67 +0,0 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "../lib/assert.h"
struct PCIE_segment_group_descriptor {
void* address; // Base address
// of enhanced configuration mechanism
uint16_t group; // PCI Segment Group Number
uint8_t start_bus; // Start PCI bus number
// decoded by this host bridge
uint8_t end_bus; // End PCI bus number
// decoded by this host bridge
uint32_t reserved;
} __attribute__((packed));
static_assert(sizeof(struct PCIE_segment_group_descriptor) == 16);
#define PCIE_SUPPORTED_SEGMENT_GROUPS 2
struct PCIE_Descriptor {
size_t size;
struct PCIE_segment_group_descriptor array[PCIE_SUPPORTED_SEGMENT_GROUPS];
// we only handle PCIE devices with only 4 segment groups max
};
// defined in pcie.c
extern struct PCIE_Descriptor pcie_descriptor;
void pcie_init(void);
void pcie_scan(void);
void pcie_init_devices(void);
typedef void (*driver_init_fun)(void* config_space);
typedef void (*driver_callback)(void);
struct driver_descriptor {
driver_init_fun install;
driver_callback remove;
const char* driver_name;
uint32_t status;
void* driver_data;
size_t driver_data_len;
};
/**
* PCIE drivers interfaces:
*
* provide void init(void* config_space_base)
*
* functions to call:
* register_irq(unsigned)
* unregister_irq(unsigned)
*
* int register_timer(void callback(void), unsigned period)
* void unregister_timer(int timer_id)
*
*/

18
kernel/drivers/pcie/pcie.c

@ -0,0 +1,18 @@
#include "pcie.h"
#include "../../lib/logging.h"
#include "../../lib/dump.h"
#include "../../lib/assert.h"
#include "../../memory/heap.h"
#include "../../memory/paging.h"
#include "../../lib/string.h"
static struct pcie_device* devices = NULL;
void pcie_init(void) {
devices = pcie_scan();
}
void pcie_init_devices(void) {
}

86
kernel/drivers/pcie/pcie.h

@ -0,0 +1,86 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "../../lib/assert.h"
void pcie_init(void);
void pcie_init_devices(void);
typedef void (*driver_init_fun)(void* config_space);
typedef void (*driver_callback)(void);
struct resource {
void* addr;
size_t size;
};
typedef union {
struct {
unsigned domain: 32;
unsigned unused: 8;
unsigned bus: 8;
unsigned device: 8;
unsigned func: 8;
};
uint64_t value;
} pcie_path_t;
struct dev_info {
uint16_t vendorID;
uint16_t deviceID;
uint8_t classcode;
uint8_t subclasscode;
uint8_t progIF;
uint8_t revID;
};
static_assert_equals(sizeof(pcie_path_t), 8);
struct pcie_dev {
struct resource bars[3];
struct dev_info info;
void* config_space;
pcie_path_t path;
struct driver* driver;
};
struct driver {
driver_init_fun install;
driver_callback remove;
const char* driver_name;
uint32_t status;
struct resource data;
const struct pcie_device* dev;
};
/**
* PCIE drivers interfaces:
*
* provide void init(void* config_space_base)
*
* functions to call:
* register_irq(unsigned)
* unregister_irq(unsigned)
*
* int register_timer(void callback(void), unsigned period)
* void unregister_timer(int timer_id)
*
*/

325
kernel/drivers/pcie/scan.c

@ -0,0 +1,325 @@
#include "scan.h"
#include "pcie.h"
#include "../../lib/logging.h"
#include "../../lib/dump.h"
#include "../../lib/assert.h"
#include "../../memory/heap.h"
#include "../../memory/paging.h"
#include "../../lib/string.h"
struct PCIE_config_space {
volatile uint16_t vendorID;
volatile uint16_t deviceID;
uint16_t unused0;
uint16_t unused1;
volatile uint8_t revID;
volatile uint8_t progIF;
volatile uint8_t subclasscode;
volatile uint8_t classcode;
volatile uint32_t infos;
volatile uint64_t bar[3];
volatile uint32_t cardbud_cis_ptr;
volatile uint16_t subsystemID;
volatile uint16_t subsystem_vendorID;
volatile uint32_t expansion_base;
volatile uint8_t capabilities;
uint8_t reserved0[3];
uint32_t reserved1;
volatile uint8_t interrupt_line;
volatile uint8_t interrupt_pin;
uint16_t reserved2[2];
};
// higher bus number: could be 0xff
// if there is only one bus group
static size_t max_bus = 0;
struct PCIE_Descriptor pcie_descriptor = {0};
// 0 | group | bus | 0 | slot | 0 | func |
// | 2 | 8 | 1 | 3 | 1 | 3 |
// 16 8
__attribute__((pure))
static struct resource get_bar_resource(
struct PCIE_config_space* cs,
unsigned i)
{
volatile uint32_t* bar_reg = (uint32_t*)&cs->bar[i];
uint32_t val = *bar_reg;
*bar_reg = ~1;
unsigned size = ~ *bar_reg + 1;
*bar_reg = val;
return (struct resource) {
.addr = val,
.size = size,
};
}
__attribute__((pure))
static struct PCIE_config_space*
get_config_space_base(unsigned bus_group,
unsigned bus,
unsigned device,
unsigned func
)
{
assert(bus_group < pcie_descriptor.size);
struct PCIE_busgroup* group_desc = &pcie_descriptor.array[bus_group];
return group_desc->address + (
(bus - group_desc->start_bus) << 20
| device << 15
| func << 12);
}
static int __attribute__((pure))
get_vendorID(unsigned bus_group,
unsigned bus,
unsigned device,
unsigned func)
{
struct PCIE_config_space* config_space =
get_config_space_base(bus_group, bus,device,func);
return config_space->vendorID;
}
struct early_device_descriptor {
uint16_t bus, device, function, domain;
struct PCIE_config_space* config_space;
};
// array of all installed devices' functions
static struct pcie_dev* found_devices = NULL;
static size_t n_found_devices = 0;
/**
* constructs the found_devices array
*/
static void scan_devices(void) {
// first create a linked list
// as we dont know how many devices are present
struct device_desc_node {
struct early_device_descriptor e;
struct device_desc_node* next;
};
// head of the list
struct device_desc_node ghost_node = {.next = NULL};
struct device_desc_node* current = &ghost_node;
// count the number of devices
void insert(
unsigned bus,
unsigned device,
unsigned func,
struct PCIE_config_space* config_space
)
{
current->next = malloc(sizeof(struct device_desc_node));
current = current->next;
current->next = NULL;
// fill the new node
current->e.bus = bus;
current->e.device = device;
current->e.function = func;
current->e.config_space = config_space;
n_found_devices++;
}
for(unsigned bus_group_i = 0;
bus_group_i < pcie_descriptor.size;
bus_group_i++)
{
struct PCIE_busgroup* group =
&pcie_descriptor.array[bus_group_i];
unsigned bus_group = group->group;
for(unsigned bus = group->start_bus;
bus < group->end_bus;
bus++)
{
for(unsigned device = 0; device < 32; device++) {
// if the first function doesn't exist, so does
// the device
uint16_t vendorID = get_vendorID(bus_group,
bus,
device,
0);
if(vendorID == 0xFFFF)
continue;
insert(bus,
device,
0,
get_config_space_base(bus_group,
bus,
device,
0)
);
for(unsigned func = 1; func < 8; func++) {
if(get_vendorID(bus_group,
bus,
device,
func) != vendorID)
continue;
insert(bus,
device,
func,
get_config_space_base(bus_group,
bus,
device,
func)
);
}
}
}
}
// now create the final array
found_devices = malloc(
n_found_devices
* sizeof(struct early_device_descriptor));
// now fill it and free the devices structure
struct pcie_dev* ptr = found_devices;
for(struct device_desc_node* device = ghost_node.next;
device != NULL;
) {
struct early_device_descriptor* early_desc = &device->e;
struct PCIE_config_space* cs = early_desc->config_space;
*ptr = (struct pcie_dev) {
.bars = {
get_bar_resource(cs, 0),
get_bar_resource(cs, 1),
get_bar_resource(cs, 2),
},
.config_space = cs,
.driver = NULL,
.info = (struct dev_info) {
.vendorID = cs->vendorID,
.deviceID = cs->deviceID,
.classcode = cs->classcode,
.subclasscode = cs->subclasscode,
.progIF = cs->progIF,
.revID = cs->revID,
},
.path = (pcie_path_t) {
.domain = early_desc->domain,
.bus = early_desc->bus,
.device = early_desc->device,
.func = early_desc->function,
}
};
struct resource bars[3];
struct dev_info info;
void* config_space;
pcie_path_t path;
struct driver* driver;
memcpy(ptr++, &device->e, sizeof(struct early_device_descriptor));
struct device_desc_node* next = device->next;
free(device);
device = next;
}
}
// identity map every page that might be a config space
static void identity_map_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
// identity map the corresponding pages
map_pages(
(uint64_t)pcie_descriptor.array[i].address, // phys
(uint64_t)pcie_descriptor.array[i].address, // virt
256 * // busses
32 * // devices
8, // functions
PRESENT_ENTRY | PCD | PL_XD
// no cache, execute disable
);
}
}
static void identity_unmap_possible_config_spaces(void) {
for(unsigned i = 0; i < pcie_descriptor.size; i++)
unmap_pages((uint64_t)pcie_descriptor.array[i].address, 256 * 32 * 8);
}
/**
* during the init,
* we identity map the pcie configuration spaces
*
*/
struct pcie_device* pcie_scan(void) {
log_debug("scanning pcie devices...");
// calculate the highest bus number
for(unsigned i = 0; i < pcie_descriptor.size; i++) {
// map the corresponding pages
if(pcie_descriptor.array[i].end_bus > max_bus)
max_bus = pcie_descriptor.array[i].end_bus;
}
log_debug("%u PCIE bus groups found", pcie_descriptor.size);
identity_map_possible_config_spaces();
scan_devices();
for(unsigned i = 0; i < n_found_devices; i++) {
log_warn("%2x:%2x:%2x: %2x:%2x:%2x, rev %2x, "
"vendor 0x%4x",
found_devices[i].path.bus,
found_devices[i].path.device,
found_devices[i].path.func,
found_devices[i].info.classcode,
found_devices[i].info.subclasscode,
found_devices[i].info.progIF,
found_devices[i].info.revID,
found_devices[i].info.vendorID
);
}
identity_unmap_possible_config_spaces();
log_info("found %u PCI Express devices", n_found_devices);
}

36
kernel/drivers/pcie/scan.h

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "../../lib/assert.h"
struct PCIE_busgroup {
void* address; // Base address
// of enhanced configuration mechanism
uint16_t group; // PCI Segment Group Number
uint8_t start_bus; // Start PCI bus number
// decoded by this host bridge
uint8_t end_bus; // End PCI bus number
// decoded by this host bridge
uint32_t reserved;
} __attribute__((packed));
static_assert_equals(sizeof(struct PCIE_busgroup), 16);
#define PCIE_SUPPORTED_SEGMENT_GROUPS 2
struct PCIE_Descriptor {
size_t size;
struct PCIE_busgroup array[PCIE_SUPPORTED_SEGMENT_GROUPS];
// we only handle PCIE devices with only 4 segment groups max
};
// defined in pcie.c
extern struct PCIE_Descriptor pcie_descriptor;
struct pcie_device* pcie_scan(void);

5
kernel/entry.c

@ -12,8 +12,9 @@
#include "int/pic.h"
#include "drivers/hpet.h"
#include "drivers/pcie.h"
#include "drivers/ps2kb.h"
#include "drivers/pcie/pcie.h"
#include "drivers/pcie/scan.h"
#include "memory/physical_allocator.h"
#include "memory/paging.h"
@ -210,7 +211,7 @@ void _start(struct stivale2_struct *stivale2_struct) {
log_flush();
pcie_init();
pcie_init_devices();
pic_init();
ps2kb_init();

2
kernel/memory/heap.c

@ -277,7 +277,7 @@ void* __attribute__((noinline)) malloc(size_t size) {
break;
}
assert(is_kernel_memory(seg));
assert(is_kernel_memory((uint64_t)seg));
if(!seg->free || seg->size < size) {
// this segment is not right, check the next one

Loading…
Cancel
Save