Operating system 如何从自己的引导加载程序跳转到自己的内核?

Operating system 如何从自己的引导加载程序跳转到自己的内核?,operating-system,Operating System,我正在开发一个自制操作系统,我不知道如何从我的自制引导程序切换到我的自制内核。 我不知道它是怎么工作的  我知道它是从记忆开始的,但我不知道它是从什么数字开始的 有人告诉我,我可以只看minix源代码,但当我从iso解析它时,它根本没有告诉我任何事情,它告诉我的只是linux目录的配置。 我已经想了一个星期了,但什么也没发生。 如果我不够努力,我很抱歉。 因为没有类似的问题,我想我会问你一个问题: < P>我会给你展示一个很好的注释,跳过一个我写的C++内核的例子,也许你可以从那里去写你自己的。

我正在开发一个自制操作系统,我不知道如何从我的自制引导程序切换到我的自制内核。 我不知道它是怎么工作的  我知道它是从记忆开始的,但我不知道它是从什么数字开始的 有人告诉我,我可以只看minix源代码,但当我从iso解析它时,它根本没有告诉我任何事情,它告诉我的只是linux目录的配置。 我已经想了一个星期了,但什么也没发生。 如果我不够努力,我很抱歉。
因为没有类似的问题,我想我会问你一个问题:

< P>我会给你展示一个很好的注释,跳过一个我写的C++内核的例子,也许你可以从那里去写你自己的。我在Linux Ubuntu 20上工作

在主目录中创建一个名为OS的文件夹,并在该文件夹中创建一个名为kernel.cpp的文件。在该文件中放置以下内容:

void map_framebuffer(){
    unsigned long* page_directory_table_pointer = (unsigned long*) 0xb018;
    *page_directory_table_pointer = 0x1e00f;
    unsigned long* page_table_pointer = (unsigned long*) 0x1e000;
    /*
    This is the actual address of the framebuffer in QEMU
    for the video mode I set up in boot.asm.
    In this case it is hardcoded. You may want to actually get this from the VBE structure.
    The VBE structure is placed at 0x7e00 in memory by the boot.asm routine.
    */
    unsigned long framebuffer_address = (unsigned long) 0xfd00000f;
    for (unsigned int i = 0; i < 512; i++){
        *page_table_pointer = framebuffer_address;
        framebuffer_address += 0x1000;
        page_table_pointer++;
    }
}

void main(){
    map_framebuffer();
    
    /*
    I made sure that the framebuffer is mapped to adress 0x600000 in the map_framebuffer() function.
    You may want to identity map it instead or map it somewhere else. You'll then have to 
    calculate page table offsets and stuff.
    */
    unsigned char* framebuffer_pointer = (unsigned char*)0x600000;
    //Make a few pixels become white. Each pixel is 3 bytes RGB.
    for (unsigned int i = 0; i < 10000; i++)
        *(framebuffer_pointer + i) = 0xff;
        
    asm volatile(
        "halt:\n\t"
        "hlt\n\t"
        "jmp halt\n\t");
}

/*
Memory map of kernel
7e00 - 8000 -> VBE INFO structure
8000 - 9000 -> paging.bin (booting asm code)
9000 - a000 -> pml4
a000 - b000 -> pdpt
b000 - c000 -> pdt
1b000 - 1c000 -> pt1 (kernel identity mapped pages)
1c000 - 1d000 -> pt2 (kernel identity mapped pages)
1d000 - 1e000 -> pt3 (kernel identity mapped pages)
1e000 - 1f000 -> pt4 (mapped to the framebuffer, only 2MB are mapped to the framebuffer which could take more than this)
30000 - 80000 -> kernel.elf (temporary kernel file)
80000 -> kernel stack (growing downward)
*/
现在创建名为paging.asm的第三个文件,并将以下内容放入其中:

org 0x8000

bits 32

;Set up paging
mov eax, 0x00009008
mov cr3, eax

pml4t:
mov dword [0x9000], 0x0000a00f
mov dword [0x9004], 0x0
pdpt:
mov dword [0xa000], 0x0000b00f
mov dword [0xa004], 0x0
pdt:
mov dword [0xb000], 0x0001b00f
mov dword [0xb004], 0x0
mov dword [0xb008], 0x0001c00f
mov dword [0xb00c], 0x0
mov dword [0xb010], 0x0001d00f
mov dword [0xb014], 0x0
pt:
mov edx, 0x3
mov eax, 0x200
mov ebx, 0x0000000f
mov ecx, 0x1b000
next_table:
next_entry:
mov dword [ecx], ebx
add ecx, 0x4
mov dword [ecx], 0x0
add ebx, 0x1000     ;add 4096 to the adress pointed to by ebx (the next physical page)
add ecx, 0x4
sub eax, 0x1
cmp eax, 0x0
jne next_entry
mov eax, 0x200
sub edx, 0x1
cmp edx, 0x0
jne next_table

mov eax, cr4            ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax

mov ecx, 0xC0000080     ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr

