summaryrefslogtreecommitdiff
path: root/src/hardware
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2017-06-10 21:26:24 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2017-06-10 21:26:24 +0200
commitd6d1bdfefafff50b7b6d15d218c0a188570be541 (patch)
tree15ee8de727d0be5d126efda146b2879de0a72773 /src/hardware
parenteea5bf4b859eb56c5772c58ca54937a90a10e7ee (diff)
downloadabaos-d6d1bdfefafff50b7b6d15d218c0a188570be541.tar.gz
abaos-d6d1bdfefafff50b7b6d15d218c0a188570be541.tar.bz2
some big renames into subdirs of aspects
updated README removed size_t in sys/types.h and sys/types.h itself, size_t is in stddef.h
Diffstat (limited to 'src/hardware')
-rw-r--r--src/hardware/interrupts.asm90
-rw-r--r--src/hardware/interrupts.c215
-rw-r--r--src/hardware/interrupts.h103
-rw-r--r--src/hardware/pci.c187
-rw-r--r--src/hardware/pci.h49
-rw-r--r--src/hardware/port.asm49
-rw-r--r--src/hardware/port.c17
-rw-r--r--src/hardware/port.h23
8 files changed, 733 insertions, 0 deletions
diff --git a/src/hardware/interrupts.asm b/src/hardware/interrupts.asm
new file mode 100644
index 0000000..155b2af
--- /dev/null
+++ b/src/hardware/interrupts.asm
@@ -0,0 +1,90 @@
+[bits 32]
+
+global interrupts_enable
+global interrupts_disable
+global interrupts_load_idt
+extern interrupts_handle_static_interrupt
+
+; void interrupts_enable( void )
+interrupts_enable:
+ push ebp
+ mov ebp, esp
+ sti
+ leave
+ ret
+
+; void interrupts_disable( void )
+interrupts_disable:
+ push ebp
+ mov ebp, esp
+ cli
+ leave
+ ret
+
+; void interrupts_load_idt( interrupt_descriptor_table_pointer_t *idt_pointer )
+interrupts_load_idt:
+ push ebp
+ mov ebp, esp
+ mov ecx, [ebp+8]
+ lidt [ecx]
+ leave
+ ret
+
+; the handler to ignore interrupts
+global interrupts_ignore_request
+interrupts_ignore_request:
+ iret
+
+; void interrupts_handle_request_0x00( );
+%macro exception_stub 1
+global interrupts_handle_exception_%1
+interrupts_handle_exception_%1:
+ mov [interrupt_no], byte %1
+ jmp int_entry
+%endmacro
+
+exception_stub 0x00
+
+; IRQs and exceptions would normally collidate, that's why the
+; hardware interrupts must be transposed by an offset
+IRQ_BASE equ 0x20
+
+%macro irq_stub 1
+global interrupts_handle_irq_%1
+interrupts_handle_irq_%1:
+ mov [interrupt_no], byte IRQ_BASE + %1
+ jmp int_entry
+%endmacro
+
+irq_stub 0x00
+irq_stub 0x01
+irq_stub 0x0C
+
+int_entry:
+ ; safe state of interrupted code
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ ; call the static C handler with the correct interrupt number
+ ; uint32_t interrupts_handle_interrupt( uint8_t interrupt_no, uint32_t esp );
+ push esp
+ mov eax, [interrupt_no]
+ push eax
+ call interrupts_handle_static_interrupt
+ ;add esp, 8
+ mov esp, eax
+
+ ; restore state
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ iret
+
+interrupt_no:
+ db 0
diff --git a/src/hardware/interrupts.c b/src/hardware/interrupts.c
new file mode 100644
index 0000000..614dbf0
--- /dev/null
+++ b/src/hardware/interrupts.c
@@ -0,0 +1,215 @@
+#include "interrupts.h"
+
+#include "string.h"
+#include "stdio.h"
+
+#include "kernel.h"
+
+#include "keyboard.h"
+
+// TODO: for now the code segment is the first entry in the boot GDT
+#define GDT_CODE_SEGMENT_SELECTOR 8
+
+// the Present bit in a IDT entry
+#define IDT_PRESENT_BIT 0x80
+
+// DPL rings
+#define KERNEL_RING 0
+
+// types of IDT entries
+#define IDT_TYPE_INTERRUPT_GATE 0xE
+
+// PIC constants
+
+#define ICW1_ICW4 0x01 // support a 4th initialization word (ICW4)
+#define ICW1_INIT 0x10 // bit indicating initialization of chip
+
+// offset for hardware interrupts (for PIC ICW2)
+#define ICW2_IRQ_BASE_MASTER IRQ_BASE
+#define ICW2_IRQ_BASE_SLAVE ICW2_IRQ_BASE_MASTER + 8
+
+// IRQ selection (master and slave, ICW3)
+#define ICW3_MASTER_IR0 0x01
+#define ICW3_MASTER_IR1 0x02
+#define ICW3_MASTER_IR2 0x04
+#define ICW3_MASTER_IR3 0x08
+#define ICW3_MASTER_IR4 0x10
+#define ICW3_MASTER_IR5 0x20
+#define ICW3_MASTER_IR6 0x40
+#define ICW3_MASTER_IR7 0x80
+#define ICW3_SLAVE_IR0 0x00
+#define ICW3_SLAVE_IR1 0x01
+#define ICW3_SLAVE_IR2 0x02
+#define ICW3_SLAVE_IR3 0x03
+#define ICW3_SLAVE_IR4 0x04
+#define ICW3_SLAVE_IR5 0x05
+#define ICW3_SLAVE_IR6 0x06
+#define ICW3_SLAVE_IR7 0x07
+
+// special mode PIC ICW4
+#define ICW4_8086 0x01
+#define ICW4_AUTO_ACK_EOI 0x02
+#define ICW4_BUF_MASTER 0x04
+#define ICW4_BUFFER 0x08
+#define ICW4_FULLY_NESTED 0x10
+
+// OCW2
+#define OCW2_EOI 0x20 // non-specific End Of Interrupt
+
+void interrupt_handler_init_void( interrupt_handler_t *handler )
+{
+ memset( handler, 0, sizeof( interrupt_handler_t ) );
+}
+
+void interrupt_handler_init( interrupt_handler_t *handler, uint8_t interrupt_no, struct interrupt_t *interrupt, interrupt_handler_func_t handle, void *driver )
+{
+ memset( handler, 0, sizeof( interrupt_handler_t ) );
+
+ handler->interrupt_no = interrupt_no;
+ handler->interrupt = interrupt;
+ handler->handle = handle;
+ handler->driver = driver;
+}
+
+// must be global: when called via assembly code we loose the context
+// of the interrupt handler structure
+static interrupt_t *active_interrupt;
+
+void interrupts_init( interrupt_t *interrupt )
+{
+ memset( interrupt, 0, sizeof( interrupt_t ) );
+
+ interrupt_handler_t empty_interrupt_handler;
+ interrupt_handler_init_void( &empty_interrupt_handler );
+
+ for( int i = 0; i < NOF_INTERRUPTS; i++ ) {
+ interrupts_register_interrupt_gate( interrupt, i, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_ignore_request, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( empty_interrupt_handler );
+ }
+
+ // divide-by-zero exception 0x00
+ interrupt_handler_t divide_by_zero_interrupt_handler;
+ interrupt_handler_init( &divide_by_zero_interrupt_handler, 0x00, interrupt, interrupts_exception_division_by_zero, NULL );
+
+ interrupts_register_interrupt_gate( interrupt, 0x00, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_exception_0x00, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( divide_by_zero_interrupt_handler );
+
+ // IRQ 0 - PIT - programmable interrupt timer
+ interrupt_handler_t pit_interrupt_handler;
+ interrupt_handler_init( &pit_interrupt_handler, IRQ_BASE + 0x00, interrupt, interrupts_interrupt_PIT, NULL );
+
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x00, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x00, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ interrupts_register_interrupt_handler( pit_interrupt_handler );
+
+ // IRQ 1 - PS/2 keyboard
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x01, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x01, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+
+ // IRQ 12 - PS/2 mouse
+ interrupts_register_interrupt_gate( interrupt, IRQ_BASE + 0x0C, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_irq_0x0C, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+
+ port8_init( &interrupt->PIC_master_control, 0x20 );
+ port8_init( &interrupt->PIC_master_data, 0x21 );
+ port8_init( &interrupt->PIC_slave_control, 0xA0 );
+ port8_init( &interrupt->PIC_slave_data, 0xA1 );
+
+ // initialize hardware management PICs (ICW1)
+ port8_write( &interrupt->PIC_master_control, ICW1_ICW4 | ICW1_INIT );
+ port8_write( &interrupt->PIC_slave_control, ICW1_ICW4 | ICW1_INIT );
+
+ // set IRQ base of both PICS (ICW2), remap them so they
+ // don't collide with CPU exceptions (this is true after 0x20)
+ port8_write( &interrupt->PIC_master_data, ICW2_IRQ_BASE_MASTER );
+ port8_write( &interrupt->PIC_slave_data, ICW2_IRQ_BASE_SLAVE );
+
+ // use IRQ2 for master slave communication (ICW3)
+ port8_write( &interrupt->PIC_master_data, ICW3_MASTER_IR2 );
+ port8_write( &interrupt->PIC_slave_data, ICW3_SLAVE_IR2 );
+
+ // unbuffered, manual acknoledgment and 8086 mode (ICW4)
+ // TODO: use buffered mode later?
+ port8_write( &interrupt->PIC_master_data, ICW4_8086 );
+ port8_write( &interrupt->PIC_slave_data, ICW4_8086 );
+
+ // done, enable all IRQs by setting the interrupt mask register
+ // 0 for all bits. We are not interested in the old state of
+ // the mask as we are initializing in protected mode
+ // TODO: later we will mask uninmported interrupts maybe?
+ port8_write( &interrupt->PIC_master_data, 0 );
+ port8_write( &interrupt->PIC_slave_data, 0 );
+
+ // tell CPU where the IDT is
+ interrupt->idt_pointer.size = NOF_INTERRUPTS * sizeof( interrupt_gate_descriptor_t ) - 1;
+ interrupt->idt_pointer.base = (uint32_t)interrupt->descriptor_table;
+
+ interrupts_load_idt( &interrupt->idt_pointer );
+
+ active_interrupt = interrupt;
+}
+
+void interrupts_register_interrupt_gate( interrupt_t *interrupts,
+ uint8_t interrupt_no, uint16_t gdt_code_segment_selector,
+ void (*helper_handler)( ), uint8_t privilege_level, uint8_t descriptor_type )
+{
+ interrupt_gate_descriptor_t *descr = &interrupts->descriptor_table[interrupt_no];
+
+ descr->handler_address_low_bits = ( (uint32_t )helper_handler ) & 0xFFFF;
+ descr->handler_address_high_bits = ( ( (uint32_t )helper_handler ) >> 16 ) & 0xFFFF;
+ descr->gdt_code_segment_selector = gdt_code_segment_selector;
+ descr->access = IDT_PRESENT_BIT | (( privilege_level & 0x3 ) << 5 ) | descriptor_type;
+ descr->reserved = 0;
+}
+
+void interrupts_register_interrupt_handler( interrupt_handler_t handler )
+{
+ handler.interrupt->interrupt_handler[handler.interrupt_no] = handler;
+}
+
+uint32_t interrupts_exception_division_by_zero( interrupt_handler_t *handler, uint32_t esp )
+{
+ // TODO: caused by kernel code or user land code? or a task we can kill?
+ // For now, the kernel has no task management, so we panic
+ kernel_panic( "Division by zero with ESP 0x%X\n", esp );
+
+ return esp;
+}
+
+uint32_t interrupts_interrupt_PIT( interrupt_handler_t *handler, uint32_t esp )
+{
+ // for now do noting with the timer tick
+ return esp;
+}
+
+uint32_t interrupts_handle_static_interrupt( uint8_t interrupt_no, uint32_t esp )
+{
+ if( active_interrupt == NULL ) {
+ kernel_panic( "No active interrupt handler!" );
+ }
+
+ return interrupts_handle_interrupt( active_interrupt, interrupt_no, esp );
+}
+
+uint32_t interrupts_handle_interrupt( interrupt_t *interrupt, uint8_t interrupt_no, uint32_t esp )
+{
+ interrupt_handler_t *handler = &interrupt->interrupt_handler[interrupt_no];
+
+ if( handler->handle == NULL ) {
+ kernel_panic( "Unhandled interrupt 0x%X with ESP 0x%X\n", interrupt_no, esp );
+ }
+
+ uint32_t new_esp = handler->handle( handler, esp );
+
+ // send ACK to PIC for hardware interrups
+ if( interrupt_no >= IRQ_BASE && interrupt_no <= IRQ_BASE + 16 ) {
+ port8_write( &interrupt->PIC_master_control, OCW2_EOI );
+ if( interrupt_no >= IRQ_BASE + 8 ) {
+ port8_write( &interrupt->PIC_slave_control, OCW2_EOI );
+ }
+ }
+
+ return new_esp;
+}
diff --git a/src/hardware/interrupts.h b/src/hardware/interrupts.h
new file mode 100644
index 0000000..8ab485e
--- /dev/null
+++ b/src/hardware/interrupts.h
@@ -0,0 +1,103 @@
+#ifndef INTERRUPTS_H
+#define INTERRUPTS_H
+
+#include <stdint.h>
+
+#include "port.h"
+
+// total number of supported interrupts
+#define NOF_INTERRUPTS 256
+
+// offset for hardware interrupts
+#define IRQ_BASE 0x20
+
+// TCC 0.9.26 bug?, __attribute__( ( packed ) ) on structs not working,
+// also not working on members.. resorting to pragmas
+#if defined( __TINYC__ )
+#pragma pack(1)
+#endif
+
+// packed IDT structure, note TCC needs every member to have a packed
+// attribute, GCC/Clang are happy with the packed attribute at the
+// structure level
+typedef struct interrupt_gate_descriptor_t {
+ uint16_t handler_address_low_bits;
+ uint16_t gdt_code_segment_selector;
+ uint8_t reserved;
+ uint8_t access;
+ uint16_t handler_address_high_bits;
+} __attribute__( ( packed ) ) interrupt_gate_descriptor_t;
+
+#if defined( __TINYC__ )
+#pragma pack()
+#endif
+
+#if defined( __TINYC__ )
+#pragma pack(1)
+#endif
+
+typedef struct {
+ uint16_t size;
+ uint32_t base;
+} __attribute__( ( packed ) ) interrupt_descriptor_table_pointer_t;
+
+#if defined( __TINYC__ )
+#pragma pack()
+#endif
+
+struct interrupt_t;
+struct interrupt_handler_t;
+
+typedef uint32_t (*interrupt_handler_func_t)( struct interrupt_handler_t *handler, uint32_t esp );
+
+typedef struct interrupt_handler_t {
+ uint8_t interrupt_no;
+ struct interrupt_t *interrupt;
+ interrupt_handler_func_t handle;
+ void *driver;
+} interrupt_handler_t;
+
+void interrupt_handler_init_void( interrupt_handler_t *handler );
+void interrupt_handler_init( interrupt_handler_t *handler, uint8_t interrupt_no, struct interrupt_t *interrupt, interrupt_handler_func_t handle, void *driver );
+
+typedef struct interrupt_t {
+ interrupt_descriptor_table_pointer_t idt_pointer;
+ interrupt_gate_descriptor_t descriptor_table[NOF_INTERRUPTS];
+ interrupt_handler_t interrupt_handler[NOF_INTERRUPTS];
+ // PIC master control register: command and status register
+ port8_t PIC_master_control;
+ // PIC master data register: interrupt mask and data register
+ port8_t PIC_master_data;
+ // since PC/AT we always have a slave PIC, we don't support the PC/XT architecture
+ // with just one PIC
+ port8_t PIC_slave_control;
+ port8_t PIC_slave_data;
+} interrupt_t;
+
+void interrupts_enable( void );
+void interrupts_disable( void );
+void interrupts_init( interrupt_t *interrupt );
+void interrupts_register_interrupt_gate( interrupt_t *interrupt,
+ uint8_t interrupt_no, uint16_t gdt_code_segment_selector,
+ void (*helper_handler)( ), uint8_t privilege_level, uint8_t descriptor_type );
+void interrupts_register_interrupt_handler( interrupt_handler_t handler );
+
+void interrupts_load_idt( interrupt_descriptor_table_pointer_t *idt_pointer );
+
+uint32_t interrupts_handle_interrupt( interrupt_t *interrupt, uint8_t interrupt_no, uint32_t esp );
+
+uint32_t interrupts_handle_static_interrupt( uint8_t interrupt_no, uint32_t esp );
+
+uint32_t interrupts_exception_division_by_zero( interrupt_handler_t *handler, uint32_t esp );
+
+uint32_t interrupts_interrupt_PIT( interrupt_handler_t *handler, uint32_t esp );
+
+void interrupts_ignore_request( );
+void interrupts_handle_exception_0x00( );
+void interrupts_handle_irq_0x00( );
+void interrupts_handle_irq_0x01( );
+void interrupts_handle_irq_0x0C( );
+
+// later: tasks and stacks, queues
+
+#endif // INTERRUPTS_H
diff --git a/src/hardware/pci.c b/src/hardware/pci.c
new file mode 100644
index 0000000..6b124b2
--- /dev/null
+++ b/src/hardware/pci.c
@@ -0,0 +1,187 @@
+#include "pci.h"
+
+#include "string.h"
+#include "stdio.h"
+
+#define NOF_BUSES 8
+#define NOF_DEVICES_PER_BUS 32
+#define NOF_FUNCTIONS_PER_DEVICE 8
+#define NOF_BARS 6
+
+#define PCI_VENDOR_ID 0x00
+#define PCI_DEVICE_ID 0x02
+#define PCI_REVISION 0x08
+#define PCI_INTERFACE 0x09
+#define PCI_SUBCLASS 0x0A
+#define PCI_CLASS 0x0B
+#define PCI_HEADER_TYPE 0x0E
+#define PCI_BAR_BASE 0x10
+
+void pci_controller_init( pci_controller_t *controller )
+{
+ memset( controller, 0, sizeof( pci_controller_t ) );
+
+ port32_init( &controller->command_port, 0xCF8 );
+ port32_init( &controller->data_port, 0xCFC );
+}
+
+static uint32_t compute_id( uint16_t bus, uint16_t device, uint16_t function, uint32_t offset )
+{
+ uint32_t id;
+
+ id = ( 0x01 << 31 )
+ | ( ( bus & 0xFF ) << 16 )
+ | ( ( device & 0x1F ) << 11 )
+ | ( ( function & 0x07 ) << 8 )
+ | ( offset & 0xFC );
+
+ return id;
+}
+
+uint16_t pci_controller_read( pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint32_t offset )
+{
+ uint32_t id = compute_id( bus, device, function, offset );
+
+ port32_write( &controller->command_port, id );
+
+ uint32_t data = port32_read( &controller->data_port );
+
+ // we get 256 byte data blocks aligned to 32-bit addresses
+ // for each offset
+ data = data >> ( ( 8 * ( offset % 4 ) ) & 0xFFFF );
+
+ return data;
+}
+
+void pci_controller_write( pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint32_t offset, uint32_t data )
+{
+ uint32_t id = compute_id( bus, device, function, offset );
+
+ port32_write( &controller->command_port, id );
+ port32_write( &controller->data_port, data );
+}
+
+void pci_controller_scan_and_register( pci_controller_t *controller, driver_manager_t *driver_manager, interrupt_t *interrupt )
+{
+ for( int bus = 0; bus < NOF_BUSES; bus++ ) {
+ for( int device = 0; device < NOF_DEVICES_PER_BUS; device++ ) {
+ // TODO: detect single/multi function device from header
+ for( int function = 0; function < NOF_FUNCTIONS_PER_DEVICE; function++ ) {
+ pci_device_descriptor_t device_descriptor;
+ pci_device_descriptor_init( &device_descriptor, controller, bus, device, function );
+
+ // no device
+ if( device_descriptor.device_id == 0x0 ||
+ device_descriptor.device_id == 0xFFFF ) {
+ continue;
+ }
+
+ printf( "%X:%X.%d: %X:%X (interface: %X, class %X:%X, rev: %d)\n",
+ bus, device, function,
+ device_descriptor.vendor_id,
+ device_descriptor.device_id,
+ device_descriptor.interface_id,
+ device_descriptor.class_id,
+ device_descriptor.subclass_id,
+ device_descriptor.revision_id );
+
+ uint8_t header_type = pci_controller_read( controller, bus, device, function, PCI_HEADER_TYPE ) & 0x7F;
+ int max_nof_bars = NOF_BARS;
+ if( header_type & 0x01 ) {
+ max_nof_bars = 2;
+ }
+
+ for( int bar_number = 0; bar_number < max_nof_bars; bar_number++ ) {
+ pci_base_address_register_t bar;
+ pci_base_address_register_init( &bar, controller, bus, device, function, bar_number );
+ if( bar.type == BASE_ADDRESS_REGISTER_TYPE_IO && bar.addr ) {
+ device_descriptor.port_base = (uint32_t)bar.addr;
+ }
+
+ driver_t *driver = pci_device_get_driver( &device_descriptor, interrupt );
+ if( driver ) {
+ driver_manager_add_driver( driver_manager, driver );
+ }
+ }
+ }
+ }
+ }
+}
+
+void pci_device_descriptor_init( pci_device_descriptor_t *descriptor, pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function )
+{
+ memset( descriptor, 0, sizeof( pci_device_descriptor_t ) );
+
+ descriptor->vendor_id = pci_controller_read( controller, bus, device, function, PCI_VENDOR_ID );
+ descriptor->device_id = pci_controller_read( controller, bus, device, function, PCI_DEVICE_ID );
+ descriptor->class_id = pci_controller_read( controller, bus, device, function, PCI_CLASS );
+ descriptor->subclass_id = pci_controller_read( controller, bus, device, function, PCI_SUBCLASS );
+ descriptor->interface_id = pci_controller_read( controller, bus, device, function, PCI_INTERFACE );
+ descriptor->revision_id = pci_controller_read( controller, bus, device, function, PCI_REVISION );
+}
+
+void pci_base_address_register_init( pci_base_address_register_t *base_address_register, pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint16_t bar )
+{
+ memset( base_address_register, 0, sizeof( pci_base_address_register_t ) );
+
+ uint32_t val = pci_controller_read( controller, bus, device, function, PCI_BAR_BASE + sizeof( uint32_t ) * bar );
+
+ // bit 0 indicates whether we are I/O or memory mapped
+ base_address_register->type = ( val & 0x01 ) ? BASE_ADDRESS_REGISTER_TYPE_IO : BASE_ADDRESS_REGISTER_TYPE_MEMORY_MAPPED;
+
+ switch( base_address_register->type ) {
+ case BASE_ADDRESS_REGISTER_TYPE_IO:
+ base_address_register->addr = (uint8_t *)( val & 0xFFFC );
+ base_address_register->prefetchable = false;
+ break;
+
+ case BASE_ADDRESS_REGISTER_TYPE_MEMORY_MAPPED:
+ switch( val << 1 & 0x03 ) {
+ case 0x00:
+ // 32-bit mode
+ break;
+
+ case 0x01:
+ // 20-bit mode
+ break;
+
+ case 0x02:
+ // 64-bit mode
+ break;
+ }
+ break;
+ }
+}
+
+driver_t *pci_device_get_driver( pci_device_descriptor_t *descriptor, interrupt_t *interrupt )
+{
+ // find a specific device
+ switch( descriptor->vendor_id ) {
+ case 0x10ec: // Realtek Semiconductor
+ switch( descriptor->device_id ) {
+ case 0x8029: // RTL-8029
+ break;
+ }
+ break;
+
+ case 0x8086: // Intel
+ break;
+
+ default:
+ break;
+ }
+
+ // find a generic driver
+ switch( descriptor->class_id ) {
+ case 0x03: // graphics
+ switch( descriptor->subclass_id ) {
+ case 0x00: // VGA
+ break;
+ }
+ break;
+
+ default: // no driver found
+ break;
+ }
+ return NULL;
+}
diff --git a/src/hardware/pci.h b/src/hardware/pci.h
new file mode 100644
index 0000000..ddbc990
--- /dev/null
+++ b/src/hardware/pci.h
@@ -0,0 +1,49 @@
+#ifndef PCI_H
+#define PCI_H
+
+#include <stdbool.h>
+
+#include "port.h"
+#include "interrupts.h"
+#include "driver.h"
+
+typedef struct {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint8_t class_id;
+ uint8_t subclass_id;
+ uint8_t interface_id;
+ uint8_t revision_id;
+ uint32_t port_base;
+} pci_device_descriptor_t;
+
+typedef struct {
+ port32_t command_port;
+ port32_t data_port;
+} pci_controller_t;
+
+typedef enum {
+ BASE_ADDRESS_REGISTER_TYPE_IO,
+ BASE_ADDRESS_REGISTER_TYPE_MEMORY_MAPPED
+} pci_base_address_register_type_t;
+
+typedef struct {
+ pci_base_address_register_type_t type;
+ bool prefetchable;
+ uint8_t *addr;
+ uint32_t *size;
+} pci_base_address_register_t;
+
+void pci_controller_init( pci_controller_t *controller );
+
+uint16_t pci_controller_read( pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint32_t offset );
+void pci_controller_write( pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint32_t offset, uint32_t data );
+void pci_controller_scan_and_register( pci_controller_t *controller, driver_manager_t *driver_manager, interrupt_t *interrupt );
+
+void pci_device_descriptor_init( pci_device_descriptor_t *descriptor, pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function );
+
+void pci_base_address_register_init( pci_base_address_register_t *base_address_register, pci_controller_t *controller, uint16_t bus, uint16_t device, uint16_t function, uint16_t bar );
+
+driver_t *pci_device_get_driver( pci_device_descriptor_t *descriptor, interrupt_t *interrupt );
+
+#endif // PCI_H
diff --git a/src/hardware/port.asm b/src/hardware/port.asm
new file mode 100644
index 0000000..555a4e8
--- /dev/null
+++ b/src/hardware/port.asm
@@ -0,0 +1,49 @@
+[bits 32]
+
+global port8_write
+global port8_read
+
+; void port8_write( port8_t *port, uint8_t data );
+port8_write:
+ push ebp
+ mov ebp, esp
+ mov ecx, [ebp+8]
+ mov edx, DWORD [ecx]
+ movzx ax, BYTE [ebp+12]
+ out dx, al
+ leave
+ ret
+
+; uint8_t port8_read( port8_t *port )
+port8_read:
+ push ebp
+ mov ebp, esp
+ mov ecx, DWORD [ebp+8]
+ mov edx, [ecx]
+ in al, dx
+ leave
+ ret
+
+global port32_write
+global port32_read
+
+; void port32_write( port32_t *port, uint32_t data );
+port32_write:
+ push ebp
+ mov ebp, esp
+ mov ecx, [ebp+8]
+ mov edx, DWORD [ecx]
+ mov eax, DWORD [ebp+12]
+ out dx, eax
+ leave
+ ret
+
+; uint32_t port32_read( port32_t *port )
+port32_read:
+ push ebp
+ mov ebp, esp
+ mov ecx, DWORD [ebp+8]
+ mov edx, [ecx]
+ in eax, dx
+ leave
+ ret
diff --git a/src/hardware/port.c b/src/hardware/port.c
new file mode 100644
index 0000000..46e7f37
--- /dev/null
+++ b/src/hardware/port.c
@@ -0,0 +1,17 @@
+#include "port.h"
+
+#include "string.h"
+
+void port8_init( port8_t *port, uint16_t number )
+{
+ memset( port, 0, sizeof( port8_t ) );
+
+ port->number = number;
+}
+
+void port32_init( port32_t *port, uint16_t number )
+{
+ memset( port, 0, sizeof( port32_t ) );
+
+ port->number = number;
+}
diff --git a/src/hardware/port.h b/src/hardware/port.h
new file mode 100644
index 0000000..ec96d09
--- /dev/null
+++ b/src/hardware/port.h
@@ -0,0 +1,23 @@
+#ifndef PORT_H
+#define PORT_H
+
+#include <stdint.h>
+
+typedef struct {
+ uint16_t number; // port number, e.g. 0x3d4 VGA index register
+} port8_t;
+
+void port8_init( port8_t *port, uint16_t number );
+void port8_write( port8_t *port, uint8_t data );
+uint8_t port8_read( port8_t *port );
+
+typedef struct {
+ uint16_t number; // port number, e.g. 0x3d4 VGA index register
+} port32_t;
+
+void port32_init( port32_t *port, uint16_t number );
+void port32_write( port32_t *port, uint32_t data );
+uint32_t port32_read( port32_t *port );
+
+#endif // PORT_H
+