第16课 - 保护模式中的特权级(中)

一种新的描述符:门描述符(Gate Descriptor)

通过门描述符在不同特权级的代码间进行跳转

根据应用场景的不同,门描述符分为:

调用门(Call Gates)

中断门(Interrupt Gates)

陷阱门(Trap Gate)

任务门(Task Gate)

门描述符的内存结构

每一个门描述符占用8字节内存

不同类项门描述的内存含义不同

调用门描述符(Call Gates)的定义

调用门描述符的工作原理

调用门描述符的使用

汇编小贴士

汇编语言中的跳转方式

段内跳转:call,jmp

参数为响度地址,函数调用时只需要保存当前偏移地址

段间跳转:call far, jmp far

参数为选择子和偏移地址

函数调用时需要同时保存段基地址和偏移地址

实验结论

门描述符是一种特殊的描述符,需要注册于段描述符表

调用门可以看作一个函数指针(保存具体函数的入口地址)

通过调用门选择子对相应的函数进行远调用(call far)

可以直接使用选择子:偏移地址的方式调用其它段的函数

使用调用门时偏移地址无意义,仅仅是语法需要

如何复用14课的PrintString函数?

将不同代码段需要复用的函数定义到独立的段中(retf)

计算每一个可复用函数的偏移量(FuncName - $$)

通过段选择子:偏移地址的方式对目标函数进行远调用

小结

门描述符是一种特殊的描述符,需要注册于段描述符表

门描述符分为:调用门,中断门,陷阱门,任务门

调用门可以看作一个函数指针(保存具体函数的入口地址)

调用门选择子对应的函数调用方式为远调用(call far)

代码

// inc.asm
; Segment Attribute     ; 一致性是指高特权不能访问低特权,但是低特权能访问高特权,不过特权级别不变
DA_32    equ    0x4000  ; 保护模式32位段
DA_DR    equ    0x90    ; 只读数据段
DA_DRW   equ    0x92    ; 可读写数据段
DA_DRWA  equ    0x93    ; 已访问可读写数据段
DA_C     equ    0x98    ; 只执行代码段
DA_CR    equ    0x9A    ; 可执行可读代码段
DA_CCO   equ    0x9C    ; 只执行一致代码段
DA_CCOR  equ    0x9E    ; 可执行可读一致代码段

; Segment Privilege
DA_DPL0        equ        0x00    ; DPL = 0
DA_DPL1        equ        0x20    ; DPL = 1
DA_DPL2        equ        0x40    ; DPL = 2
DA_DPL3        equ        0x60    ; DPL = 3

; Special Attribute
DA_LDT        equ 0x82
DA_TaskGate    equ 0x85    ; 任务门类型值
DA_386TSS      equ 0x89    ; 可用 386 任务状态段类型值
DA_386CGate    equ 0x8C    ; 386 调用门类型值
DA_386IGate    equ 0x8E    ; 386 中断门类型值
DA_386TGate    equ 0x8F    ; 386 陷阱门类型值

; Selector Attribute
SA_RPL0    equ    0        ; 高特权,内核级
SA_RPL1    equ    1        ; 中高特权,服务级
SA_RPL2    equ    2        ; 中低特权,服务级
SA_RPL3    equ    3        ; 低特权,用户级

SA_TIG    equ    0         ; GDT
SA_TIL    equ    4         ; LDT,本质是1,因为后面还有2bit的RPL标志位

; 描述符
; usage: Descriptor Base, Limit, Attr
;         Base: dd
;         Limit:dd (low 20 bits available)
;         Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3                            ; 段基址,段界限,段属性
    dw    %2 & 0xFFFF                          ; 段界限1
    dw    %1 & 0xFFFF                          ; 段基址1
    db    (%1 >> 16) & 0xFF                    ; 段基址2
    dw    ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF)  ; 属性1 + 段界限2 + 属性2
    db    (%1 >> 24) & 0xFF                    ; 段基址3