mov eax, cr0            ;enable paging
or eax, 1 << 31
mov cr0, eax

lgdt[gdtr]          ;load a 64 bit gdt (will be ignored afterwards)

jmp 0x08:longMode

bits 64

longMode:

mov ax, 0x10        ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov rsp, 0x80000

mov rdi, 0x30000    ;address where elf reading occurs
mov rsi, 0x30000

add rdi, 24         ;program entry
mov rax, [rdi]      ;placed in rax

add rdi, 8          ;program header table position
mov rbx, [rdi]      ;put it in rbx

add rdi, 24         ;move to number of entries in program header
mov cx, [rdi]       ;put it in cx (2 bytes)

mov rdi, 0x30000    ;beginning of file
add rdi, rbx        ;go to program header 0
add rsi, rbx

next_segment:
mov edx, [rdi]      ;put the type of segment in edx (4 bytes)
cmp edx, 0x1        ;determine if it is loadable
jne not_loadable    ;if it isnt jump to not_loadable
add rdi, 0x8        ;else load it
mov r8, [rdi]       ;put the offset in the file where data for this segment resides in r8
add rdi, 0x8        ;go to virtual address of segment
mov r9, [rdi]       ;put it in r9
add rdi, 0x10       ;move to size of segment in file
mov r10, [rdi]      ;put it in r10
add rdi, 0x8        ;move to size of segment in memory
mov r11, [rdi]      ;put it in r11

mov rdi, 0x30000    ;move back to beginning of file
add rdi, r8         ;add segment offset to be at the segment position

next_byte:
mov dl, [rdi]       ;put the byte at rdi in dl
mov [r9], dl        ;move it to virtual address in r9
add r9, 0x1         ;add 1 byte to virtual address
add rdi, 0x1        ;add 1 byte to rdi
sub r10, 0x1        ;substract 1 byte from size of segment in file
sub r11, 0x1        ;substract 1 byte from size of segment in memory
cmp r10, 0x0        ;is segment finished
jne next_byte       ;if not go to next_byte
cmp r11, 0x0        ;if yes tcheck memory size
je no_padding       ;if no padding required jmp to no_padding
padding:
xor dl, dl
mov [r9], dl
add r9, 0x01
sub r11, 0x1
cmp r11, 0x0
jne padding
                    
not_loadable:
no_padding:
sub cx, 0x1
cmp cx, 0x0
je finished
add rsi, 56
mov rdi, rsi
jmp next_segment

finished:

jmp rax ;Jump to the entry point of the ELF CPP file

halt:
hlt
jmp halt

;The 64-bits GDT
gdt_start:
        dw 0xFFFF
    dw 0
    db 0                        
    db 0
    db 1
    db 0
gdt_code:
    dw 0x1111   ;limit 0-15
        dw 0x0      ;base 0-15
        db 0x0      ;base 16-23 
        db 10011010b    ;pr, privi (2), s, ex, dc, rw, ac 
        db 10101111b    ;gr, sz, limit 16-19
        db 0x0      ;base 24-31
gdt_data:
        dw 0x1111
        dw 0x0
        db 0x0
        db 10010010b
        db 00001111b
        db 0x0  
gdtr:
        dw $ - gdt_start - 1
        dq gdt_start
您始终可以将所有这些内容放在bash文件中并自动启动它。QEMU命令中的-s使虚拟机侦听端口1234。您可以使用
gdb
调试内核。在bashshell中键入
gdb
,然后
目标远程本地主机:1234
。然后您将调试机器。您可以键入
dump memory result.bin 0x1000 0x2000
将机器内存从0x1000转储到文件result.bin中的0x2000(或任何地址)。您可以使用
hextump-C result.bin
查看来自终端的内存转储。输出是little endian,因此需要将其反转为实际值


在上面的代码中,我正在加载用g++编译的ELF文件,以寻址0x30000。然后我解析文件并跳到它的入口点。cpp代码将帧缓冲区映射到虚拟内存中,并使几个像素变为白色。代码远不是完美的,但如果您完全理解它,那么您可以走很长的路。

好的,您可以让引导加载程序从存储内核的磁盘扇区读取,然后跳转到内存中与内核入口点对应的任何位置。但是你的问题太模糊了,无法判断哪一部分是你的问题。这是你从引导加载程序改为ke的地方我不知道如何描述它谢谢你的长源代码!我想设计它的基础上的源代码。在哪里可以找到内存地址和其他规范?您可以在osdev.org上找到有关代码的大部分信息。此外,我选择的内存地址是任意的,但依赖于启动时RAM的标准内存映射。你也可以在osdev.org上找到这个内存映射。我会尽我最大的努力,这将需要大量的知识。
org 0x8000

bits 32

;Set up paging
mov eax, 0x00009008
mov cr3, eax

