无法使用e820获取内存映射

时间:2017-02-13 10:57:52

标签: c assembly memory-management operating-system

我正在编写一个非常基本的内核,并且为了获取物理内存映射,我使用了来自osdev的代码

global do_e820

do_e820:
    xor ebx, ebx        ; ebx must be 0 to start
    xor bp, bp      ; keep an entry count in bp
    mov edx, 0x0534D4150    ; Place "SMAP" into edx
    mov eax, 0xe820
    mov [es:di + 20], dword 1   ; force a valid ACPI 3.X entry
    mov ecx, 24     ; ask for 24 bytes
    int 0x15
    jc short .failed    ; carry set on first call means "unsupported function"
    mov edx, 0x0534D4150    ; Some BIOSes apparently trash this register?
    cmp eax, edx        ; on success, eax must have been reset to "SMAP"
    jne short .failed
    test ebx, ebx       ; ebx = 0 implies list is only 1 entry long (worthless)
    je short .failed
    jmp short .jmpin
.e820lp:
    mov eax, 0xe820     ; eax, ecx get trashed on every int 0x15 call
    mov [es:di + 20], dword 1   ; force a valid ACPI 3.X entry
    mov ecx, 24     ; ask for 24 bytes again
    int 0x15
    jc short .e820f     ; carry set means "end of list already reached"
    mov edx, 0x0534D4150    ; repair potentially trashed register
.jmpin:
    jcxz .skipent       ; skip any 0 length entries
    cmp cl, 20      ; got a 24 byte ACPI 3.X response?
    jbe short .notext
    test byte [es:di + 20], 1   ; if so: is the "ignore this data" bit clear?
    je short .skipent
.notext:
    mov ecx, [es:di + 8]    ; get lower uint32_t of memory region length
    or ecx, [es:di + 12]    ; "or" it with upper uint32_t to test for zero
    jz .skipent     ; if length uint64_t is 0, skip entry
    inc bp          ; got a good entry: ++count, move to next storage spot
    add di, 24
.skipent:
    test ebx, ebx       ; if ebx resets to 0, list is complete
    jne short .e820lp
.e820f:
    extern memMapLength
    mov [memMapLength], bp  ; store the entry count
    clc         ; there is "jc" on end of list to this point, so the carry must be cleared
    jmp .done
.failed:
    ;stc            ; "function unsupported" error exit 
    hlt
    ret
.done: 

C代码如下:

uint8_t memMapLength=0;
uint64_t availableMemBytes=0;

typedef struct memoryMapEntry{
    uint32_t baseLow;
    uint32_t baseHigh;
    uint64_t length;   
    uint32_t type;
    uint32_t acpi_null; 
    } memoryMapEntry;

memoryMapEntry* memMapArr=0; 
void DetectMem(){
    memMapArr= (memoryMapEntry*)(0x00007c40);
    uint8_t i;
    for(i=0;i<memMapLength;i++){
            if(memMapArr[i].type==1){           
                availableMemBytes += memMapArr[i].length;               
            }               
    }
}

在kernel_main函数中,我调用do_e820函数,然后调用DetectMem,如下所示:

do_e820();
DetectMem();
char *arr = (char *)&availableMemBytes;
terminal_write(arr);

然而,我无法启动我的内核,因为启动陷入无限循环。我做错了什么?

1 个答案:

答案 0 :(得分:1)

此答案使用了英特尔大会。
您刚刚删除了一段非常重要的代码。

mov di, 0x8004

使用jmp .done代替ret
删除.done空块。
您是否还在使用REAL MODE?因此,您应该在Boot Loader中执行此操作。
使用pushapopa
使其遵循调用约定。要使它遵循一个:

pop es

要使其正常工作,请使用以下程序集:

    jmp kmain
do_e820:
    pop es
    pusha
    mov di, 0x8004
    xor ebx, ebx
    xor bp, bp
    mov edx, 0x0534D4150
    mov eax, 0xe820
    mov [es:di + 20], dword 1
    mov ecx, 24
    int 0x15
    jc short .failed
    mov edx, 0x0534D4150    ; Some BIOSes apparently trash this register?
    cmp eax, edx        ; on success, eax must have been reset to "SMAP"
    jne short .failed
    test ebx, ebx       ; ebx = 0 implies list is only 1 entry long (worthless)
    je short .failed
    jmp short .jmpin
.e820lp:
    mov eax, 0xe820     ; eax, ecx get trashed on every int 0x15 call
    mov [es:di + 20], dword 1   ; force a valid ACPI 3.X entry
    mov ecx, 24     ; ask for 24 bytes again
    int 0x15
    jc short .e820f     ; carry set means "end of list already reached"
    mov edx, 0x0534D4150    ; repair potentially trashed register
.jmpin:
    jcxz .skipent       ; skip any 0 length entries
    cmp cl, 20      ; got a 24 byte ACPI 3.X response?
    jbe short .notext
    test byte [es:di + 20], 1   ; if so: is the "ignore this data" bit clear?
    je short .skipent
.notext:
    mov ecx, [es:di + 8]    ; get lower uint32_t of memory region length
    or ecx, [es:di + 12]    ; "or" it with upper uint32_t to test for zero
    jz .skipent     ; if length uint64_t is 0, skip entry
    inc bp          ; got a good entry: ++count, move to next storage spot
    add di, 24
.skipent:
    test ebx, ebx       ; if ebx resets to 0, list is complete
    jne short .e820lp
.e820f:
    mov di, 0x8000
    mov [es:di], bp
    clc
    popa
    ret
.failed:
    stc
    popa
    ret

用您的内核主要函数名称替换kmain
构建:
为此使用Linux或WSL。
准备:

sudo apt install python3 wget -y
wget http://dl.devdrive.org/xcdk.py -o ./xcdk.py
sudo python3 ./xcdk.py

用于构建的Envar表:

Envar        Description
=================================
CFILE        A file with your C code! No spaces. Extension must be .c
ASM          A file with the above assembly! No spaces. Extension must be .asm
TEMP         A temporary file. No spaces.
OUT          This is gonna be your build result. No spaces.

内部版本:

xcbuild 16 $ASM $CFILE $TEMP
nasm -fbin -o $OUT $TEMP

关于xcbuild的说明:它使用gccintel2gas将不同的文件组合为一个NASM程序集文件。安装程序脚本会为您安装此程序。它支持.asm.s.c文件。不支持C ++,因为C ++需要标准库。 xcbuild基于Python,目前处于实验阶段。有时会创建大型程序集文件。它不会更改任何指针,因此您将不得不更改这些指针。如果您使用64位输出,由于intel2gas中的当前错误,您将在NASM中获得少量的GAS(来自push指令)。您可以直接调用它的原因是 she bang 行。由于intel2gas中的其他错误,xcbuild会执行intel2gas之前和之后的intel2gas任务。 intel2gas之前的任务正在对代码进行“二进制化”以使其准备好intel2gas。 Binarifying包括删除前4行以及LFE*:行及其下的3行。它还会删除.cfi_*行和其他垃圾。 LFB是代码,LFE是Linux废话。