%endmacro                                      ; 共8字节

; 门
; usage: Gate Selector, Offset, DCount, Attr
;            Selector:    dw
;            Offset:      dd
;            DCount:      db
;            Attr:        db
%macro Gate 4
    dw (%2 & 0xFFFF)                         ; 偏移地址1
    dw %1                                    ; 选择子
    dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00)    ; 属性
    dw ((%2 >> 16) & 0xFFFF)                 ; 偏移地址2
%endmacro

// loader.asm
%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]                                ; 全局描述符表,部分段基址暂未知地址需使用时再调节
; GDT definition
;                        "函数名"    段基址        段界限                    段属性
GDT_ENTRY       :        Descriptor    0,            0,                        0                ; 全局段描述符表第0项不使用
CODE32_DESC     :        Descriptor    0,            Code32SegLen - 1,        DA_C    + DA_32
VIDEO_DESC      :        Descriptor    0xB8000,      0x07FFF,                 DA_DRWA + DA_32
STACK32_DESC    :        Descriptor    0,            TopOfStack32,            DA_DRW  + DA_32
FUNCTION_DESC   :        Descriptor    0,            FunctionSegLen - 1,      DA_C + DA_32
; Gate Descriptor
; Call Gate                            选择子            偏移    参数个数    属性
FUNC_CG_ADD_DESC        Gate    FunctionSelector,    CG_Add,        0,        DA_386CGate
FUNC_CG_SUB_DESC        Gate    FunctionSelector,    CG_Sub,        0,        DA_386CGate
; GDT end

GdtLen    equ    $ - GDT_ENTRY

GdtPtr:                                        ; 全局描述符表指针
        dw    GdtLen - 1    ; 偏移,记录描述符数量
        dd    0             ; 全局描述符起始地址,先初始化为0

; GDT Selector
;                                TI:全局、局部    RPL:请求权限级别
Code32Selector       equ (0x0001 << 3) + SA_TIG + SA_RPL0    ; 0x0001==第二个选择子
VideoSelector        equ (0x0002 << 3) + SA_TIG + SA_RPL0
Stack32Selector      equ (0x0003 << 3) + SA_TIG + SA_RPL0
FunctionSelector     equ (0x0004 << 3) + SA_TIG + SA_RPL0
FuncCGAddSelector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
FuncCGSubSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0

; end of [section .gdt]

TopOfStack16 equ 0x7c00

[section .s16]    ; 实模式代码段(16bit)
[bits 16]         ; 使用16位编译
ENTRY_SEGMENT:                                ; 16位保护模式入口段
    mov ax, cs                    ; 初始化相关寄存器
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16

            ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT        ; 初始化32位保护模式32位代码段描述符
    mov edi, CODE32_DESC

    call InitDescItem

    mov esi, STACK32_SEGMENT       ; 初始化32位保护模式32位栈段描述符
    mov edi, STACK32_DESC

    call InitDescItem

    mov esi, FUNCTION_SEGMENT     ; 初始化函数段描述符
    mov edi, FUNCTION_DESC

    call InitDescItem

            ; initialize GDT pointer struct
    mov eax, 0                     ; 代码段地址左移4位
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY             ; 代码段偏移地址==> 左移过后的代码段+全局描述符表入口地址偏移量
    mov dword [GdtPtr + 2], eax    ; 写入全局描述符表指针

            ; 1. load GDT
    lgdt [GdtPtr]                  ; 加载全局描述符表

            ; 2. close interrupt
    cli                            ; 关闭中断

            ; 3. open A20
    in al, 0x92                    ; 通过0x92端口开启A20地址线开关
    or al, 00000010b
    out 0x92, al

            ; 4. enter protect mode
    mov eax, cr0                   ; 设置cr0寄存器,进入保护模式
    or eax, 0x01
    mov cr0, eax

            ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0  ; 使用jmp跳转到32位代码段选择子的0偏移处

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:                                ; 初始化描述符项目
    push eax

    mov eax, 0                        ; 代码段地址左移4位
    mov ax, cs
    shl eax, 4                        ; 实地址=段寄存器地址左移4位+偏移地址
    add eax, esi
    mov word [edi + 2], ax            ; 将段基址写入描述符2个字节(16位寄存器),低32位的16-31bit(偏移2字节)
    shr eax, 16                       ; 移除eax实地址中已经写入段基址的2字节数据
    mov byte [edi + 4], al            ; 将段基址写入描述符1个字节(8位寄存器),高32位的0-7bit(偏移4+0=4字节)
    mov byte [edi + 7], ah            ; 将段基址写入描述符1个字节(8位寄存器),高32位的24-31bit(偏移4+3=7字节)

    pop eax

    ret

