Attempt 2 #2
@@ -20,7 +20,7 @@ file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/release)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/isodir/boot/grub)
|
||||
|
||||
# Create grub.cfg for ISO
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" { multiboot /boot/kernel.bin }")
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" { multiboot2 /boot/kernel.bin }")
|
||||
|
||||
|
||||
# ISO Generation
|
||||
|
||||
@@ -43,8 +43,8 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Update the build system to create both ISO and Floppy images. Verify these work using a test script. The standard CMake build target should automatically generate both images. (Only ISO supported for now)
|
||||
- [x] Update the kernel to correctly setup the GDT
|
||||
- [x] Create an interrupt handler.
|
||||
- [ ] Implement a PIC handler
|
||||
- [ ] 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] Implement a PIC handler
|
||||
- [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.
|
||||
- [ ] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
|
||||
- [ ] 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.
|
||||
|
||||
62
docs/interrupts.md
Normal file
62
docs/interrupts.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Interrupt Subsystem
|
||||
|
||||
## Overview
|
||||
|
||||
The interrupt subsystem handles both CPU exceptions (faults, traps, aborts) and hardware interrupts (IRQs) from external devices. It consists of three cooperating components:
|
||||
|
||||
1. **IDT (Interrupt Descriptor Table)** — Maps interrupt vectors 0–255 to handler entry points.
|
||||
2. **ISR (Interrupt Service Routines)** — Assembly stubs and a C dispatcher that routes interrupts.
|
||||
3. **PIC (Programmable Interrupt Controller)** — Manages the 8259A PIC chips that multiplex hardware IRQs onto CPU interrupt lines.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Interrupt Vector Layout
|
||||
|
||||
| Vector Range | Purpose |
|
||||
|---|---|
|
||||
| 0–31 | CPU exceptions (Division by Zero, Page Fault, GPF, etc.) |
|
||||
| 32–47 | Hardware IRQs (remapped from default 0–15) |
|
||||
| 48–255 | Available for software interrupts / future use |
|
||||
|
||||
### Flow of an Interrupt
|
||||
|
||||
1. CPU or device raises an interrupt.
|
||||
2. CPU looks up the vector in the IDT and jumps to the assembly stub.
|
||||
3. The stub pushes a dummy error code (if the CPU didn't push one), the interrupt number, and all general-purpose registers onto the stack.
|
||||
4. The stub loads the kernel data segment (0x10) and calls `isr_handler()` in C.
|
||||
5. For hardware interrupts (vectors 32–47), `isr_handler` sends an End-of-Interrupt (EOI) to the PIC.
|
||||
6. For CPU exceptions (vectors 0–31), the handler prints the exception name and halts.
|
||||
7. On return, the stub restores all registers and executes `iret`.
|
||||
|
||||
### PIC Remapping
|
||||
|
||||
The 8259A PIC ships with IRQ 0–7 mapped to vectors 8–15, which collide with CPU exceptions. During initialization, we remap:
|
||||
|
||||
- **Master PIC** (IRQ 0–7) → vectors 32–39
|
||||
- **Slave PIC** (IRQ 8–15) → vectors 40–47
|
||||
|
||||
The PIC is initialized in cascade mode with ICW4 (8086 mode). Original IRQ masks are saved and restored after remapping.
|
||||
|
||||
## Key Files
|
||||
|
||||
- `src/idt.c` / `src/idt.h` — IDT setup and gate registration.
|
||||
- `src/interrupts.S` — Assembly stubs for ISRs 0–31 and IRQs 0–15.
|
||||
- `src/isr.c` / `src/isr.h` — C interrupt dispatcher and `registers_t` structure.
|
||||
- `src/pic.c` / `src/pic.h` — PIC initialization, EOI, mask/unmask.
|
||||
- `src/port_io.h` — Inline `inb`, `outb`, `io_wait` helpers.
|
||||
|
||||
## Register Save Frame
|
||||
|
||||
When an interrupt fires, the following is pushed onto the stack (from high to low address):
|
||||
|
||||
```
|
||||
ss, useresp (only on privilege change)
|
||||
eflags
|
||||
cs, eip (pushed by CPU)
|
||||
err_code (pushed by CPU or stub as 0)
|
||||
int_no (pushed by stub)
|
||||
eax..edi (pushed by pusha)
|
||||
ds (pushed by stub)
|
||||
```
|
||||
|
||||
This matches the `registers_t` struct in `isr.h`.
|
||||
63
docs/pmm.md
Normal file
63
docs/pmm.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Physical Memory Manager (PMM)
|
||||
|
||||
## Overview
|
||||
|
||||
The PMM manages physical page frames using a bitmap allocator. It tracks which 4 KiB pages of physical RAM are free or in use, and supports allocating pages from different memory zones.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Bitmap Allocator
|
||||
|
||||
Each bit in a global bitmap corresponds to one 4 KiB physical page frame:
|
||||
- **Bit = 1** → page is in use (allocated or reserved)
|
||||
- **Bit = 0** → page is free
|
||||
|
||||
The bitmap covers the entire 32-bit physical address space (up to 4 GiB), requiring 128 KiB of storage in BSS.
|
||||
|
||||
### Memory Zones
|
||||
|
||||
The allocator supports zone-based allocation to satisfy constraints from different subsystems:
|
||||
|
||||
| Zone | Range | Purpose |
|
||||
|---|---|---|
|
||||
| `PMM_ZONE_DMA` | 0 – 16 MiB | ISA DMA-compatible memory |
|
||||
| `PMM_ZONE_NORMAL` | 16 MiB – 4 GiB | General-purpose allocation |
|
||||
|
||||
When `PMM_ZONE_NORMAL` is requested but no pages are available above 16 MiB (e.g., on systems with less than 16 MiB of RAM), the allocator falls back to `PMM_ZONE_DMA`.
|
||||
|
||||
Page 0 (address 0x00000000) is always marked as used to prevent returning NULL as a valid physical address.
|
||||
|
||||
### Initialization
|
||||
|
||||
1. The entire bitmap is initialized to "all used" (0xFFFFFFFF).
|
||||
2. The Multiboot2 memory map is parsed to discover available RAM regions.
|
||||
3. Available regions are marked as free in the bitmap.
|
||||
4. The kernel's own memory (between `_kernel_start` and `_kernel_end` linker symbols) is re-marked as used.
|
||||
5. The Multiboot2 info structure itself is marked as used.
|
||||
|
||||
### Linker Symbols
|
||||
|
||||
The linker script exports `_kernel_start` and `_kernel_end` symbols so the PMM knows which physical pages the kernel occupies and must not allocate.
|
||||
|
||||
## API
|
||||
|
||||
```c
|
||||
void init_pmm(uint32_t multiboot_addr);
|
||||
phys_addr_t pmm_alloc_page(pmm_zone_t zone);
|
||||
void pmm_free_page(phys_addr_t addr);
|
||||
```
|
||||
|
||||
- `init_pmm` — Parse Multiboot2 info and build the free-page bitmap.
|
||||
- `pmm_alloc_page` — Allocate a single 4 KiB page from the specified zone. Returns 0 on OOM.
|
||||
- `pmm_free_page` — Return a page to the free pool.
|
||||
|
||||
## Key Files
|
||||
|
||||
- `src/pmm.c` / `src/pmm.h` — Allocator implementation and zone definitions.
|
||||
- `src/linker.ld` — Exports `_kernel_start` / `_kernel_end`.
|
||||
|
||||
## Limitations
|
||||
|
||||
- First-fit allocation (linear scan) — O(n) per allocation.
|
||||
- No per-zone free counts or statistics yet.
|
||||
- Does not handle memory hot-plug or ACPI reclaim regions.
|
||||
@@ -15,8 +15,6 @@ void offset_print(const char *str)
|
||||
}
|
||||
}
|
||||
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||
|
||||
void print_hex(uint32_t val)
|
||||
{
|
||||
const char *hex = "0123456789ABCDEF";
|
||||
@@ -29,9 +27,7 @@ void print_hex(uint32_t val)
|
||||
}
|
||||
|
||||
void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
(void)addr; // Unused for now
|
||||
|
||||
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC && magic != MULTIBOOT_BOOTLOADER_MAGIC) {
|
||||
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC) {
|
||||
offset_print("Invalid magic number: ");
|
||||
print_hex(magic);
|
||||
return;
|
||||
|
||||
21
src/pmm.c
21
src/pmm.c
@@ -1,12 +1,18 @@
|
||||
#include "pmm.h"
|
||||
#include "port_io.h"
|
||||
#include <multiboot2.h>
|
||||
|
||||
extern uint32_t _kernel_start;
|
||||
extern uint32_t _kernel_end;
|
||||
|
||||
/* Printing helpers (defined in kernel.c) */
|
||||
extern void offset_print(const char *str);
|
||||
extern void print_hex(uint32_t val);
|
||||
|
||||
#define MAX_PHYSICAL_MEMORY 0xFFFFFFFF // 4GB
|
||||
#define BITMAP_SIZE (MAX_PHYSICAL_MEMORY / PAGE_SIZE / 32)
|
||||
#define TOTAL_FRAMES (MAX_PHYSICAL_MEMORY / PAGE_SIZE + 1)
|
||||
#define FRAMES_PER_INDEX 32
|
||||
#define BITMAP_SIZE ((TOTAL_FRAMES + FRAMES_PER_INDEX - 1) / FRAMES_PER_INDEX)
|
||||
|
||||
static uint32_t memory_bitmap[BITMAP_SIZE];
|
||||
static uint32_t memory_size = 0;
|
||||
@@ -42,17 +48,18 @@ void init_pmm(uint32_t multiboot_addr) {
|
||||
uint32_t addr = multiboot_addr;
|
||||
|
||||
/* Initialize all memory as used (1) initially */
|
||||
for (int i = 0; i < BITMAP_SIZE; i++) {
|
||||
for (uint32_t i = 0; i < BITMAP_SIZE; i++) {
|
||||
memory_bitmap[i] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (addr & 7) {
|
||||
// offset_print("Multiboot alignment error\n"); // Need offset_print here? No, kernel handles prints
|
||||
offset_print(" PMM: multiboot alignment error\n");
|
||||
return; // Alignment issue
|
||||
}
|
||||
|
||||
uint32_t size = *(uint32_t *)addr;
|
||||
|
||||
uint32_t regions_found = 0;
|
||||
for (tag = (struct multiboot_tag *)(addr + 8);
|
||||
tag->type != MULTIBOOT_TAG_TYPE_END;
|
||||
tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7) & ~7))) {
|
||||
@@ -60,6 +67,8 @@ void init_pmm(uint32_t multiboot_addr) {
|
||||
if (tag->type == MULTIBOOT_TAG_TYPE_BASIC_MEMINFO) {
|
||||
struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag;
|
||||
memory_size = meminfo->mem_upper;
|
||||
offset_print(" PMM: mem_upper = ");
|
||||
print_hex(memory_size);
|
||||
} else if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
|
||||
multiboot_memory_map_t *mmap;
|
||||
struct multiboot_tag_mmap *tag_mmap = (struct multiboot_tag_mmap *)tag;
|
||||
@@ -73,6 +82,8 @@ void init_pmm(uint32_t multiboot_addr) {
|
||||
uint64_t len = mmap->len;
|
||||
uint64_t end = start + len;
|
||||
|
||||
regions_found++;
|
||||
|
||||
if (start % PAGE_SIZE != 0) {
|
||||
start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
|
||||
}
|
||||
@@ -109,6 +120,10 @@ void init_pmm(uint32_t multiboot_addr) {
|
||||
|
||||
// Mark first page as used to avoid returning 0 (NULL)
|
||||
set_frame(0);
|
||||
|
||||
offset_print(" PMM: found ");
|
||||
print_hex(regions_found);
|
||||
offset_print(" PMM: available memory regions\n");
|
||||
}
|
||||
|
||||
phys_addr_t pmm_alloc_page(pmm_zone_t zone) {
|
||||
|
||||
Reference in New Issue
Block a user