pml4t:
mov dword [0x9000], 0x0000a00f
mov dword [0x9004], 0x0
pdpt:
mov dword [0xa000], 0x0000b00f
mov dword [0xa004], 0x0
pdt:
mov dword [0xb000], 0x0001b00f
mov dword [0xb004], 0x0
mov dword [0xb008], 0x0001c00f
mov dword [0xb00c], 0x0
mov dword [0xb010], 0x0001d00f
mov dword [0xb014], 0x0
pt:
mov edx, 0x3
mov eax, 0x200
mov ebx, 0x0000000f
mov ecx, 0x1b000
next_table:
next_entry:
mov dword [ecx], ebx
add ecx, 0x4
mov dword [ecx], 0x0
add ebx, 0x1000     ;add 4096 to the adress pointed to by ebx (the next physical page)
add ecx, 0x4
sub eax, 0x1
cmp eax, 0x0
jne next_entry
mov eax, 0x200
sub edx, 0x1
cmp edx, 0x0
jne next_table

mov eax, cr4            ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax

mov ecx, 0xC0000080     ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr

mov eax, cr0            ;enable paging
or eax, 1 << 31
mov cr0, eax

lgdt[gdtr]          ;load a 64 bit gdt (will be ignored afterwards)

jmp 0x08:longMode

bits 64

longMode:

mov ax, 0x10        ;10000b = 10 for segment selector 2 (data)
mov ss, ax
mov rsp, 0x80000

mov rdi, 0x30000    ;address where elf reading occurs
mov rsi, 0x30000

add rdi, 24         ;program entry
mov rax, [rdi]      ;placed in rax

add rdi, 8          ;program header table position
mov rbx, [rdi]      ;put it in rbx

add rdi, 24         ;move to number of entries in program header
mov cx, [rdi]       ;put it in cx (2 bytes)

mov rdi, 0x30000    ;beginning of file
add rdi, rbx        ;go to program header 0
add rsi, rbx

next_segment:
mov edx, [rdi]      ;put the type of segment in edx (4 bytes)
cmp edx, 0x1        ;determine if it is loadable
jne not_loadable    ;if it isnt jump to not_loadable
add rdi, 0x8        ;else load it
mov r8, [rdi]       ;put the offset in the file where data for this segment resides in r8
add rdi, 0x8        ;go to virtual address of segment
mov r9, [rdi]       ;put it in r9
add rdi, 0x10       ;move to size of segment in file
mov r10, [rdi]      ;put it in r10
add rdi, 0x8        ;move to size of segment in memory
mov r11, [rdi]      ;put it in r11

mov rdi, 0x30000    ;move back to beginning of file
add rdi, r8         ;add segment offset to be at the segment position

next_byte:
mov dl, [rdi]       ;put the byte at rdi in dl
mov [r9], dl        ;move it to virtual address in r9
add r9, 0x1         ;add 1 byte to virtual address
add rdi, 0x1        ;add 1 byte to rdi
sub r10, 0x1        ;substract 1 byte from size of segment in file
sub r11, 0x1        ;substract 1 byte from size of segment in memory
cmp r10, 0x0        ;is segment finished
jne next_byte       ;if not go to next_byte
cmp r11, 0x0        ;if yes tcheck memory size
je no_padding       ;if no padding required jmp to no_padding
padding:
xor dl, dl
mov [r9], dl
add r9, 0x01
sub r11, 0x1
cmp r11, 0x0
jne padding
                    
not_loadable:
no_padding:
sub cx, 0x1
cmp cx, 0x0
je finished
add rsi, 56
mov rdi, rsi
jmp next_segment

finished:

jmp rax ;Jump to the entry point of the ELF CPP file

halt:
hlt
jmp halt

;The 64-bits GDT
gdt_start:
        dw 0xFFFF
    dw 0
    db 0                        
    db 0
    db 1
    db 0
gdt_code:
    dw 0x1111   ;limit 0-15
        dw 0x0      ;base 0-15
        db 0x0      ;base 16-23 
        db 10011010b    ;pr, privi (2), s, ex, dc, rw, ac 
        db 10101111b    ;gr, sz, limit 16-19
        db 0x0      ;base 24-31
gdt_data:
        dw 0x1111
        dw 0x0
        db 0x0
        db 10010010b
        db 00001111b
        db 0x0  
gdtr:
        dw $ - gdt_start - 1
        dq gdt_start
nasm -fbin  OS/boot.asm -oOS/boot.bin

nasm -fbin OS/paging.asm -oOS/paging.bin

g++ -static -ffreestanding -nostdlib -c -m64 OS/kernel.cpp -oOS/kernel.o

ld --entry main --oformat elf64-x86-64 --no-dynamic-linker -static -nostdlib OS/kernel.o -oOS/kernel.elf

dd if=/dev/zero of=OS/disk.img count=100 & dd if=OS/boot.bin of=OS/disk.img conv=notrunc & dd if=OS/paging.bin of=OS/disk.img seek=1 conv=notrunc & dd if=OS/kernel.elf of=OS/disk.img seek=9 conv=notrunc

qemu-system-x86_64 -hda OS/disk.img -s