[section .s32]    ; 32位代码段
[bits 32]        ; 使用32位编译
CODE32_SEGMENT:                                ; 32位代码段数据
    mov ax, VideoSelector            ; 把视频段选择子放到gs全局段寄存器
    mov gs, ax

    mov ax, Stack32Selector
    mov ss, ax

    mov eax, TopOfStack32
    mov esp, eax

    mov ax, 2
    mov bx, 1

    call FuncCGAddSelector : 0        ; call FunctionSelector : CG_Add    ;选择子:偏移量
    call FuncCGSubSelector : 0        ; call FunctionSelector : CG_Sub

    jmp $

Code32SegLen    equ    $ - CODE32_SEGMENT

[section .func]
[bits 32]
FUNCTION_SEGMENT:

; ax --> a
; bx --> b
;
; return:
;        cx --> a + b
AddFunc:
    mov cx, ax
    add cx, bx
    retf

CG_Add equ AddFunc - $$

; ax --> a
; bx --> b
;
; return:
;        cx --> a - b
SubFunc:
    mov cx, ax
    sub cx, bx
    retf

CG_Sub equ SubFunc - $$

FunctionSegLen    equ $ - FUNCTION_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:                            ; 32位栈段定义
    times 1024 * 4 db 0

Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1

// loader.asm    修改复用的PrintString(loader.asm - Lesson 14)
%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]                                ; 全局描述符表,部分段基址暂未知地址需使用时再调节
; GDT definition
;                        "函数名"    段基址        段界限                    段属性
GDT_ENTRY         :        Descriptor    0,            0,                        0                ; 全局段描述符表第0项不使用
CODE32_DESC       :        Descriptor    0,            Code32SegLen - 1,       DA_C    + DA_32
VIDEO_DESC        :        Descriptor    0xB8000,      0x07FFF,                DA_DRWA + DA_32
DATA32_DESC       :        Descriptor    0,            Data32SegLen - 1,       DA_DR   + DA_32
STACK32_DESC      :        Descriptor    0,            TopOfStack32,           DA_DRW  + DA_32
CODE16_DESC       :        Descriptor    0,            0xFFFF,                 DA_C
UPDATE_DESC       :        Descriptor    0,            0xFFFF,                 DA_DRW            ; 回到保护模式
TASK_A_LDT_DESC   :        Descriptor    0,            TaskALdtLen - 1,        DA_LDT            ; 全局描述符表中的局部描述符
FUNCTION_DESC     :        Descriptor    0,            FunctionSegLen - 1,     DA_C    + DA_32   ; 选择门描述符
; GDT end

GdtLen    equ    $ - GDT_ENTRY

GdtPtr:                                        ; 全局描述符表指针
        dw    GdtLen - 1    ; 偏移,记录描述符数量
        dd    0             ; 全局描述符起始地址,先初始化为0

; GDT Selector
;                            TI:全局、局部    RPL:请求权限级别
Code32Selector    equ (0x0001 << 3) + SA_TIG + SA_RPL0    ; 0x0001==第二个选择子
VideoSelector     equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector    equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector   equ (0x0004 << 3) + SA_TIG + SA_RPL0
Code16Selector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
UpdateSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0    ; 回到保护模式
TaskALdtSelector  equ (0x0007 << 3) + SA_TIG + SA_RPL0    ; 局部描述符表选择子A
FunctionSelector  equ (0x0008 << 3) + SA_TIG + SA_RPL0    ; 选择门
; end of [section .gdt]

