mov eax , cr0
or eax , 0x01
mov cr0 , eax



back to months list

Project : The "Microkernel" Operating System

Journal Entry Date : 2024.12.25

Merry Christmas! I brought some christmas present for the "Microkernel" Project!!!

This new present will (somewhat) revolutionize the development of device drivers and lot more!

Previously, we had to manually add the driver initialization function to the array in order to register the driver. Now, with the help of linker script and sections, we no longer have to manually add the driver to the array! Just like Linux's device driver, we now can add driver using just one line of macro.

static void init_ide_driver(void) {
    io_write_byte(IDE_DEVICECONTROL_PRIMARY_BASE+IDE_PORT_DIGITAL_OUTPUT , 0);
    io_write_byte(IDE_DEVICECONTROL_SECONDARY_BASE+IDE_PORT_DIGITAL_OUTPUT , 0);
    interrupt::general::register_interrupt(32+14 , ide::interrupt_handler_irq14 , INTERRUPT_HANDLER_LEVEL_KERNEL|INTERRUPT_HANDLER_HARDWARE);
    interrupt::general::register_interrupt(32+15 , ide::interrupt_handler_irq15 , INTERRUPT_HANDLER_LEVEL_KERNEL|INTERRUPT_HANDLER_HARDWARE);
    blockdev::register_driver(new ide_driver , "idehd");
    blockdev::register_driver(new ide_cd_driver , "idecd");
}

REGISTER_DRIVER(init_ide_driver)

The secret behind this magic is astonishingly simple. We just have to shove the pointers of all the functions into a section, making it an array of functions. What we have to do is to create a new section in the linker script and locate the function pointers to the created section.

SECTIONS {
  . = 0x100000;
  .entry 0x100000 : {
    *(.entry) *(.entry.*)
  }
  .text ALIGN(4096) : {
    *(.text) *(.text.*)
  }
  ...
  .drivers_init ALIGN(4096) : {
    __drivers_init_start__ = .;
    *(.drivers_init) *(.drivers_init.*)
    __drivers_init_end__ = .;
  }
  ...
}

Because we have to provide where the section is, we make two variable that will be passed on to our kernel, that respectively provides the start and the end of the section.

(Note: we Do Not store the entire function in the section. We are making the array-like section that only stores the "pointer" of the function.)

We use the attribute keyword(just like before) to store the pointer of the function. Simply declare the variable that stores the location of function, and store it to the section!

typedef void(*driver_init_func_ptr_t)(void);

#define REGISTER_DRIVER_PRIORITY(init_driver , priority) void __register_driver_init_##priority_##init_driver(void) { init_driver(); } \
static __attribute__ ((section(".drivers_init."#priority))) driver_init_func_ptr_t __device_driver_init_##priority_##init_driver  = __register_driver_init_##priority_##init_driver; 

#define REGISTER_DRIVER(init_driver) REGISTER_DRIVER_PRIORITY(init_driver , 0)

Here, we can see that we first declare the wrapper function that calls the actual function that does the initialization. After that, the function pointer to the wrapper is stored to the ".driver.init" section. We also add the option to change the name of the section with the given priority, in case we may have to set the priorities of executing the function.

Say goodbye to useless arrays of functions now! Just like Linux, we now have an elegant solution to registering the device drivers!