Function comments (v1)
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,3 +12,5 @@ symbols.map
|
|||||||
symbols.S
|
symbols.S
|
||||||
*.log
|
*.log
|
||||||
build/
|
build/
|
||||||
|
compile_commands.json
|
||||||
|
.cache/
|
||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
|
First install the dependencies: `sudo apt install python3 xorriso make qemu-system`
|
||||||
|
|
||||||
|
Also, you have to get an x86_64 toolchain for compilation. The easiest way to do that on most systems is to install it from Homebrew:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install x86_64-elf-gcc
|
||||||
|
```
|
||||||
|
|
||||||
Then, to compile the kernel and make an ISO image file: `make build-iso`
|
Then, to compile the kernel and make an ISO image file: `make build-iso`
|
||||||
To run it with QEMU, `make run`
|
To run it with QEMU, `make run`
|
||||||
|
|
||||||
|
|||||||
@@ -71,12 +71,16 @@ Function prototypes should include parameter names and their data types.
|
|||||||
|
|
||||||
## Commenting
|
## Commenting
|
||||||
|
|
||||||
Comments should describe what a function does and why, not how it does it.
|
Comments should describe what a function does and why, not how it does it. The preferred way of commenting functions is the following:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
/*
|
/*
|
||||||
* This is the preferred style for multi-line
|
* function_name - Function brief description
|
||||||
* comments in the Pepper kernel
|
* @arg1: Argument 1 description
|
||||||
|
* @arg2: Argument 2 description
|
||||||
|
*
|
||||||
|
* A longer description can be featured here, explaining more
|
||||||
|
* in detail what the function does and why it does it.
|
||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* @author xamidev <xamidev@riseup.net>
|
* @author xamidev <xamidev@riseup.net>
|
||||||
* @brief Limine requests for boot
|
* @brief Limine requests for boot
|
||||||
|
* @description
|
||||||
|
* The kernel makes a few requests to the Limine bootloader
|
||||||
|
* in order to get precious information about the system.
|
||||||
|
* We get a framebuffer, a memory map, the address of the
|
||||||
|
* kernel in memory, and the Higher Half Direct Map offset.
|
||||||
* @license GPL-3.0-only
|
* @license GPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -39,4 +39,7 @@
|
|||||||
/* term */
|
/* term */
|
||||||
#define TERM_HISTORY_MAX_LINES 256
|
#define TERM_HISTORY_MAX_LINES 256
|
||||||
|
|
||||||
|
/* time */
|
||||||
|
#define TIMER_FREQUENCY 1000
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,7 +10,14 @@
|
|||||||
|
|
||||||
extern struct boot_context boot_ctx;
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
// Display the memmap so we see how the memory is laid out at handoff
|
/*
|
||||||
|
* memmap_display - displays a memory map
|
||||||
|
* @response: Limine memory map response
|
||||||
|
*
|
||||||
|
* Displays the memory map we get from Limine
|
||||||
|
* to see different regions, their sizes, and
|
||||||
|
* how the memory is laid out at handoff.
|
||||||
|
*/
|
||||||
void memmap_display(struct limine_memmap_response* response)
|
void memmap_display(struct limine_memmap_response* response)
|
||||||
{
|
{
|
||||||
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
|
DEBUG("Got memory map from Limine: revision %u, %u entries", response->revision, response->entry_count);
|
||||||
@@ -51,12 +58,18 @@ void memmap_display(struct limine_memmap_response* response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the HHDM
|
/*
|
||||||
|
* hhdm_display - displays the HHDM offset
|
||||||
|
* @hhdm: Limine HHDM offset response
|
||||||
|
*/
|
||||||
void hhdm_display(struct limine_hhdm_response* hhdm)
|
void hhdm_display(struct limine_hhdm_response* hhdm)
|
||||||
{
|
{
|
||||||
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
DEBUG("Got HHDM revision=%u offset=0x%p", hhdm->revision, hhdm->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* boot_mem_display - displays all memory info
|
||||||
|
*/
|
||||||
void boot_mem_display()
|
void boot_mem_display()
|
||||||
{
|
{
|
||||||
memmap_display(boot_ctx.mmap);
|
memmap_display(boot_ctx.mmap);
|
||||||
|
|||||||
@@ -11,6 +11,15 @@
|
|||||||
|
|
||||||
extern struct init_status init;
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* panic - Kernel panic
|
||||||
|
* @ctx: CPU context (optional)
|
||||||
|
* @str: Error message
|
||||||
|
*
|
||||||
|
* Ends execution of the kernel in case of an unrecoverable error.
|
||||||
|
* Will display to terminal if it is initialized, otherwise serial only.
|
||||||
|
* Can be called with or without a CPU context.
|
||||||
|
*/
|
||||||
void panic(struct cpu_status_t* ctx, const char* str)
|
void panic(struct cpu_status_t* ctx, const char* str)
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
|
|||||||
@@ -9,6 +9,13 @@
|
|||||||
|
|
||||||
extern struct init_status init;
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* debug_stack_trace - Prints the stack trace
|
||||||
|
* @max_frames: Maximum amount of stack frames to walk
|
||||||
|
*
|
||||||
|
* Walks back the stack and gets all return values (RIP)
|
||||||
|
* and prints them to the DEBUG interface.
|
||||||
|
*/
|
||||||
void debug_stack_trace(unsigned int max_frames)
|
void debug_stack_trace(unsigned int max_frames)
|
||||||
{
|
{
|
||||||
DEBUG("*** begin stack trace ***");
|
DEBUG("*** begin stack trace ***");
|
||||||
@@ -52,7 +59,16 @@ typedef struct {
|
|||||||
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
__attribute__((weak)) extern kernel_symbol_t symbol_table[];
|
||||||
__attribute__((weak)) extern uint64_t symbol_count;
|
__attribute__((weak)) extern uint64_t symbol_count;
|
||||||
|
|
||||||
// binary search
|
/*
|
||||||
|
* debug_find_symbol - Finds the symbol name associated to an address
|
||||||
|
* @rip: Pointer to executable code
|
||||||
|
* @offset: Out pointer to reference the offset in the found function, if any
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <symbol name> - symbol name
|
||||||
|
* "???" - no symbol table found
|
||||||
|
* "unknown" - symbol table found, but address isn't in the table
|
||||||
|
*/
|
||||||
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
const char* debug_find_symbol(uintptr_t rip, uintptr_t* offset)
|
||||||
{
|
{
|
||||||
if (!symbol_table || symbol_count == 0) {
|
if (!symbol_table || symbol_count == 0) {
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ extern char vector_0_handler[];
|
|||||||
// Timer ticks
|
// Timer ticks
|
||||||
extern volatile uint64_t ticks;
|
extern volatile uint64_t ticks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_set_entry - Sets an Interrupt Descriptor Table entry
|
||||||
|
* @vector: Vector number in the IDT
|
||||||
|
* @handler: Pointer to the executable Interrupt Service Routine
|
||||||
|
* @dpl: Desired privilege level
|
||||||
|
*/
|
||||||
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
||||||
{
|
{
|
||||||
uint64_t handler_addr = (uint64_t)handler;
|
uint64_t handler_addr = (uint64_t)handler;
|
||||||
@@ -42,6 +48,10 @@ void idt_set_entry(uint8_t vector, void* handler, uint8_t dpl)
|
|||||||
entry->ist = 0;
|
entry->ist = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_load - Loads the Interrupt Descriptor Table
|
||||||
|
* @idt_addr: Address to the IDT
|
||||||
|
*/
|
||||||
void idt_load(void* idt_addr)
|
void idt_load(void* idt_addr)
|
||||||
{
|
{
|
||||||
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
// "limit" = "size" = Size of the IDT - 1 byte = (16*256)-1 = 0xFFF
|
||||||
@@ -50,9 +60,14 @@ void idt_load(void* idt_addr)
|
|||||||
asm volatile("lidt %0" :: "m"(idt_reg));
|
asm volatile("lidt %0" :: "m"(idt_reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* idt_init - Initializes the Interrupt Descriptor Table
|
||||||
|
*
|
||||||
|
* Sets all IDT entries and their corresponding service routines,
|
||||||
|
* then loads it.
|
||||||
|
*/
|
||||||
void idt_init()
|
void idt_init()
|
||||||
{
|
{
|
||||||
// Hardcoded...
|
|
||||||
for (size_t i=0; i<=KERNEL_IDT_ENTRIES; i++) {
|
for (size_t i=0; i<=KERNEL_IDT_ENTRIES; i++) {
|
||||||
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
|
// Each vector handler is 16-byte aligned, so <vector_no>*16 = address of that handler
|
||||||
idt_set_entry(i, vector_0_handler + (i*16), 0);
|
idt_set_entry(i, vector_0_handler + (i*16), 0);
|
||||||
@@ -61,6 +76,15 @@ void idt_init()
|
|||||||
DEBUG("IDT initialized");
|
DEBUG("IDT initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read_cr2 - Reads the CR2 register
|
||||||
|
*
|
||||||
|
* This function is useful because it gets the address
|
||||||
|
* that the CPU tried to access in the case of a #PF.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %val - CR2 register value
|
||||||
|
*/
|
||||||
static inline uint64_t read_cr2(void)
|
static inline uint64_t read_cr2(void)
|
||||||
{
|
{
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
@@ -68,6 +92,15 @@ static inline uint64_t read_cr2(void)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* page_fault_handler - Handler for #PF
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a #PF, especially what instruction (RIP)
|
||||||
|
* caused it, and what address access (CR2) caused it.
|
||||||
|
* Also displays an interpretation of the thrown error code.
|
||||||
|
* Then halts the system. We could implement demand paging later.
|
||||||
|
*/
|
||||||
static void page_fault_handler(struct cpu_status_t* ctx)
|
static void page_fault_handler(struct cpu_status_t* ctx)
|
||||||
{
|
{
|
||||||
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
|
// It could be used to remap pages etc. to fix the fault, but right now what I'm more
|
||||||
@@ -89,6 +122,13 @@ static void page_fault_handler(struct cpu_status_t* ctx)
|
|||||||
panic(ctx, "page fault");
|
panic(ctx, "page fault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gp_fault_handler - Handler for #GP
|
||||||
|
* @ctx: CPU context
|
||||||
|
*
|
||||||
|
* Shows detail about a General Protection Fault,
|
||||||
|
* and what may have caused it. Halts the system.
|
||||||
|
*/
|
||||||
static void gp_fault_handler(struct cpu_status_t* ctx)
|
static void gp_fault_handler(struct cpu_status_t* ctx)
|
||||||
{
|
{
|
||||||
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
|
DEBUG("\x1b[38;5;231mGeneral Protection Fault at rip=0x%p, err=%u (%s)\x1b[0m",
|
||||||
@@ -114,6 +154,17 @@ static void gp_fault_handler(struct cpu_status_t* ctx)
|
|||||||
panic(ctx, "gp fault");
|
panic(ctx, "gp fault");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interrupt_dispatch - Interrupt dispatcher
|
||||||
|
* @context: CPU context
|
||||||
|
*
|
||||||
|
* This function is where all interrupt routines go, after they passed
|
||||||
|
* through their corresponding vector handler in the IDT assembly stub.
|
||||||
|
* It catches all exceptions.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context after interrupt
|
||||||
|
*/
|
||||||
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
struct cpu_status_t* interrupt_dispatch(struct cpu_status_t* context)
|
||||||
{
|
{
|
||||||
if (context == NULL) {
|
if (context == NULL) {
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ struct cpu_status_t {
|
|||||||
uint64_t r9;
|
uint64_t r9;
|
||||||
uint64_t r8;
|
uint64_t r8;
|
||||||
uint64_t rbp;
|
uint64_t rbp;
|
||||||
//uint64_t rsp;
|
|
||||||
uint64_t rdi;
|
uint64_t rdi;
|
||||||
uint64_t rsi;
|
uint64_t rsi;
|
||||||
uint64_t rdx;
|
uint64_t rdx;
|
||||||
|
|||||||
@@ -156,6 +156,14 @@ unsigned char kbdfr_shifted[128] =
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_handler - Keyboard event handler
|
||||||
|
*
|
||||||
|
* Is called from the interrupt dispatcher.
|
||||||
|
* When a key is pressed or released, we get a scancode, and
|
||||||
|
* it is then translated to an ASCII character.
|
||||||
|
* Left Shift, Ctrl, and Alt keys are also taken into consideration.
|
||||||
|
*/
|
||||||
void keyboard_handler()
|
void keyboard_handler()
|
||||||
{
|
{
|
||||||
unsigned char scancode = inb(0x60);
|
unsigned char scancode = inb(0x60);
|
||||||
@@ -212,6 +220,12 @@ void keyboard_handler()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* keyboard_init - Keyboard initialization
|
||||||
|
* @layout: Desired layout
|
||||||
|
*
|
||||||
|
* Prepares the PS/2 keyboard to recieve input.
|
||||||
|
*/
|
||||||
void keyboard_init(unsigned char layout)
|
void keyboard_init(unsigned char layout)
|
||||||
{
|
{
|
||||||
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
// Here we might go and select PS/2, USB, or other... (once we implement multiple keyboard protocols)
|
||||||
|
|||||||
@@ -9,11 +9,25 @@
|
|||||||
|
|
||||||
extern struct init_status init;
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* outb - Writes a byte to a CPU port
|
||||||
|
* @port: CPU port to write to
|
||||||
|
* @data: Byte to write
|
||||||
|
*
|
||||||
|
* Writes a single byte to the serial interface.
|
||||||
|
*/
|
||||||
void outb(int port, unsigned char data)
|
void outb(int port, unsigned char data)
|
||||||
{
|
{
|
||||||
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
__asm__ __volatile__("outb %%al, %%dx" :: "a" (data),"d" (port));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inb - Gets a byte in through a CPU port
|
||||||
|
* @port: The CPU port to get a byte from
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <data> - byte got from port
|
||||||
|
*/
|
||||||
unsigned char inb(int port)
|
unsigned char inb(int port)
|
||||||
{
|
{
|
||||||
unsigned char data = 0;
|
unsigned char data = 0;
|
||||||
@@ -21,6 +35,13 @@ unsigned char inb(int port)
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* serial_init - Initializes serial interface
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %-EIO - Input/output error
|
||||||
|
* %0 - Success
|
||||||
|
*/
|
||||||
int serial_init()
|
int serial_init()
|
||||||
{
|
{
|
||||||
outb(PORT + 1, 0x00); // Disable all interrupts
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
@@ -45,12 +66,23 @@ int serial_init()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_transmit_empty - Check if the serial transmit register is empty
|
||||||
|
*
|
||||||
|
* Return: Non-zero if the transmit register is empty and a new
|
||||||
|
* byte can be written to the serial port, 0 otherwise.
|
||||||
|
*/
|
||||||
static int is_transmit_empty()
|
static int is_transmit_empty()
|
||||||
{
|
{
|
||||||
return inb(PORT + 5) & 0x20;
|
return inb(PORT + 5) & 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putchar
|
/*
|
||||||
|
* skputc - Serial kernel putchar
|
||||||
|
* @c: character to write
|
||||||
|
*
|
||||||
|
* Writes a single character to the serial interface.
|
||||||
|
*/
|
||||||
void skputc(char c)
|
void skputc(char c)
|
||||||
{
|
{
|
||||||
// TODO: Spinlock here (serial access)
|
// TODO: Spinlock here (serial access)
|
||||||
@@ -58,7 +90,12 @@ void skputc(char c)
|
|||||||
outb(PORT, c);
|
outb(PORT, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serial kernel putstring
|
/*
|
||||||
|
* skputs - Serial kernel puts
|
||||||
|
* @str: Message to write
|
||||||
|
*
|
||||||
|
* Writes a non-formatted string to serial output.
|
||||||
|
*/
|
||||||
void skputs(const char* str)
|
void skputs(const char* str)
|
||||||
{
|
{
|
||||||
unsigned int i=0;
|
unsigned int i=0;
|
||||||
|
|||||||
@@ -23,14 +23,22 @@ because this shitty implementation will be replaced one day by Flanterm
|
|||||||
extern struct flanterm_context* ft_ctx;
|
extern struct flanterm_context* ft_ctx;
|
||||||
extern struct init_status init;
|
extern struct init_status init;
|
||||||
|
|
||||||
// Overhead that could be avoided, right? (for printf)
|
/*
|
||||||
|
* _putchar - Writes a character to terminal
|
||||||
|
* @character: character to write
|
||||||
|
*/
|
||||||
void _putchar(char character)
|
void _putchar(char character)
|
||||||
{
|
{
|
||||||
// TODO: Spinlock here (terminal access)
|
// TODO: Spinlock here (terminal access)
|
||||||
flanterm_write(ft_ctx, &character, 1);
|
flanterm_write(ft_ctx, &character, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug-printing
|
/*
|
||||||
|
* kputs - Kernel puts
|
||||||
|
* @str: String to write
|
||||||
|
*
|
||||||
|
* Writes a non-formatted string to terminal
|
||||||
|
*/
|
||||||
void kputs(const char* str)
|
void kputs(const char* str)
|
||||||
{
|
{
|
||||||
size_t i=0;
|
size_t i=0;
|
||||||
@@ -44,12 +52,26 @@ void kputs(const char* str)
|
|||||||
extern struct flanterm_context* ft_ctx;
|
extern struct flanterm_context* ft_ctx;
|
||||||
extern struct boot_context boot_ctx;
|
extern struct boot_context boot_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* flanterm_free_wrapper - free() wrapper for Flanterm
|
||||||
|
* @ptr: pointer to free
|
||||||
|
* @size: amount of bytes to free
|
||||||
|
*
|
||||||
|
* This function exists solely because the Flanterm initialization
|
||||||
|
* function only accepts a free() function with a size parameter,
|
||||||
|
* and the default one doesn't have it.
|
||||||
|
*/
|
||||||
void flanterm_free_wrapper(void* ptr, size_t size)
|
void flanterm_free_wrapper(void* ptr, size_t size)
|
||||||
{
|
{
|
||||||
(void)size;
|
(void)size;
|
||||||
kfree(ptr);
|
kfree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* term_init - Video output/terminal initialization
|
||||||
|
*
|
||||||
|
* Uses Flanterm and the framebuffer given by Limine.
|
||||||
|
*/
|
||||||
void term_init()
|
void term_init()
|
||||||
{
|
{
|
||||||
uint32_t bgColor = 0x252525;
|
uint32_t bgColor = 0x252525;
|
||||||
|
|||||||
25
src/kmain.c
25
src/kmain.c
@@ -30,13 +30,25 @@
|
|||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile LIMINE_BASE_REVISION(3);
|
volatile LIMINE_BASE_REVISION(3);
|
||||||
|
|
||||||
// Halt and catch fire (makes machine stall)
|
/*
|
||||||
|
* hcf - Halt and catch fire
|
||||||
|
*
|
||||||
|
* This function is called only in the case of an unrecoverable
|
||||||
|
* error. It halts interrupts, and stops execution. The machine
|
||||||
|
* will stay in an infinite loop state.
|
||||||
|
*/
|
||||||
void hcf()
|
void hcf()
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
CLEAR_INTERRUPTS; for (;;)asm("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doing nothing (can be interrupted)
|
/*
|
||||||
|
* idle - Make the machine idle
|
||||||
|
*
|
||||||
|
* When there is nothing else to do, this function
|
||||||
|
* gets called. It can be interrupted, so it allows
|
||||||
|
* the scheduler, timer, and keyboard to work.
|
||||||
|
*/
|
||||||
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
void idle() {SET_INTERRUPTS; for(;;)asm("hlt");}
|
||||||
|
|
||||||
struct flanterm_context *ft_ctx;
|
struct flanterm_context *ft_ctx;
|
||||||
@@ -72,7 +84,14 @@ void idle_main(void* arg)
|
|||||||
|
|
||||||
extern uintptr_t kheap_start;
|
extern uintptr_t kheap_start;
|
||||||
|
|
||||||
// This is our entry point
|
/*
|
||||||
|
* kmain - Kernel entry point
|
||||||
|
*
|
||||||
|
* This is where execution begins at handoff from Limine.
|
||||||
|
* The function fetches all needed information from the
|
||||||
|
* bootloader, initializes all kernel modules and structures,
|
||||||
|
* and then goes in an idle state.
|
||||||
|
*/
|
||||||
void kmain()
|
void kmain()
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
|
|||||||
@@ -14,11 +14,20 @@
|
|||||||
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
uint64_t gdt_entries[NUM_GDT_ENTRIES];
|
||||||
struct GDTR gdtr;
|
struct GDTR gdtr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_load - Loads Global Descriptor Table
|
||||||
|
*/
|
||||||
static void gdt_load()
|
static void gdt_load()
|
||||||
{
|
{
|
||||||
asm("lgdt %0" : : "m"(gdtr));
|
asm("lgdt %0" : : "m"(gdtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_flush - Flushes the Global Descriptor Table
|
||||||
|
*
|
||||||
|
* This function loads new Segment Selectors to make
|
||||||
|
* the GDT changes take effect
|
||||||
|
*/
|
||||||
static void gdt_flush()
|
static void gdt_flush()
|
||||||
{
|
{
|
||||||
// Here, 0x8 is the kernel code selector
|
// Here, 0x8 is the kernel code selector
|
||||||
@@ -42,6 +51,15 @@ static void gdt_flush()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gdt_init - Global Descriptor Table initialization
|
||||||
|
*
|
||||||
|
* This function loads a new GDT in the CPU.
|
||||||
|
* It contains a null descriptor, kernel code and data
|
||||||
|
* segments, and user code and data segments.
|
||||||
|
* However, we do not use segmentation to manage memory on
|
||||||
|
* 64-bit x86, as it's deprecated. Instead, we use paging.
|
||||||
|
*/
|
||||||
void gdt_init()
|
void gdt_init()
|
||||||
{
|
{
|
||||||
// Null descriptor (required)
|
// Null descriptor (required)
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ static uintptr_t end;
|
|||||||
// Kernel root table (level 4)
|
// Kernel root table (level 4)
|
||||||
extern uint64_t *kernel_pml4;
|
extern uint64_t *kernel_pml4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kheap_init - Kernel heap initialization
|
||||||
|
*
|
||||||
|
* This function physically allocates and maps enough pages
|
||||||
|
* of memory for KHEAP_SIZE, which is defined in config.h.
|
||||||
|
*
|
||||||
|
* It then creates one big heap block, which will be the
|
||||||
|
* base for a linked list.
|
||||||
|
*/
|
||||||
void kheap_init()
|
void kheap_init()
|
||||||
{
|
{
|
||||||
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
kheap_start = ALIGN_UP(kernel_virt_base + KERNEL_SIZE, PAGE_SIZE);
|
||||||
@@ -53,6 +62,18 @@ void kheap_init()
|
|||||||
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
|
DEBUG("Kernel heap initialized, head=0x%p, size=%u bytes", head, head->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kmalloc - Kernel memory allocation
|
||||||
|
* @size: number of bytes to allocate
|
||||||
|
*
|
||||||
|
* Looks for a big enough free block and marks it
|
||||||
|
* as taken. Each block of memory is preceded by
|
||||||
|
* the linked list header.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to at least <size> bytes of usable memory
|
||||||
|
* NULL - No more memory, or no valid size given
|
||||||
|
*/
|
||||||
void* kmalloc(size_t size)
|
void* kmalloc(size_t size)
|
||||||
{
|
{
|
||||||
// No size, no memory allocated!
|
// No size, no memory allocated!
|
||||||
@@ -92,6 +113,14 @@ void* kmalloc(size_t size)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kfree - Kernel memory freeing
|
||||||
|
* @ptr: pointer to memory region to free
|
||||||
|
*
|
||||||
|
* Marks the memory block beginning at <ptr>
|
||||||
|
* as free. Also merges adjacent free blocks
|
||||||
|
* to lessen fragmentation.
|
||||||
|
*/
|
||||||
void kfree(void* ptr)
|
void kfree(void* ptr)
|
||||||
{
|
{
|
||||||
// Nothing to free
|
// Nothing to free
|
||||||
@@ -113,8 +142,17 @@ void kfree(void* ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should alloc enough for a stack (at least 64kb) to be used for a process.
|
/*
|
||||||
// Should return a pointer to top of the stack (as stack grows DOWNWARDS)
|
* kalloc_stack - Stack memory allocation
|
||||||
|
*
|
||||||
|
* Allocates a memory region of at least PROCESS_STACK_SIZE,
|
||||||
|
* to be used as a stack for a process. The pointer returned
|
||||||
|
* points to the end of the region, as the stack grows downwards.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <ptr> - Pointer to a region after at least PROCESS_STACK_SIZE bytes of usable memory
|
||||||
|
* NULL - No more memory
|
||||||
|
*/
|
||||||
void* kalloc_stack()
|
void* kalloc_stack()
|
||||||
{
|
{
|
||||||
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
|
uint8_t* ptr = kmalloc(PROCESS_STACK_SIZE); // As it's out of kmalloc, stack is already mapped into kernel space
|
||||||
|
|||||||
@@ -16,6 +16,19 @@
|
|||||||
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
// We use the "restrict" keyword on pointers so that the compiler knows it can
|
||||||
// do more optimization on them (and as it's a much used function, it's good to
|
// do more optimization on them (and as it's a much used function, it's good to
|
||||||
// be able to do that)
|
// be able to do that)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcpy - Copy memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to copy
|
||||||
|
*
|
||||||
|
* This function copies n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
uint8_t* restrict pdest = (uint8_t* restrict)dest;
|
||||||
@@ -28,6 +41,18 @@ void* memcpy(void* restrict dest, const void* restrict src, size_t n)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memset - Sets a memory region to given byte
|
||||||
|
* @s: pointer to memory region
|
||||||
|
* @c: byte to be written
|
||||||
|
* @n: amount of bytes to write
|
||||||
|
*
|
||||||
|
* This function writes n times the byte c
|
||||||
|
* to the memory region pointed to by s.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <s> - Pointer to memory region
|
||||||
|
*/
|
||||||
void* memset(void* s, int c, size_t n)
|
void* memset(void* s, int c, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* p = (uint8_t*)s;
|
uint8_t* p = (uint8_t*)s;
|
||||||
@@ -39,6 +64,18 @@ void* memset(void* s, int c, size_t n)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memmove - Move memory from one place to another
|
||||||
|
* @dest: pointer to the destination region
|
||||||
|
* @src: pointer to the source region
|
||||||
|
* @n: amount of bytes to move
|
||||||
|
*
|
||||||
|
* This function moves n bytes of memory from
|
||||||
|
* src to dest.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <dest> - Pointer to destination region
|
||||||
|
*/
|
||||||
void* memmove(void *dest, const void* src, size_t n)
|
void* memmove(void *dest, const void* src, size_t n)
|
||||||
{
|
{
|
||||||
uint8_t* pdest = (uint8_t*)dest;
|
uint8_t* pdest = (uint8_t*)dest;
|
||||||
@@ -56,6 +93,20 @@ void* memmove(void *dest, const void* src, size_t n)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* memcmp - Compare two memory regions
|
||||||
|
* @s1: pointer to the first region
|
||||||
|
* @s2: pointer to the second region
|
||||||
|
* @n: amount of bytes to compare
|
||||||
|
*
|
||||||
|
* This function compares n bytes of memory
|
||||||
|
* bewteen regions pointed to by s1 and s2.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* %0 - if s1 and s2 are equal
|
||||||
|
* %-1 - if s1 is smaller than s2
|
||||||
|
* %1 - if s1 is greater than s2
|
||||||
|
*/
|
||||||
int memcmp(const void* s1, const void* s2, size_t n)
|
int memcmp(const void* s1, const void* s2, size_t n)
|
||||||
{
|
{
|
||||||
const uint8_t* p1 = (const uint8_t*)s1;
|
const uint8_t* p1 = (const uint8_t*)s1;
|
||||||
|
|||||||
@@ -24,17 +24,41 @@ If we use 1GB huge pages: PML4 -> PDPT -> 1gb pages
|
|||||||
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
4KB (regular size): PML4 -> PDPT -> PD -> PT -> 4kb pages
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* load_cr3 - Load a new value into the CR3 register
|
||||||
|
* @value: the value to load
|
||||||
|
*
|
||||||
|
* This function is used to load the physical address
|
||||||
|
* of the root page table (PML4), to switch the paging
|
||||||
|
* structures the CPU sees and uses.
|
||||||
|
*/
|
||||||
void load_cr3(uint64_t value) {
|
void load_cr3(uint64_t value) {
|
||||||
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
asm volatile ("mov %0, %%cr3" :: "r"(value) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// To flush TLB
|
/*
|
||||||
|
* invlpg - Invalidates a Translation Lookaside Buffer entry
|
||||||
|
* @addr: page memory address
|
||||||
|
*
|
||||||
|
* This function is used to flush at least the TLB entrie(s)
|
||||||
|
* for the page that contains the <addr> address.
|
||||||
|
*/
|
||||||
static inline void invlpg(void *addr)
|
static inline void invlpg(void *addr)
|
||||||
{
|
{
|
||||||
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
asm volatile("invlpg (%0)" :: "r"(addr) : "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocates a 512-entry 64bit page table/directory/whatever (zeroed)
|
/*
|
||||||
|
* alloc_page_table - Page table allocation
|
||||||
|
*
|
||||||
|
* This function allocates enough memory for a 512-entry
|
||||||
|
* 64-bit page table, for any level (PML4/3/2).
|
||||||
|
*
|
||||||
|
* Memory allocated here is zeroed.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <virt> - Pointer to allocated page table
|
||||||
|
*/
|
||||||
static uint64_t* alloc_page_table()
|
static uint64_t* alloc_page_table()
|
||||||
{
|
{
|
||||||
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
uint64_t* virt = (uint64_t*)PHYS_TO_VIRT(pmm_alloc());
|
||||||
@@ -49,10 +73,19 @@ static uint64_t* alloc_page_table()
|
|||||||
__attribute__((aligned(4096)))
|
__attribute__((aligned(4096)))
|
||||||
uint64_t *kernel_pml4;
|
uint64_t *kernel_pml4;
|
||||||
|
|
||||||
// Map a page, taking virt and phys address. This will go through the paging structures
|
/*
|
||||||
// beginning at the given root table, translate the virtual address in indexes in
|
* paging_map_page - Mapping a memory page
|
||||||
// page table/directories, and then mapping the correct page table entry with the
|
* @root_table: Address of the PML4
|
||||||
// given physical address + flags
|
* @virt: Virtual address
|
||||||
|
* @phys: Physical address
|
||||||
|
* @flags: Flags to set on page
|
||||||
|
*
|
||||||
|
* This function maps the physical address <phys> to the virtual
|
||||||
|
* address <virt>, using the paging structures beginning at
|
||||||
|
* <root_table>. <flags> can be set according to the PTE_FLAGS enum.
|
||||||
|
*
|
||||||
|
* If a page table/directory entry is not present yet, it creates it.
|
||||||
|
*/
|
||||||
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags)
|
||||||
{
|
{
|
||||||
virt = PAGE_ALIGN_DOWN(virt);
|
virt = PAGE_ALIGN_DOWN(virt);
|
||||||
@@ -102,6 +135,15 @@ void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_
|
|||||||
uint64_t kernel_phys_base;
|
uint64_t kernel_phys_base;
|
||||||
uint64_t kernel_virt_base;
|
uint64_t kernel_virt_base;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* paging_init - Paging initialization
|
||||||
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function initializes new paging structures, to replace
|
||||||
|
* the ones given by the bootloader.
|
||||||
|
*
|
||||||
|
* It maps the kernel, the HHDM space, and the framebuffer.
|
||||||
|
*/
|
||||||
void paging_init(struct boot_context boot_ctx)
|
void paging_init(struct boot_context boot_ctx)
|
||||||
{
|
{
|
||||||
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
// We should map the kernel, GDT, IDT, stack, framebuffer.
|
||||||
|
|||||||
@@ -41,12 +41,15 @@ extern uint64_t hhdm_off;
|
|||||||
|
|
||||||
// Page entry special bits
|
// Page entry special bits
|
||||||
// Bits set on a parent (directory, table) fall back to their children
|
// Bits set on a parent (directory, table) fall back to their children
|
||||||
#define PTE_PRESENT (1ULL << 0)
|
enum PTE_FLAGS
|
||||||
#define PTE_WRITABLE (1ULL << 1)
|
{
|
||||||
#define PTE_USER (1ULL << 2)
|
PTE_PRESENT = (1ULL << 0),
|
||||||
#define PTE_PWT (1ULL << 3)
|
PTE_WRITABLE = (1ULL << 1),
|
||||||
#define PTE_PCD (1ULL << 4)
|
PTE_USER = (1ULL << 2),
|
||||||
#define PTE_HUGE (1ULL << 7)
|
PTE_PWT = (1ULL << 3),
|
||||||
#define PTE_NOEXEC (1ULL << 63)
|
PTE_PCD = (1ULL << 4),
|
||||||
|
PTE_HUGE = (1ULL << 7),
|
||||||
|
PTE_NOEXEC = (1ULL << 63)
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -24,13 +24,16 @@ First we'll have to discover the physical memory layout,
|
|||||||
and for that we can use a Limine request.
|
and for that we can use a Limine request.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
We will look for the biggest usable physical memory region
|
|
||||||
and use this for the bitmap. The reserved memory will be ignored.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct limine_memmap_entry* biggest_entry;
|
struct limine_memmap_entry* biggest_entry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_find_biggest_usable_region - Finding the biggest free memory region
|
||||||
|
* @memmap: Limine memory map
|
||||||
|
* @hhdm: Limine HHDM offset
|
||||||
|
*
|
||||||
|
* This function uses the memory map provided by the bootloader
|
||||||
|
* to find the single biggest free memory region we can use.
|
||||||
|
*/
|
||||||
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
static void pmm_find_biggest_usable_region(struct limine_memmap_response* memmap, struct limine_hhdm_response* hhdm)
|
||||||
{
|
{
|
||||||
// Max length of a usable memory region
|
// Max length of a usable memory region
|
||||||
@@ -62,6 +65,14 @@ uint64_t hhdm_off;
|
|||||||
|
|
||||||
static uintptr_t g_freelist = 0;
|
static uintptr_t g_freelist = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_alloc - Allocate a physical page
|
||||||
|
*
|
||||||
|
* This function allocates a single physical page (frame)
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <addr> - Address for the allocated page
|
||||||
|
*/
|
||||||
uintptr_t pmm_alloc()
|
uintptr_t pmm_alloc()
|
||||||
{
|
{
|
||||||
if (!g_freelist) {
|
if (!g_freelist) {
|
||||||
@@ -72,12 +83,22 @@ uintptr_t pmm_alloc()
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_free - Frees a memory page
|
||||||
|
* @addr: Address to the page
|
||||||
|
*/
|
||||||
void pmm_free(uintptr_t addr)
|
void pmm_free(uintptr_t addr)
|
||||||
{
|
{
|
||||||
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
*(uintptr_t*) PHYS_TO_VIRT(addr) = g_freelist;
|
||||||
g_freelist = addr;
|
g_freelist = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_init_freelist - PMM freelist initialization
|
||||||
|
*
|
||||||
|
* This function marks the biggest memory region as
|
||||||
|
* free, so we can use it in pmm_alloc.
|
||||||
|
*/
|
||||||
static void pmm_init_freelist()
|
static void pmm_init_freelist()
|
||||||
{
|
{
|
||||||
// We simply call pmm_free() on each page that is marked USABLE
|
// We simply call pmm_free() on each page that is marked USABLE
|
||||||
@@ -93,6 +114,13 @@ static void pmm_init_freelist()
|
|||||||
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
DEBUG("%u frames in freelist, available for use (%u bytes)", page_count, page_count*PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pmm_init - Physical memory manager initialization
|
||||||
|
* @boot_ctx: Boot context structure
|
||||||
|
*
|
||||||
|
* This function prepares the PMM for use.
|
||||||
|
* The PMM works with a freelist.
|
||||||
|
*/
|
||||||
void pmm_init(struct boot_context boot_ctx)
|
void pmm_init(struct boot_context boot_ctx)
|
||||||
{
|
{
|
||||||
hhdm_off = boot_ctx.hhdm->offset;
|
hhdm_off = boot_ctx.hhdm->offset;
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ void* vmm_pt_root = 0;
|
|||||||
// Linked list head for virtual memory objects
|
// Linked list head for virtual memory objects
|
||||||
struct vm_object* vm_objs = NULL;
|
struct vm_object* vm_objs = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Will have to be rewritten and expanded,
|
||||||
|
* to prepare for userspace.
|
||||||
|
* The platform-agnostic flags will be removed
|
||||||
|
* because as long as the kernel is x86 only,
|
||||||
|
* we don't need over complication.
|
||||||
|
* Plus I don't plan to port to other architectures
|
||||||
|
*/
|
||||||
|
|
||||||
uint64_t convert_x86_vm_flags(size_t flags)
|
uint64_t convert_x86_vm_flags(size_t flags)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,13 +23,22 @@ extern uint64_t *kernel_pml4;
|
|||||||
|
|
||||||
size_t next_free_pid = 0;
|
size_t next_free_pid = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_init - Initializes process list
|
||||||
|
*/
|
||||||
void process_init()
|
void process_init()
|
||||||
{
|
{
|
||||||
processes_list = NULL;
|
processes_list = NULL;
|
||||||
current_process = NULL;
|
current_process = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only for debug
|
/*
|
||||||
|
* process_display_list - Debug function to display processes
|
||||||
|
* @processes_list: head of the process linked list
|
||||||
|
*
|
||||||
|
* This function prints the linked list of processes
|
||||||
|
* to the DEBUG output.
|
||||||
|
*/
|
||||||
void process_display_list(struct process_t* processes_list)
|
void process_display_list(struct process_t* processes_list)
|
||||||
{
|
{
|
||||||
int process_view_id = 0;
|
int process_view_id = 0;
|
||||||
@@ -42,6 +51,19 @@ void process_display_list(struct process_t* processes_list)
|
|||||||
DEBUG("NULL");
|
DEBUG("NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_create - Create a process
|
||||||
|
* @name: name of the process
|
||||||
|
* @function: beginning of process executable code
|
||||||
|
* @arg: (optional) argument provided to process
|
||||||
|
*
|
||||||
|
* This function creates a process, gives it all
|
||||||
|
* necessary context and a stack, and adds the
|
||||||
|
* process to the linked list.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <proc> - pointer to created process
|
||||||
|
*/
|
||||||
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
||||||
{
|
{
|
||||||
CLEAR_INTERRUPTS;
|
CLEAR_INTERRUPTS;
|
||||||
@@ -81,6 +103,11 @@ struct process_t* process_create(char* name, void(*function)(void*), void* arg)
|
|||||||
return proc;
|
return proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_add - Add a process to the end of the linked list
|
||||||
|
* @processes_list: pointer to the head of the linked list
|
||||||
|
* @process: process to add at the end of the linked list
|
||||||
|
*/
|
||||||
void process_add(struct process_t** processes_list, struct process_t* process)
|
void process_add(struct process_t** processes_list, struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!process) return;
|
if (!process) return;
|
||||||
@@ -100,6 +127,11 @@ void process_add(struct process_t** processes_list, struct process_t* process)
|
|||||||
tmp->next = process;
|
tmp->next = process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_delete - Delete a process from the linked list
|
||||||
|
* @processes_list: pointer to head of linked list
|
||||||
|
* @process: the process to delete from the list
|
||||||
|
*/
|
||||||
void process_delete(struct process_t** processes_list, struct process_t* process)
|
void process_delete(struct process_t** processes_list, struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!processes_list || !*processes_list || !process) return;
|
if (!processes_list || !*processes_list || !process) return;
|
||||||
@@ -128,14 +160,30 @@ void process_delete(struct process_t** processes_list, struct process_t* process
|
|||||||
kfree(process);
|
kfree(process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* process_get_next - Get the next process (unused)
|
||||||
|
* @process: pointer to process
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <process->next> - process right after the one specified
|
||||||
|
*/
|
||||||
struct process_t* process_get_next(struct process_t* process)
|
struct process_t* process_get_next(struct process_t* process)
|
||||||
{
|
{
|
||||||
if (!process) return NULL;
|
if (!process) return NULL;
|
||||||
return process->next;
|
return process->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will be used to clean up resources (if any, when we implement it)
|
/*
|
||||||
// Just mark as DEAD then idle. Scheduler will delete process at next timer interrupt % quantum.
|
* process_exit - Exit from a process
|
||||||
|
*
|
||||||
|
* This function is pushed to all process stacks, as a last
|
||||||
|
* return address. Once the process is done executing, it
|
||||||
|
* ends up here.
|
||||||
|
*
|
||||||
|
* Process is marked as DEAD, and then execution loops.
|
||||||
|
* Next time the scheduler sees the process, it will
|
||||||
|
* automatically delete it from the linked list.
|
||||||
|
*/
|
||||||
void process_exit()
|
void process_exit()
|
||||||
{
|
{
|
||||||
DEBUG("Exiting from process '%s'", current_process->name);
|
DEBUG("Exiting from process '%s'", current_process->name);
|
||||||
|
|||||||
@@ -14,12 +14,24 @@ extern struct process_t* processes_list;
|
|||||||
extern struct process_t* current_process;
|
extern struct process_t* current_process;
|
||||||
extern struct process_t* idle_proc;
|
extern struct process_t* idle_proc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_init - Choose the first process
|
||||||
|
*/
|
||||||
void scheduler_init()
|
void scheduler_init()
|
||||||
{
|
{
|
||||||
// Choose first process?
|
|
||||||
current_process = processes_list;
|
current_process = processes_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* scheduler_schedule - Main scheduling routine
|
||||||
|
* @context: CPU context of previous process
|
||||||
|
*
|
||||||
|
* Chooses the next process that we should run.
|
||||||
|
* The routine is executed every SCHEDULER_QUANTUM ticks.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* <context> - CPU context for next process
|
||||||
|
*/
|
||||||
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
|
struct cpu_status_t* scheduler_schedule(struct cpu_status_t* context)
|
||||||
{
|
{
|
||||||
if (context == NULL) {
|
if (context == NULL) {
|
||||||
|
|||||||
@@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strcpy - copy a NULL-terminated string
|
||||||
|
* @dest: destination buffer where the string is copied
|
||||||
|
* @src: source string to copy from
|
||||||
|
*
|
||||||
|
* Copies the string pointed to by @src (including the terminating
|
||||||
|
* NULL byte) into the buffer pointed to by @dest.
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
char* strcpy(char *dest, const char *src)
|
char* strcpy(char *dest, const char *src)
|
||||||
{
|
{
|
||||||
char *temp = dest;
|
char *temp = dest;
|
||||||
@@ -13,7 +23,21 @@ char* strcpy(char *dest, const char *src)
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/2488563/strcat-implementation
|
/*
|
||||||
|
* strcat - append a NUL-terminated string
|
||||||
|
* @dest: destination buffer containing the initial string
|
||||||
|
* @src: source string to append
|
||||||
|
*
|
||||||
|
* Appends the string pointed to by @src to the end of the string
|
||||||
|
* pointed to by @dest. The terminating NUL byte in @dest is
|
||||||
|
* overwritten and a new terminating NUL byte is added.
|
||||||
|
*
|
||||||
|
* The destination buffer must be large enough to hold the result.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/2488563/strcat-implementation
|
||||||
|
*
|
||||||
|
* Return: pointer to the destination string (@dest)
|
||||||
|
*/
|
||||||
char *strcat(char *dest, const char *src)
|
char *strcat(char *dest, const char *src)
|
||||||
{
|
{
|
||||||
size_t i,j;
|
size_t i,j;
|
||||||
@@ -26,7 +50,21 @@ char *strcat(char *dest, const char *src)
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/14159625/implementation-of-strncpy
|
/*
|
||||||
|
* strncpy - copy a string with length limit
|
||||||
|
* @dst: destination buffer
|
||||||
|
* @src: source string
|
||||||
|
* @n: maximum number of bytes to copy
|
||||||
|
*
|
||||||
|
* Copies up to @n bytes from @src to @dst. Copying stops early if a
|
||||||
|
* NULL byte is encountered in @src. If @src is shorter than @n, the
|
||||||
|
* remaining bytes in @dst are left unchanged in this implementation.
|
||||||
|
*
|
||||||
|
* Note: This differs slightly from the standard strncpy behavior,
|
||||||
|
* which pads the remaining bytes with NULL.
|
||||||
|
*
|
||||||
|
* Taken from: https://stackoverflow.com/questions/14159625/implementation-of-strncpy
|
||||||
|
*/
|
||||||
void strncpy(char* dst, const char* src, size_t n)
|
void strncpy(char* dst, const char* src, size_t n)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "io/serial/serial.h"
|
#include "io/serial/serial.h"
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
For now, the timer module will be using the PIC.
|
For now, the timer module will be using the PIC.
|
||||||
@@ -20,6 +21,13 @@ volatile uint64_t ticks = 0;
|
|||||||
|
|
||||||
extern struct init_status init;
|
extern struct init_status init;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_remap - Remap the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* By default, interrupts are mapped at the wrong place.
|
||||||
|
* This function remaps interrupt numbers so interrupts
|
||||||
|
* don't conflict with each other.
|
||||||
|
*/
|
||||||
void pic_remap()
|
void pic_remap()
|
||||||
{
|
{
|
||||||
uint8_t master_mask = inb(0x21);
|
uint8_t master_mask = inb(0x21);
|
||||||
@@ -47,6 +55,12 @@ void pic_remap()
|
|||||||
outb(0xA1, slave_mask);
|
outb(0xA1, slave_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pic_enable - Enable the Programmable Interrupt Controller
|
||||||
|
*
|
||||||
|
* This function enables IRQ0 and IRQ1, which correspond to
|
||||||
|
* the timer and keyboard interrupts, respectively.
|
||||||
|
*/
|
||||||
void pic_enable()
|
void pic_enable()
|
||||||
{
|
{
|
||||||
// Enabling IRQ0 (unmasking it) but not the others
|
// Enabling IRQ0 (unmasking it) but not the others
|
||||||
@@ -57,12 +71,15 @@ void pic_enable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Base frequency = 1.193182 MHz
|
* pit_init - Initialization of the Programmable Interval Timer
|
||||||
1 tick per ms (divide by 1000) = roughly 1193 Hz
|
*
|
||||||
*/
|
* The PIT is the simplest timer we can get working on x86.
|
||||||
|
* It has a base frequency of 1.193182 MHz.
|
||||||
|
* A custom frequency can be set using TIMER_FREQUENCY macro.
|
||||||
|
*/
|
||||||
void pit_init()
|
void pit_init()
|
||||||
{
|
{
|
||||||
uint32_t frequency = 1000; // 1 kHz
|
uint32_t frequency = TIMER_FREQUENCY;
|
||||||
uint32_t divisor = 1193182 / frequency;
|
uint32_t divisor = 1193182 / frequency;
|
||||||
|
|
||||||
// Set PIT to mode 3, channel 0
|
// Set PIT to mode 3, channel 0
|
||||||
@@ -73,8 +90,12 @@ void pit_init()
|
|||||||
outb(0x40, (divisor >> 8) & 0xFF);
|
outb(0x40, (divisor >> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait n ticks
|
/*
|
||||||
// Given that there's a tick every 1ms, wait n milliseconds
|
* timer_wait - Wait for X ticks
|
||||||
|
*
|
||||||
|
* By default, the timer frequency is 1000Hz, meaning
|
||||||
|
* ticks are equal to milliseconds.
|
||||||
|
*/
|
||||||
void timer_wait(uint64_t wait_ticks)
|
void timer_wait(uint64_t wait_ticks)
|
||||||
{
|
{
|
||||||
uint64_t then = ticks + wait_ticks;
|
uint64_t then = ticks + wait_ticks;
|
||||||
@@ -83,6 +104,11 @@ void timer_wait(uint64_t wait_ticks)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* timer_init - Initialization of the timer
|
||||||
|
*
|
||||||
|
* This function wakes the PIT.
|
||||||
|
*/
|
||||||
void timer_init()
|
void timer_init()
|
||||||
{
|
{
|
||||||
// Remapping the PIC, because at startup it conflicts with
|
// Remapping the PIC, because at startup it conflicts with
|
||||||
|
|||||||
Reference in New Issue
Block a user