TopOfStack16 equ 0x7c00

[section .dat]                                ; 32位数据段
[bits 32]
DATA32_SEGMENT:
    DTOS                db "D.T.OS!", 0    ; 注意添加字符串结束标记0
    DTOS_OFFSET            equ DTOS - $$
    HELLO_WORLD            db "Hello World!", 0
    HELLO_WORLD_OFFSET    equ HELLO_WORLD - $$

Data32SegLen equ $ - DATA32_SEGMENT

[section .s16]    ; 实模式代码段(16bit)
[bits 16]         ; 使用16位编译
ENTRY_SEGMENT:                                ; 16位保护模式入口段
    mov ax, cs                    ; 初始化相关寄存器
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16

    mov [BACK_TO_REAL_MODE + 3], ax

            ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT        ; 初始化32位保护模式32位代码段描述符
    mov edi, CODE32_DESC

    call InitDescItem

    mov esi, DATA32_SEGMENT        ; 初始化32位保护模式32位数据段描述符
    mov edi, DATA32_DESC

    call InitDescItem

    mov esi, STACK32_SEGMENT       ; 初始化32位保护模式32位栈段描述符
    mov edi, STACK32_DESC

    call InitDescItem

    mov esi, CODE16_SEGMENT        ; 初始化32位保护模式16位代码段描述符
    mov edi, CODE16_DESC

    call InitDescItem

    mov esi, TASK_A_LDT_ENTRY      ; 初始化局部描述符表
    mov edi, TASK_A_LDT_DESC

    call InitDescItem

    mov esi, TASK_A_CODE32_SEGMENT
    mov edi, TASK_A_CODE32_DESC

    call InitDescItem

    mov esi, TASK_A_DATA32_SEGMENT
    mov edi, TASK_A_DATA32_DESC

    call InitDescItem

    mov esi, TASK_A_STACK32_SEGMENT
    mov edi, TASK_A_STACK32_DESC

    call InitDescItem

    mov esi, FUNCTION_SEGMENT
    mov edi, FUNCTION_DESC

    call InitDescItem

            ; initialize GDT pointer struct
    mov eax, 0                    ; 代码段地址左移4位
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY             ; 代码段偏移地址==> 左移过后的代码段+全局描述符表入口地址偏移量
    mov dword [GdtPtr + 2], eax    ; 写入全局描述符表指针

            ; 1. load GDT
    lgdt [GdtPtr]                 ; 加载全局描述符表

            ; 2. close interrupt
    cli                            ; 关闭中断

            ; 3. open A20
    in al, 0x92                    ; 通过0x92端口开启A20地址线开关
    or al, 00000010b
    out 0x92, al

            ; 4. enter protect mode
    mov eax, cr0                   ; 设置cr0寄存器,进入保护模式
    or eax, 0x01
    mov cr0, eax

            ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0  ; 使用jmp跳转到32位代码段选择子的0偏移处

