2

我正在编写一个 64 位 UEFI 操作系统(GNU-EFI - Bootloader)。我想知道操作系统中的用户模式和内核模式,我必须在我的操作系统中实现用户模式和内核模式,我在互联网上找到了一些但它对我不起作用(我认为这是因为 64 位),那么我该怎么做呢?

我用这个:

OSDEV- Ring3

但是当我在我的 gdt 中实现它时,我的内核挂掉了!

我的内核代码:

评论不是我的普通代码,这些是从 OSDEV 复制的 - 这些挂在内核中,所以我评论了那些。

gdt.h

// gdt.h

#pragma once
#include <stdint.h>

struct GDTDescriptor {
    uint16_t Size;
    uint64_t Offset;
} __attribute__((packed));

struct GDTEntry {
    uint16_t Limit0;
    uint16_t Base0;
    uint8_t Base1;
    uint8_t AccessByte;
    uint8_t Limit1_Flags;
    uint8_t Base2;
    
    // These are from OSDEV, if I enable these, Kernel Hangs Out

    // unsigned int Read_Write;
    // unsigned int Confirming_Expand_Down;
    // unsigned int Code;
    // unsigned int Code_data_segment;
    // unsigned int DPL;
    // unsigned int present;
    // unsigned int available;
    // unsigned int long_mode;
    // unsigned int big;
    // unsigned int gran;

}__attribute__((packed));

struct GDT {
    GDTEntry Null; //0x00
    GDTEntry KernelCode; //0x08
    GDTEntry KernelData; //0x10
    GDTEntry UserNull;
    GDTEntry UserCode;
    GDTEntry UserData;
} __attribute__((packed)) 
__attribute((aligned(0x1000)));

extern GDT DefaultGDT;

extern "C" void LoadGDT(GDTDescriptor* gdtDescriptor);

gdt.cpp

// gdt.cpp
#include "gdt.h"


__attribute__((aligned(0x1000)))
GDT DefaultGDT = {
    {0, 0, 0, 0x00, 0x00, 0}, // null
    {0, 0, 0, 0x9a, 0xa0, 0}, // kernel code segment
    {0, 0, 0, 0x92, 0xa0, 0}, // kernel data segment
    {0, 0, 0, 0x00, 0x00, 0}, // user null
    {0, 0, 0, 0x9a, 0xa0, 0}, // user code segment    
    {0, 0, 0, 0x92, 0xa0, 0}, // user data segment
};


// This is for commented vars of GDTEntry Struct

// __attribute__((aligned(0x1000)))
// GDT DefaultGDT = {
//     {0, 0, 0, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // null
//     {0, 0, 0, 0x9a, 0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // kernel code segment
//     {0, 0, 0, 0x92, 0xa0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // kernel data segment

//     {0, 0, 0, 0x00, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // user null
    
//     {0xFFFF, 0, 0, 0x00, 0xF, 0, 1, 0, 1, 1, 3, 1, 1, 0, 1, 1}, // user code segment

//     {0xFFFF, 0, 0, 0x00, 0xF, 0, 1, 0, 0, 1, 3, 1, 1, 0, 1, 1}, // user code segment
// };

gdt.asm

[bits 64]
LoadGDT:   
    lgdt [rdi]
    mov ax, 0x10 
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    pop rdi
    mov rax, 0x08
    push rax
    push rdi
    retfq
GLOBAL LoadGDT

如何在 64 位 UEFI 操作系统中进入 Ring3,然后如何将用户模式切换到内核模式?

4

1 回答 1

0

您从 OSDev引用的 GDT 条目结构如下所示:

struct gdt_entry_bits {
    unsigned int limit_low              : 16;
    unsigned int base_low               : 24;
    unsigned int accessed               :  1;
    unsigned int read_write             :  1; // readable for code, writable for data
    unsigned int conforming_expand_down :  1; // conforming for code, expand down for data
    unsigned int code                   :  1; // 1 for code, 0 for data
    unsigned int code_data_segment      :  1; // should be 1 for everything but TSS and LDT
    unsigned int DPL                    :  2; // privilege level
    unsigned int present                :  1;
    unsigned int limit_high             :  4;
    unsigned int available              :  1; // only used in software; has no effect on hardware
    unsigned int long_mode              :  1;
    unsigned int big                    :  1; // 32-bit opcodes for code, uint32_t stack for data
    unsigned int gran                   :  1; // 1 to use 4k page addressing, 0 for byte addressing
    unsigned int base_high              :  8;
} __packed; // or `__attribute__((packed))` depending on compiler

这个结构定义使用了位域;注意每个字段声明的结尾,其中有一个冒号,后跟该字段占用的位数。尽管每个字段都有自己的类型声明,但字段将根据它们的位宽打包在一起;OSDev 示例中这些位宽的总和为 64。

您的书面定义(如果包含注释字段)为 48 字节宽。GDT 条目应为 8 字节(64 位)宽:请参阅英特尔软件开发人员手册的第 3.4.5 节。我强烈建议将英特尔手册放在手边;OSDev wiki 上的大部分信息对于 x86_64 来说已经过时了,而 Intel(和 AMD)手册会不断更新。

关于内核和用户模式之间的切换,x86_64 有专门的系统调用指令SYSCALLSYSRET. 我建议使用这些指令在内核模式和用户模式之间进行切换,例如 Linux 内核通常会这样做(尽管它IRET在某些情况下也会使用)。SYSRET即使进程没有SYSCALL通过正确设置环境,您也可以跳转到用户代码(请参阅链接的指令参考)。

于 2021-06-02T16:26:47.330 回答