Implement driver architecture with linker-section registration (AI)
- Created driver framework with probe/init lifecycle. Drivers register via REGISTER_DRIVER macro which places pointers in a .drivers linker section. - During boot, init_drivers() iterates the section, probes each driver (checking if hardware is present), and initializes those that respond OK. - Added .drivers section to linker.ld with __drivers_start/__drivers_end symbols for iteration. - Also added .rodata.* pattern to the .rodata section for string literals placed in sub-sections by the compiler. - No drivers are registered yet; the VGA driver will be the first. Tested: boots cleanly with driver scan completing (0 registered, 0 loaded).
This commit is contained in:
@@ -47,7 +47,7 @@ Once a task is completed, it should be checked off.
|
|||||||
- [x] Create a physical memory allocator and mapper. The kernel should live in the upper last gigabyte of virtual memory. It should support different zones (e.g.: `SUB_16M`, `DEFAULT`, ...) These zones describe the region of memory that memory should be allocated in. If it is not possible to allocate in that region (because it is full, or has 0 capacity to begin with), it should fallback to another zone.
|
- [x] Create a physical memory allocator and mapper. The kernel should live in the upper last gigabyte of virtual memory. It should support different zones (e.g.: `SUB_16M`, `DEFAULT`, ...) These zones describe the region of memory that memory should be allocated in. If it is not possible to allocate in that region (because it is full, or has 0 capacity to begin with), it should fallback to another zone.
|
||||||
- [x] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
|
- [x] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
|
||||||
- [x] Create a memory allocator. This should provide the kernel with `malloc` and `free`. Internally, it should use the paging subsystem to ensure that the address it returns have actual RAM paged to them.
|
- [x] Create a memory allocator. This should provide the kernel with `malloc` and `free`. Internally, it should use the paging subsystem to ensure that the address it returns have actual RAM paged to them.
|
||||||
- [ ] Create an initial driver architecture, allowing different drivers included in the kernel to test whether they should load or not.
|
- [x] Create an initial driver architecture, allowing different drivers included in the kernel to test whether they should load or not.
|
||||||
- [ ] Create a VGA driver. On startup, some memory statistics should be displayed, as well as boot progress.
|
- [ ] Create a VGA driver. On startup, some memory statistics should be displayed, as well as boot progress.
|
||||||
- [ ] Create subsystem for loading new processes in Ring 3.
|
- [ ] Create subsystem for loading new processes in Ring 3.
|
||||||
- [ ] Update the build script to generate a ramdisk containing any applications to run. This initial ramdisk is in CPIO format.
|
- [ ] Update the build script to generate a ramdisk containing any applications to run. This initial ramdisk is in CPIO format.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ add_executable(kernel
|
|||||||
paging.c
|
paging.c
|
||||||
kmalloc.c
|
kmalloc.c
|
||||||
string.c
|
string.c
|
||||||
|
driver.c
|
||||||
interrupts.S
|
interrupts.S
|
||||||
kernel.c
|
kernel.c
|
||||||
)
|
)
|
||||||
|
|||||||
72
src/driver.c
Normal file
72
src/driver.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @file driver.c
|
||||||
|
* @brief Driver framework implementation.
|
||||||
|
*
|
||||||
|
* Iterates over all driver descriptors placed in the .drivers linker section
|
||||||
|
* by the REGISTER_DRIVER macro. Each driver is probed and, if the probe
|
||||||
|
* succeeds, initialized.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "driver.h"
|
||||||
|
#include "port_io.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* Debug print helpers defined in kernel.c */
|
||||||
|
extern void offset_print(const char *str);
|
||||||
|
extern void print_hex(uint32_t val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linker-provided symbols marking the start and end of the .drivers section.
|
||||||
|
* Each entry is a pointer to a driver_t.
|
||||||
|
*/
|
||||||
|
extern const driver_t *__drivers_start[];
|
||||||
|
extern const driver_t *__drivers_end[];
|
||||||
|
|
||||||
|
void init_drivers(void) {
|
||||||
|
const driver_t **drv;
|
||||||
|
int loaded = 0;
|
||||||
|
int skipped = 0;
|
||||||
|
int failed = 0;
|
||||||
|
|
||||||
|
offset_print(" DRIVERS: scanning registered drivers...\n");
|
||||||
|
|
||||||
|
for (drv = __drivers_start; drv < __drivers_end; drv++) {
|
||||||
|
const driver_t *d = *drv;
|
||||||
|
if (!d || !d->name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_print(" DRIVERS: probing ");
|
||||||
|
offset_print(d->name);
|
||||||
|
offset_print("... ");
|
||||||
|
|
||||||
|
/* Run probe function if provided */
|
||||||
|
if (d->probe) {
|
||||||
|
driver_probe_result_t result = d->probe();
|
||||||
|
if (result == DRIVER_PROBE_NOT_FOUND) {
|
||||||
|
offset_print("not found\n");
|
||||||
|
skipped++;
|
||||||
|
continue;
|
||||||
|
} else if (result == DRIVER_PROBE_ERROR) {
|
||||||
|
offset_print("probe error\n");
|
||||||
|
failed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run init function */
|
||||||
|
if (d->init) {
|
||||||
|
int ret = d->init();
|
||||||
|
if (ret != 0) {
|
||||||
|
offset_print("init failed\n");
|
||||||
|
failed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_print("loaded\n");
|
||||||
|
loaded++;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset_print(" DRIVERS: done\n");
|
||||||
|
}
|
||||||
56
src/driver.h
Normal file
56
src/driver.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* @file driver.h
|
||||||
|
* @brief Kernel driver architecture.
|
||||||
|
*
|
||||||
|
* Provides a simple framework for registering and initializing kernel drivers.
|
||||||
|
* Each driver provides a probe function that returns whether it should load,
|
||||||
|
* and an init function that performs the actual initialization.
|
||||||
|
*
|
||||||
|
* Drivers are registered at compile time using the REGISTER_DRIVER macro,
|
||||||
|
* which places driver descriptors in a special linker section. The kernel
|
||||||
|
* iterates over all registered drivers during boot.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRIVER_H
|
||||||
|
#define DRIVER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/** Driver probe result codes. */
|
||||||
|
typedef enum {
|
||||||
|
DRIVER_PROBE_OK = 0, /**< Driver should be loaded. */
|
||||||
|
DRIVER_PROBE_NOT_FOUND = 1, /**< Hardware not found, skip this driver. */
|
||||||
|
DRIVER_PROBE_ERROR = 2 /**< Error during probing. */
|
||||||
|
} driver_probe_result_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Driver descriptor structure.
|
||||||
|
*
|
||||||
|
* Each driver provides a name, a probe function (to test if hardware is
|
||||||
|
* present), and an init function (to set up the driver).
|
||||||
|
*/
|
||||||
|
typedef struct driver {
|
||||||
|
const char *name; /**< Human-readable driver name. */
|
||||||
|
driver_probe_result_t (*probe)(void); /**< Probe function. Returns DRIVER_PROBE_OK if driver should load. */
|
||||||
|
int (*init)(void); /**< Init function. Returns 0 on success, non-zero on failure. */
|
||||||
|
} driver_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a driver.
|
||||||
|
*
|
||||||
|
* Places the driver descriptor in the .drivers linker section so it
|
||||||
|
* is automatically discovered during boot.
|
||||||
|
*/
|
||||||
|
#define REGISTER_DRIVER(drv) \
|
||||||
|
static const driver_t * __attribute__((used, section(".drivers"))) \
|
||||||
|
_driver_##drv = &(drv)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize all registered drivers.
|
||||||
|
*
|
||||||
|
* Iterates over the .drivers section, probes each driver, and initializes
|
||||||
|
* those that respond positively to probing.
|
||||||
|
*/
|
||||||
|
void init_drivers(void);
|
||||||
|
|
||||||
|
#endif /* DRIVER_H */
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "pmm.h"
|
#include "pmm.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "kmalloc.h"
|
#include "kmalloc.h"
|
||||||
|
#include "driver.h"
|
||||||
|
|
||||||
void offset_print(const char *str)
|
void offset_print(const char *str)
|
||||||
{
|
{
|
||||||
@@ -67,6 +68,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
|||||||
init_kmalloc();
|
init_kmalloc();
|
||||||
offset_print("Memory allocator initialized\n");
|
offset_print("Memory allocator initialized\n");
|
||||||
|
|
||||||
|
init_drivers();
|
||||||
|
offset_print("Drivers initialized\n");
|
||||||
|
|
||||||
/* Test kmalloc/kfree */
|
/* Test kmalloc/kfree */
|
||||||
uint32_t *test_alloc = (uint32_t *)kmalloc(64);
|
uint32_t *test_alloc = (uint32_t *)kmalloc(64);
|
||||||
if (test_alloc) {
|
if (test_alloc) {
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ SECTIONS
|
|||||||
.rodata BLOCK(4K) : ALIGN(4K)
|
.rodata BLOCK(4K) : ALIGN(4K)
|
||||||
{
|
{
|
||||||
*(.rodata)
|
*(.rodata)
|
||||||
|
*(.rodata.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.drivers BLOCK(4K) : ALIGN(4K)
|
||||||
|
{
|
||||||
|
__drivers_start = .;
|
||||||
|
KEEP(*(.drivers))
|
||||||
|
__drivers_end = .;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data BLOCK(4K) : ALIGN(4K)
|
.data BLOCK(4K) : ALIGN(4K)
|
||||||
|
|||||||
Reference in New Issue
Block a user