-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/#10 init simple memory map #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements fundamental kernel infrastructure including a Physical Memory Manager (PMM), Interrupt Descriptor Table (IDT), memory map dumping functionality, and console auto-scrolling. The implementation adds bitmap-based page allocation with proper initialization that excludes the 0-1MB region, sets up exception and IRQ handlers, and enables the console to automatically scroll when the screen fills up.
Key Changes
- PMM Implementation: Bitmap-based physical memory allocator with page allocation/deallocation functions, excluding 0-1MB from management
- IDT Setup: Exception handlers (ISR 0-31) and IRQ handlers (32-47) with assembly stubs and C handler functions
- Console Scrolling: Automatic screen scrolling for both VGA text mode and framebuffer mode when cursor exceeds screen height
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| src/mem/pmm.c | Implements bitmap-based physical memory manager with initialization, allocation, and deallocation functions |
| src/mem/mmap.c | Adds memory map dump functionality to display Multiboot2 memory map information |
| src/kernel.c | Integrates IDT and PMM initialization, adds test code for page allocation/deallocation |
| src/drivers/video/video.c | Implements screen scrolling for both VGA text and framebuffer modes |
| src/drivers/console/console.c | Adds automatic scrolling when cursor exceeds screen height |
| src/arch/x86/isr.asm | Assembly stubs for CPU exception handlers (ISR 0-31) |
| src/arch/x86/irq.asm | Assembly stubs for hardware interrupt handlers (IRQ 0-15) |
| src/arch/x86/idt_flush.asm | Assembly function to load IDT register |
| src/arch/x86/idt.c | IDT initialization and interrupt handler registration |
| src/arch/x86/gdt.c | Removes GDT verification output, fixes comment typo |
| linker.ld | Adds kernel_start and kernel_end symbols for PMM to determine kernel memory region |
| include/mem/pmm.h | Header defining PMM interface |
| include/mem/mmap.h | Header defining memory map dump function |
| include/drivers/video/video.h | Adds video_scroll_up function declaration |
| include/arch/x86/idt.h | Header defining IDT initialization interface |
| Makefile | Adds build rules for new IDT, ISR, IRQ, MMAP, and PMM modules |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| uint8_t* p = start; | ||
| uint32_t entry_count = 0; | ||
|
|
||
| while (p < end && entry_count < 64) { |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entry count limit of 64 is arbitrary and undocumented. If there are more than 64 memory map entries, they will be silently ignored, potentially causing incorrect memory management. Consider either documenting why 64 is sufficient, or removing this limit entirely and relying on the bounds check against 'end' pointer.
| struct multiboot_tag { | ||
| uint32_t type; | ||
| uint32_t size; | ||
| }; | ||
|
|
||
| struct multiboot_tag_mmap { | ||
| uint32_t type; | ||
| uint32_t size; | ||
| uint32_t entry_size; | ||
| uint32_t entry_version; | ||
| }; | ||
|
|
||
| struct multiboot_mmap_entry { | ||
| uint64_t addr; | ||
| uint64_t len; | ||
| uint32_t type; | ||
| uint32_t zero; | ||
| }; | ||
|
|
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The multiboot2 structures (multiboot_tag, multiboot_tag_mmap, multiboot_mmap_entry) are duplicated in both pmm.c and mmap.c. This code duplication should be eliminated by defining these structures once in a shared header file (e.g., include/mem/mmap.h or a new multiboot2.h header) and including it in both files.
| struct multiboot_tag { | |
| uint32_t type; | |
| uint32_t size; | |
| }; | |
| struct multiboot_tag_mmap { | |
| uint32_t type; | |
| uint32_t size; | |
| uint32_t entry_size; | |
| uint32_t entry_version; | |
| }; | |
| struct multiboot_mmap_entry { | |
| uint64_t addr; | |
| uint64_t len; | |
| uint32_t type; | |
| uint32_t zero; | |
| }; | |
| /* multiboot_tag, multiboot_tag_mmap, and multiboot_mmap_entry | |
| * are declared in mem/mmap.h to avoid duplication. */ |
|
|
||
| mmap_dump(magic, mbinfo); |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses tabs for indentation while other lines use spaces. This creates inconsistent indentation in the file. The entire file should use consistent indentation (spaces or tabs).
| mmap_dump(magic, mbinfo); | |
| mmap_dump(magic, mbinfo); |
| __asm__ __volatile__ ( | ||
| "cld\n\t" | ||
| "rep stosb\n\t" | ||
| : "+c" (size), "+D" (bmp) | ||
| : "a" (0xFF) | ||
| : "memory" | ||
| ); |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assembly uses "rep stosb" to fill the bitmap, which requires ECX to contain the count. However, 'size' is modified by the instruction (decremented to 0), and the "+c" constraint allows this. The 'bmp' pointer is also incremented to point past the end. While this works correctly, both variables are modified in potentially unexpected ways. Consider adding a comment explaining that these variables are consumed by the instruction.
| // EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX (pusha) | ||
| // int_no, err_code (pushed manually) | ||
| // EIP, CS, EFLAGS (pushed by CPU) | ||
| // (useresp, ss only in user mode) | ||
| struct interrupt_frame { | ||
| uint32_t gs, fs, es, ds; // Segment registers (top of stack) | ||
| uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // pusha order |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interrupt_frame struct member order for pusha registers appears to be incorrect. The pusha instruction pushes registers in the order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI. This means in memory (from low to high address), they appear as EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI. However, the struct defines them as "edi, esi, ebp, esp, ebx, edx, ecx, eax" (low address to high address). When ESP is passed to the C handler and cast to this struct, the first field should be at the lowest address. Since GS was pushed last, it's at the lowest address, so the struct starts correctly with gs. After the segment registers come the pusha registers, where EAX should be at the lowest address, not EDI. The correct order should be: eax, ecx, edx, ebx, esp, ebp, esi, edi.
| // EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX (pusha) | |
| // int_no, err_code (pushed manually) | |
| // EIP, CS, EFLAGS (pushed by CPU) | |
| // (useresp, ss only in user mode) | |
| struct interrupt_frame { | |
| uint32_t gs, fs, es, ds; // Segment registers (top of stack) | |
| uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // pusha order | |
| // EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI (pusha, from low to high address) | |
| // int_no, err_code (pushed manually) | |
| // EIP, CS, EFLAGS (pushed by CPU) | |
| // (useresp, ss only in user mode) | |
| struct interrupt_frame { | |
| uint32_t gs, fs, es, ds; // Segment registers (top of stack) | |
| uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi; // pusha order |
|
|
||
| __asm__ __volatile__ ( | ||
| "cld\n\t" | ||
| "rep stosb\n\t" | ||
| : "+c" (size), "+D" (bmp) | ||
| : "a" (0xFF) |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline assembly constraint uses "+D" for the pointer, which treats it as a pointer in the EDI register. However, 'bmp' is declared as uint8_t*, and after the assembly, bmp will point to the end of the bitmap. While this doesn't affect the operation since the result isn't used afterward, it's potentially confusing. Consider using "=D" and a dummy output variable, or make it clearer with a comment that bmp is modified.
| __asm__ __volatile__ ( | |
| "cld\n\t" | |
| "rep stosb\n\t" | |
| : "+c" (size), "+D" (bmp) | |
| : "a" (0xFF) | |
| uint8_t* bmp_end; | |
| __asm__ __volatile__ ( | |
| "cld\n\t" | |
| "rep stosb\n\t" | |
| : "+c" (size), "=D" (bmp_end) | |
| : "a" (0xFF), "1" (bmp) |
| if (total_pages > 0x100000) { | ||
| total_pages = 0x100000; | ||
| } |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The maximum page limit of 0x100000 (1048576 pages = 4GB of memory) is hardcoded. This arbitrary limit should be documented with a comment explaining why it exists, or ideally be defined as a named constant at the top of the file.
| if (total_pages > 0) { | ||
| uint32_t t = total_pages; | ||
| char buf[12]; | ||
| int idx = 0; | ||
| while (t > 0 && idx < 11) { | ||
| buf[idx++] = (char)('0' + (t % 10)); | ||
| t /= 10; | ||
| } | ||
| while (idx--) console_putc(buf[idx]); | ||
| } |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When v is 0, the function returns early and never enters the while loop. However, in pmm.c lines 294-303 and 305-314, the code assumes the loop will run at least once. If total_pages or free_pages is 0, nothing will be printed, which may not be the intended behavior. Consider handling the zero case explicitly by printing "0" instead of printing nothing.
| buf[idx++] = (char)('0' + (f % 10)); | ||
| f /= 10; | ||
| } | ||
| while (idx--) console_putc(buf[idx]); |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When free_pages is 0, the while loop condition (f > 0) prevents any output. This means nothing will be printed between " total pages, " and " free pages\n", which creates confusing output like "X total pages, free pages". The zero case should be handled explicitly.
| while (idx--) console_putc(buf[idx]); | |
| while (idx--) console_putc(buf[idx]); | |
| } else { | |
| console_putc('0'); |
|
|
||
| #define MB2_MAGIC 0x36d76289 |
Copilot
AI
Dec 27, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MB2_MAGIC constant is defined in multiple files (pmm.c, mmap.c, and kernel.c). This should be defined once in a shared header to avoid duplication and potential inconsistencies.
| #define MB2_MAGIC 0x36d76289 | |
| #ifndef MB2_MAGIC | |
| #define MB2_MAGIC 0x36d76289 | |
| #endif |
closes #10
개발 내용