Before I begin, I understand there may be similar questions to mine, but I do not see any questions that answer mine. If you really think you see one that answers my question, I respectfully ask you to tell me in the comments before closing my answer. Thank you.
Hello!
I have a problem - for the last couple of hours I've been trying to get physical memory management in my C kernel. The C kernel is running in protected mode, so my first initial thought was to do the memory mapping code in the ASM portion.
The problem is, I don't know exactly how I should organize or go about this. My C kernel is located at 0x1000, and whenever I try to change that address, the system has a horrendous crash. I need some advice on how to go about doing this. Also, my MBR boot sector loads in some more assembly code into 0x0600, so it will overwrite that code.
My code structure: MBR (also referred to as 1st stage) -> ASM kernel (also referred to as Stage 3) -> ASM kernel loads C kernel and jumps to protected mode, then calls the kernel -> C kernel
What I've tried:
- Reading the memory map into 0x000:0x1000 in ASM and hoping for the best - resulted in a sector read error (probably because some of the code was overwritten or something like that)
- Using code samples from here (C language) and running it in the kernel - doing the E820 method resulted in an ISR exception of division by 0, and doing manual probing resulted in strange values that didn't change based on the memory in the VM.
- Using BrokenThorn Entertainment's memory mapping in Assembly. This was a little weird to try to implement, and it also took up too much space, but I tried - resulted in either a crash or bad values (strangely, I couldn't get any of the values to appear as a parameter, maybe my struct code was messed up)
- Enabling paging in Assembly - resulted in no change.
- (giving up around this point) - Doing multiboot code just to see if it might work (when I say multiboot code I mean stuff like
MEMINFO equ 1 << 0
, and, just to reiterate, my code is not multiboot) - resulted in some(?) success, not with memory mapping, but the kernel could read the parameters passed to it. - Reading the memory map into 0x0000:0x0900, for some reason (I think I forgot my second stage was located in that range.) - resulted in no change.
I'm at a loss here. I can't figure out what to do.
Could anyone help me with what type of structure I should use and what code I should use to do it? (when I say structure, I mean like, load in ASM at this point, load kernel to 0xblah, etc)
Thank you all so much! This means a lot to me.
Some code samples:
ASM kernel file (all includes are fine):
; ===============================================
; 32-bit kernel for reduceOS
; ===============================================
; Loaded in by loader.asm at 0x0600,
bits 16 ; Beginning at 16-bits, switching to 32-bit later
org 0x0600 ; Starting at address 0x0600, loaded in by loader.asm
jmp main
; ---------------------------------------
; Includes
; --------------------------------------
%include "include/stdio.inc"
%include "include/gdt.inc"
%include "include/a20.inc"
%include "include/memory.inc"
; ---------------------------------------
; Data and strings
; ---------------------------------------
preparing db "Loading reduceOS...", 0x0D, 0x0A, 0x00
readErrorStr db "Error reading sectors.", 0x0D, 0x0A, 0x00
kernelOffset equ 0x1000
; -------------------------------------------------------------------
; readSector - Purpose is to load the actual C kernel to 0x1000
; Parameters: CL, ES, BX, AL
; -------------------------------------------------------------------
readSector:
mov ah, 2h
mov dh, 0
mov ch, 0
int 13h
jnc loadGood
fail:
mov si, readErrorStr
call print16
int 0x16
stop:
cli
hlt
jmp stop
loadGood:
mov si, preparing
call print16
ret
; ---------------------------------------
; main - 16-bit entry point
; Installing GDT, storing BIOS info, and enabling protected mode
; ---------------------------------------
main:
; The loader has successfully loaded us to 0x0600! Continue with execution to protected mode!
call installGDT ; Install the GDT
call enableA20_KKbrd_Out ; Enable A20
mov si, preparing ; Print our preparing string
call print16
; Before we enter protected mode, we need to load our C kernel.
; BIOS interrupts aren't supported in pmode, so we do it here.
; ES is already set to the proper values.
mov al, 40 ; AL - sector amount to read
mov bx, kernelOffset ; Read to 0x1000 (remember it reads to ES:BX)
mov cl, 4 ; Starting from sector 4, which in our case is 0x600(where the code is located)
call readSector ; Read the sector
EnablePmode:
; Enter protected mode
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_DESC:main32 ; Far jump to fix CS
bits 32 ; We are now 32 bit!
%include "include/stdio32.inc"
%include "include/paging.inc"
main32:
; Set registers up
mov ax, DATA_DESC
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax ; Stack between 0x00000000
mov esp, 0x00090000 ; and 0x0008FFFF (576KB)
call clear32 ; Clear screen
mov ebx, buildNum
call puts32 ; Call puts32 to print
mov ebx, kernOK ; Print our kernel okay message
call puts32
call enablePaging ; Enable paging.
call kernelOffset ; The kernel is located at
jmp $
buildNum db 0x0A, 0x0A, 0x0A, " reduceOS Development Build", 0
kernOK db 0x0A, 0x0A, 0x0A, " Loading C kernel...", 0
I don't think I need to provide any C kernel code. If I do, let me know in the comments, I happily will provide any code.
The C kernel loads IDT (GDT is already loaded in kernel.asm), installs ISR, sets up PIT and keyboard, and then waits.
Again, thank you all for your help!
CodePudding user response:
I'm posting this as a resource for anyone who needs help with this kind of problem.
I managed to get it working - pretty much. Using BrokenThorn Entertainment's bootinfo structure I was able to successfully get my kernel to read memory data.
I fixed the bug I mentioned where the kernel wouldn't load to anything outside of 0x1000 shortly after - it was a linker problem (I specified -Ttext 0x1000
). After I updated the address to 0x1100, that fixed that, meaning the memory map would load properly.
There was still 2 more problems - the first major one was getting the memory map overwrote some of my code loaded in memory - making the OS fail to boot. After some debugging and a lot of trial and error, I relocated my 2nd stage to 0x0000:0x07C0, fixing the issue.
The final problem was pushing the values to the kernel. This took considerably less time, but was still a debacle none the less. The reason it was a problem was because my kernel goes through 2 ASM files before being loaded (2nd stage and a very very short kernel loader that is linked with the kernel). I managed to fix it by setting eax
to boot_info
and pushing that in the 2nd stage, then pushing eax
again in the kernel loader.
If you came to this question looking for an answer where the kernel can stay located at 0x1000, I think the only solution is to write to some really far off memory address, but that isn't a good idea, as eventually your kernel will overwrite it with its size.