BACK_ENTRY_SEGMENT:               ; 返回入口段(保护模式返回实模式)
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16          ; 设置16位栈顶指针

    in al, 0x92                                ; 关闭A20地址线
    and al, 11111101b
    out 0x92, al

    sti                                        ; 开启中断

    mov bp, HELLO_WORLD                        ; 使用实模式打印提示语句,注意bp指向的是字符串而不是段起始地址
    mov cx, 12                    ; 字符串长度
    mov dx, 0                     ; 打印于第0行
    mov ax, 0x1301                ; 在电传打字机模式输出,字符串只含字符,启用BL属性
    mov bx, 0x0007                ; 打印第0页,输出白色前景色
    int 0x10

    jmp $

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:                                ; 初始化描述符项目
    push eax

    mov eax, 0                        ; 代码段地址左移4位
    mov ax, cs
    shl eax, 4                        ; 实地址=段寄存器地址左移4位+偏移地址
    add eax, esi
    mov word [edi + 2], ax            ; 将段基址写入描述符2个字节(16位寄存器),低32位的16-31bit(偏移2字节)
    shr eax, 16                       ; 移除eax实地址中已经写入段基址的2字节数据
    mov byte [edi + 4], al            ; 将段基址写入描述符1个字节(8位寄存器),高32位的0-7bit(偏移4+0=4字节)
    mov byte [edi + 7], ah            ; 将段基址写入描述符1个字节(8位寄存器),高32位的24-31bit(偏移4+3=7字节)

    pop eax

    ret

[section .s16]
[bits 16]
CODE16_SEGMENT:                                ; 保护模式返回实模式
    mov ax, UpdateSelector            ; 注意不要操作CS寄存器,因为当前还是保护模式
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov eax, cr0                      ; 进入保护模式
    and al, 11111110b
    mov cr0, eax

BACK_TO_REAL_MODE:
    jmp 0 : BACK_ENTRY_SEGMENT        ; 注意使用段基地址 + 偏移量的方式

Code16SegLen equ $ - CODE16_SEGMENT

[section .func]
[bits 32]
FUNCTION_SEGMENT:

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintStringFunc:                            ; 打印字符串函数
    push ebp
    push eax
    push edi
    push cx
    push dx

print:
    mov cl, [ds:ebp]                  ; cl记录要打印的字符
    cmp cl, 0                         ; 对比字符串结束符号
    je end
    mov eax, 80                       ; 每行字符数
    mul dh                            ; 乘以行数
    add al, dl                        ; 加上列数,最终计算出要显示的位置
    shl eax, 1                        ; 左移乘以2,计算字节偏移
    mov edi, eax                      ; 写入显示的偏移地址到edi
    mov ah, bl                        ; 字符属性写入高位
    mov al, cl                        ; 字符写入低位
    mov [gs:edi], ax                  ; 写入显存对应地址
    inc ebp                           ; 指向下一个字符
    inc dl                            ; 指向屏幕的下一列
    jmp print

end:
    pop dx
    pop cx
    pop edi
    pop eax
    pop ebp

    retf

PrintString        equ        PrintStringFunc - $$

FunctionSegLen    equ        $ - FUNCTION_SEGMENT

[section .s32]    ; 32位代码段
[bits 32]        ; 使用32位编译
CODE32_SEGMENT:                                ; 32位代码段数据
    mov ax, VideoSelector            ; 把视频段选择子放到gs全局段寄存器
    mov gs, ax

    mov ax, Stack32Selector
    mov ss, ax

    mov eax, TopOfStack32
    mov esp, eax

    mov ax, Data32Selector
    mov ds, ax

    mov ebp, DTOS_OFFSET              ; 在保护模式输出字符串,地址为段偏移
    mov bx, 0x0C                      ; 黑底淡红字
    mov dh, 12                        ; 指定行地址,注意行列都是从0开始
    mov dl, 33                        ; 指定列地址

    call FunctionSelector : PrintString

    mov ebp, HELLO_WORLD_OFFSET       ; 打印另一个字符串
    mov bx, 0x0C
    mov dh, 13
    mov dl, 31

    call FunctionSelector : PrintString

    mov ax, TaskALdtSelector           ; 使用局部描述符表

    lldt ax

    jmp TaskACode32Selector : 0

    ; jmp Code16Selector : 0             ; 返回到16位实模式

Code32SegLen    equ    $ - CODE32_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:                          ; 32位栈段定义
    times 1024 * 4 db 0

Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1

; =======================================
;
;            Task A code Segment
;
; =======================================

[section .task-a-ldt]
; Task A LDT definition
;                                段基址,            段界限,                段属性
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC        :    Descriptor    0,        TaskACode32SegLen - 1,        DA_C   + DA_32
TASK_A_DATA32_DESC        :    Descriptor    0,        TaskAData32SegLen - 1,        DA_DR  + DA_32
TASK_A_STACK32_DESC       :    Descriptor    0,        TaskAStack32SegLen - 1,       DA_DRW + DA_32

TaskALdtLen    equ $ - TASK_A_LDT_ENTRY

; Task A LDT Selector
TaskACode32Selector     equ (0x0000 << 3) + SA_TIL + SA_RPL0
TaskAData32Selector     equ (0x0001 << 3) + SA_TIL + SA_RPL0
TaskAStack32Selector    equ (0x0002 << 3) + SA_TIL + SA_RPL0

[section .task-a-dat]
[bits 32]
TASK_A_DATA32_SEGMENT:
    TASK_A_STRING            db "This is Task A!", 0
    TASK_A_STRING_OFFSET    equ TASK_A_STRING - $$

TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT

[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
    times 1024 db 0

TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32 equ TaskAStack32SegLen - 1

[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
    mov ax, VideoSelector
    mov gs, ax

    mov ax, TaskAStack32Selector
    mov ss, ax

    mov eax, TaskATopOfStack32
    mov esp, eax

    mov ax, TaskAData32Selector
    mov ds, ax

    mov ebp, TASK_A_STRING_OFFSET
    mov bx, 0x0C
    mov dh, 14
    mov dl, 29

    call FunctionSelector : PrintString

    jmp Code16Selector : 0                        ; 返回16位保护模式
; 删除原有TaskAPrintString函数
TaskACode32SegLen    equ    $ - TASK_A_CODE32_SEGMENT
 

输出

ndisasm -o 0x9000 loader > loader.txt反汇编查看两个函数的调用情况,可从16位实模式跳转到32位保护模式的地方开始(包含jmp指令),下图是采用调用门调用两个函数的结果

采用段选择子:偏移地址的方式调用PrintString函数

原文地址:https://www.cnblogs.com/Dua677/p/10054392.html

时间: 2024-08-27 07:46:41

第16课 - 保护模式中的特权级(中)的相关文章

保护模式下的特权级检查

http://weibo.com/oA53NcVF=05Gp/1001604186124800122428蝗史录颂窗DHJ灾杏匾却 http://weibo.com/38w6WQw0=qZGp/1001604186124863037468右赘列焉寐CTU期倨挛婆 http://weibo.com/Uq14g91D=5gXp/1001604186124816899756忱阶反豪头TSK夯忻眉呀 http://weibo.com/065JmsW1=2qZp/100160418612503500875

第十七课 保护模式中的特权级(下)

问题: 使用调用门如何实现不同特权级代码之间的跳转(如:从高特权级到低特权级)? 不幸的事实: 调用门只支持从低特权级到高特权级执行 无法利用调用门从高特权级到低特权级执行 从高特权级的代码段通过return  far可以返回到低特权级的代码段执行.这时return  far是一个跳转指令,完成从高特权级到低特权级的跳转,这正是我们想要的. return的本质是做跳转的,而不是我们根深蒂固的做返回的.只是最常用的方式是做返回使用. 思路整理: 调用门的特权级跳转: 1.通过远调用(call  f

操作系统中的特权级检查

这里涉及到的几个概念:DPL.RPL.CPL DPL:存在于段描述符中,描述访问此段的最低特权级,即访问此段的进程CPL和RPL必须大于等于此段的DPL RPL:存在于段选择符中的最低两位,成为请求特权级即进程的请求权限,这个可以由程序员自己设置但是其功能受到CPL的制约 CPL:存在于cs和ss寄存器中的最低两位,是当前执行程序或者任务的特权级. 一个段描述符的DPL为0表示只允许内核层的进程访问,为3表示允许任意进程访问. 在实模式下,由于段寄存器是16位的,地址线有20根,当时的寻址是通过

第十课 实模式到保护模式 上

远古时期的程序开发是直接操作物理内存的,CPU指令的操作数直接使用实地址(实际内存地址),程序员拥有绝对的权利,可以随意访问内存的任意一个地址,可以说是指哪打哪. 使用绝对的物理地址会带来很多问题,例如: 1.难以重定位,程序每次都需要同样地址的内存来运行. 2.给多道程序设计带来了障碍,也就是说,不管内存多大,但凡一个字节被其他程序占用都无法执行. 为了解决以上的问题,英特尔开发出了8086 CPU处理器,其引入了如下特性: 1.地址线宽度为20位,可访问1M内存空间 2.引入[段地址:偏移地

内核保护模式之分段机制

CPU的三种模式 1982年,intel推出了80286处理器,第一次提出了保护模式,在保护模式下,段寄存器中存储的不再是段基址,而是段选择子. 真正的段基址存储在描述符高速缓存中,80286处理器访问内存,不需要段寄存器左移加上偏移. 在x86体系的CPU下,支持三种模式 实模式:兼容16位CPU的模式,当前的PC系统处于实模式(16位模式)运行状态,在这种状态下软件可访问的物理内存空间不能超过1MB,且无法发挥Intel 80386以上级别的32位CPU的4GB内存管理能力.实模式将整个物理

【自制操作系统04】从实模式到保护模式

通过前三章的努力,我们成功将控制权转交给了 loader.asm 这个程序.具体说就是 bios 通过加载并跳转到 0x7c00(IMB大叔们定的) 把控制权转交给了我们操作系统的第一个汇编程序 mbr.asm,然后 mbr.asm 里做的事就是通过加载 loader 程序并跳转到 0x900(这个是我们自己定的)把控制权转交给了 loader.asm 程序,目前这个程序里还只是向屏幕输出一行字符串"loader",今天我们就将扩展它.并且今天我们要做的事,是操作系统中的第一个精彩之处

第十一课 实模式到保护模式 中

为了解决内存访问越界的问题,英特尔发明了80286处理器,这款处理器引入了保护模式,为现代操作系统和应用程序奠定了基础,但是80286在设计上有些奇葩,例如: 段寄存器为24位,通用寄存器为16位,这样显得不伦不类. 理论上,段寄存器中的数值可以直接作为段基址.80286中16位寄存器最多访问64K的内存,为了访问16M的内存,必须不停的切换段基址. 虽然存在以上的问题,但是80286引入的保护模式是成功的,为了改进以上存在的问题,英特尔设计出了80386,这时计算机进入新时期的标志,80386

第12课 - 实模式到保护模式(下)

不一般的jmp(s16->s32) 在16位代码中,所有的立即数默认为16位 从16位代码段跳转到32位代码段时,必须做强制转换 深入保护模式:定义显存段 为了显示数据,必须存在两大硬件:显卡 + 显示器 显卡 为显示器提供需要显示的数据 控制显示器的模式和状态 显示器 将目标数据以可见的方式呈现在屏幕上 显存的概念和意义 显卡拥有自己内部的数据存储器,简称显存 显卡的工作模式:文本模式&图形模式 在不同的模式下,显卡对显存内容的解释是不同的 可以使用专属指令或int 0x10中断改变显卡工

任务和特权级保护(二)——《x86汇编语言:从实模式到保护模式》读书笔记32

之前做了那么多铺垫,我们终于可以看看第14章的代码了. 对于引导代码和用户程序,依然采用第13章的:对于内核程序(c14_core.asm),编译的时候有几行报错了,只要加上dword即可解决. 1. 为什么要用调用门 在第13章,为了能使用内核提供的例程,用户程序是用call far指令直接转移到内核例程(非一致代码段).因为CPL=目标代码段描述符的DPL=RPL=0,符合下面表格的条件,所以转移是没有问题的. 但是在本章,用户程序工作在3特权级,而非0特权级,所以是无法直接转移的.不过也不