美文网首页
【异常】

【异常】

作者: 月下蓑衣江湖夜雨 | 来源:发表于2020-11-30 00:13 被阅读0次

效果

效果

代码

代码

代码结构

代码_start.asm

SELECTOR_KERNEL_CS  equ 8

; 导入函数
extern  cstart
extern  main

; 导入全局变量
extern  gdt_ptr
extern  idt_ptr
extern  c_cs;
extern  c_ds;
extern  c_es;
extern  c_ss;

; 导出 _start
global _start   

[SECTION .bss]
StackSpace      resb    2 * 1024
StackTop:       ; 栈顶

[section .text] ; 代码在此
_start:
    ; 此时内存看上去是这样的(更详细的内存情况在 LOADER.ASM 中有说明):
    ;              ┃                                    ┃
    ;              ┃                 ...                ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■Page  Tables■■■■■■┃
    ;              ┃■■■■■(大小由LOADER决定)■■■■┃ PageTblBase
    ;    00101000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M
    ;    00100000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃□□□□ Hardware  Reserved □□□□┃ B8000h ← gs
    ;       9FC00h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
    ;       90000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■KERNEL.BIN■■■■■■┃
    ;       80000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
    ;       30000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┋                 ...                ┋
    ;              ┋                                    ┋
    ;           0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
    ;
    ;
    ; GDT 以及相应的描述符是这样的:
    ;
    ;                     Descriptors               Selectors
    ;              ┏━━━━━━━━━━━━━━━━━━┓
    ;              ┃         Dummy Descriptor           ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_C    (0~4G)     ┃   8h = cs
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_RW   (0~4G)     ┃  10h = ds, es, fs, ss
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_VIDEO                 ┃  1Bh = gs
    ;              ┗━━━━━━━━━━━━━━━━━━┛
    ;
    ; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
    ; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es.
    ;
    ;


    ; 把 esp 从 LOADER 挪到 KERNEL
    mov esp, StackTop   ; 堆栈在 bss 段中
    sgdt    [gdt_ptr]   ; cstart() 调用的relocate_gdt中将会用到 gdt_ptr
    
    mov word[c_cs], cs
    mov word[c_ds], ds
    mov word[c_es], es
    mov word[c_ss], ss
    
    call    cstart      ; 在此函数中改变了gdt_ptr,让它指向新的GDT
    lgdt    [gdt_ptr]   ; 使用新的GDT
    lidt    [idt_ptr]   ; 使用IDT
    call    main        ; 重新放置好GDT后,重新进入main
    sti
    ud2
    jmp $

代码const.h

#ifndef _CONST_H_
#define _CONST_H_

/* 函数类型 */
#define PUBLIC          /* PUBLIC宏展开后,啥都没有,仅仅起个提示作用,此函数本文件外也可访问 */
#define PRIVATE static  /* static指定函数作用域是本文件内 */

#define KernelPrintBufSize  1024    /* static指定函数作用域是本文件内 */

#endif

代码cstart.c

#include "type.h"
#include "const.h"
#include "protect.h"
#include "interrupt.h"
#include "string.h"
#include "vsprintf.h"
#include "print.h"

/* 本文件内函数声明 */
char buf[KernelPrintBufSize];          // 显示用临时缓冲区

// 打印显示保护模式下cs, ds、es、ss的值
// 段选择子16位:索引号13位,T1(1位):T1=0时指向GDT,T1=1时指向LDT,RPL(2位)请求特权级
uint16 c_cs;
uint16 c_ds;
uint16 c_es;
uint16 c_ss;

/*======================================================================*/
PUBLIC void cstart(){
    // 打印在第2行,前两行分别是boot、loader打印
    print_str("\n\n[cstart] start\n");
    
    // 重新放置GDT
    relocate_gdt();
    
    // 灰底红字:打印DPL
    print_descriptor();
    
    // 打印寄存器的值
    sprintf(buf, "[cstart], [cs: %d], [ds: %d], [es: %d], ss[%d]", c_cs, c_ds, c_es, c_ss);
    print_str_fix_pos((80*24+0)*2, buf);
    
    // 初始化idt
    init_idt();
}

// 设置完GDT、IDT后,进入main
PUBLIC void main(){
    print_str("[main] start\n");
}

代码interrupt.asm

; 导入函数
extern  exception_handler
extern  spurious_irq

; 导出函数
global  divide_error
global  single_step_exception
global  nmi
global  breakpoint_exception
global  overflow
global  bounds_check
global  inval_opcode
global  copr_not_available
global  double_fault
global  copr_seg_overrun
global  inval_tss
global  segment_not_present
global  stack_exception
global  general_protection
global  page_fault
global  copr_error
global  hwint00
global  hwint01
global  hwint02
global  hwint03
global  hwint04
global  hwint05
global  hwint06
global  hwint07
global  hwint08
global  hwint09
global  hwint10
global  hwint11
global  hwint12
global  hwint13
global  hwint14
global  hwint15

; 中断和异常 -- 硬件中断
; ---------------------------------
%macro  hwint_master    1
    push    %1
    call    spurious_irq
    add esp, 4
    hlt
%endmacro

[section .text] ; 代码在此
ALIGN   16
hwint00:        ; Interrupt routine for irq 0 (the clock).
    hwint_master    0

ALIGN   16
hwint01:        ; Interrupt routine for irq 1 (keyboard)
    hwint_master    1

ALIGN   16
hwint02:        ; Interrupt routine for irq 2 (cascade!)
    hwint_master    2

ALIGN   16
hwint03:        ; Interrupt routine for irq 3 (second serial)
    hwint_master    3

ALIGN   16
hwint04:        ; Interrupt routine for irq 4 (first serial)
    hwint_master    4

ALIGN   16
hwint05:        ; Interrupt routine for irq 5 (XT winchester)
    hwint_master    5

ALIGN   16
hwint06:        ; Interrupt routine for irq 6 (floppy)
    hwint_master    6

ALIGN   16
hwint07:        ; Interrupt routine for irq 7 (printer)
    hwint_master    7

; ---------------------------------
%macro  hwint_slave 1
    push    %1
    call    spurious_irq
    add esp, 4
    hlt
%endmacro
; ---------------------------------

ALIGN   16
hwint08:        ; Interrupt routine for irq 8 (realtime clock).
    hwint_slave 8

ALIGN   16
hwint09:        ; Interrupt routine for irq 9 (irq 2 redirected)
    hwint_slave 9

ALIGN   16
hwint10:        ; Interrupt routine for irq 10
    hwint_slave 10

ALIGN   16
hwint11:        ; Interrupt routine for irq 11
    hwint_slave 11

ALIGN   16
hwint12:        ; Interrupt routine for irq 12
    hwint_slave 12

ALIGN   16
hwint13:        ; Interrupt routine for irq 13 (FPU exception)
    hwint_slave 13

ALIGN   16
hwint14:        ; Interrupt routine for irq 14 (AT winchester)
    hwint_slave 14

ALIGN   16
hwint15:        ; Interrupt routine for irq 15
    hwint_slave 15



; 中断和异常 -- 异常
divide_error:
    push    0xFFFFFFFF  ; no err code
    push    0       ; vector_no = 0
    jmp exception
single_step_exception:
    push    0xFFFFFFFF  ; no err code
    push    1       ; vector_no = 1
    jmp exception
nmi:
    push    0xFFFFFFFF  ; no err code
    push    2       ; vector_no = 2
    jmp exception
breakpoint_exception:
    push    0xFFFFFFFF  ; no err code
    push    3       ; vector_no = 3
    jmp exception
overflow:
    push    0xFFFFFFFF  ; no err code
    push    4       ; vector_no = 4
    jmp exception
bounds_check:
    push    0xFFFFFFFF  ; no err code
    push    5       ; vector_no = 5
    jmp exception
inval_opcode:
    push    0xFFFFFFFF  ; no err code
    push    6       ; vector_no = 6
    jmp exception
copr_not_available:
    push    0xFFFFFFFF  ; no err code
    push    7       ; vector_no = 7
    jmp exception
double_fault:
    push    8       ; vector_no = 8
    jmp exception
copr_seg_overrun:
    push    0xFFFFFFFF  ; no err code
    push    9       ; vector_no = 9
    jmp exception
inval_tss:
    push    10      ; vector_no = A
    jmp exception
segment_not_present:
    push    11      ; vector_no = B
    jmp exception
stack_exception:
    push    12      ; vector_no = C
    jmp exception
general_protection:
    push    13      ; vector_no = D
    jmp exception
page_fault:
    push    14      ; vector_no = E
    jmp exception
copr_error:
    push    0xFFFFFFFF  ; no err code
    push    16      ; vector_no = 10h
    jmp exception

exception:
    call    exception_handler
    add esp, 4*2    ; 让栈顶指向 EIP,堆栈中从顶向下依次是:EIP、CS、EFLAGS
    hlt 

代码interrupt.c

#include "type.h"
#include "const.h"
#include "protect.h"
#include "interrupt.h"
#include "string.h"
#include "vsprintf.h"
#include "print.h"

// 显示用临时缓冲区,定义在cstart.c中
extern char buf[KernelPrintBufSize]; 
 
// 初始化可编程中断控制器,仅在本文件中使用,定义在本文件中;
PRIVATE void init_8259A();

// 读写IO端口,定义在io.asm中;
void   out_byte(t_port port, uint8 value);
uint8  in_byte(t_port port);

// 汇编:中断处理函数,定义在interrupt.asm中
void    divide_error();
void    single_step_exception();
void    nmi();
void    breakpoint_exception();
void    overflow();
void    bounds_check();
void    inval_opcode();
void    copr_not_available();
void    double_fault();
void    copr_seg_overrun();
void    inval_tss();
void    segment_not_present();
void    stack_exception();
void    general_protection();
void    page_fault();
void    copr_error();
void    hwint00();
void    hwint01();
void    hwint02();
void    hwint03();
void    hwint04();
void    hwint05();
void    hwint06();
void    hwint07();
void    hwint08();
void    hwint09();
void    hwint10();
void    hwint11();
void    hwint12();
void    hwint13();
void    hwint14();
void    hwint15();
/*======================================================================*
                            init_prot
 *----------------------------------------------------------------------*
 初始化 IDT
 *======================================================================*/
/* 中断向量 */
#define INT_VECTOR_DIVIDE           0x0
#define INT_VECTOR_DEBUG            0x1
#define INT_VECTOR_NMI              0x2
#define INT_VECTOR_BREAKPOINT       0x3
#define INT_VECTOR_OVERFLOW         0x4
#define INT_VECTOR_BOUNDS           0x5
#define INT_VECTOR_INVAL_OP         0x6
#define INT_VECTOR_COPROC_NOT       0x7
#define INT_VECTOR_DOUBLE_FAULT     0x8
#define INT_VECTOR_COPROC_SEG       0x9
#define INT_VECTOR_INVAL_TSS        0xA
#define INT_VECTOR_SEG_NOT          0xB
#define INT_VECTOR_STACK_FAULT      0xC
#define INT_VECTOR_PROTECTION       0xD
#define INT_VECTOR_PAGE_FAULT       0xE
#define INT_VECTOR_COPROC_ERR       0x10

/* 中断向量 */
#define INT_VECTOR_IRQ0             0x20
#define INT_VECTOR_IRQ8             0x28 

// 设置中断的主要步骤:
// 1、设置8259A可编程中断器
// 2、设置IDT
// 开中断?
//   
PUBLIC void init_prot(){
    init_8259A();

    // 全部初始化成中断门(没有陷阱门)
    // #define  PRIVILEGE_KRNL  0 全部在r0特权级
    init_idt_desc(INT_VECTOR_DIVIDE,        DA_386IGate, divide_error,          PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_DEBUG,         DA_386IGate, single_step_exception, PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_NMI,           DA_386IGate, nmi,                   PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_BREAKPOINT,    DA_386IGate, breakpoint_exception,  PRIVILEGE_KRNL);  // PRIVILEGE_USER
    init_idt_desc(INT_VECTOR_OVERFLOW,      DA_386IGate, overflow,              PRIVILEGE_KRNL);  // PRIVILEGE_USER
    init_idt_desc(INT_VECTOR_BOUNDS,        DA_386IGate, bounds_check,          PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_INVAL_OP,      DA_386IGate, inval_opcode,          PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_NOT,    DA_386IGate, copr_not_available,    PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_DOUBLE_FAULT,  DA_386IGate, double_fault,          PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_SEG,    DA_386IGate, copr_seg_overrun,      PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_INVAL_TSS,     DA_386IGate, inval_tss,             PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_SEG_NOT,       DA_386IGate, segment_not_present,   PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_STACK_FAULT,   DA_386IGate, stack_exception,       PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_PROTECTION,    DA_386IGate, general_protection,    PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_PAGE_FAULT,    DA_386IGate, page_fault,            PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_COPROC_ERR,    DA_386IGate, copr_error,            PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 0,      DA_386IGate, hwint00,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 1,      DA_386IGate, hwint01,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 2,      DA_386IGate, hwint02,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 3,      DA_386IGate, hwint03,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 4,      DA_386IGate, hwint04,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 5,      DA_386IGate, hwint05,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 6,      DA_386IGate, hwint06,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ0 + 7,      DA_386IGate, hwint07,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 0,      DA_386IGate, hwint08,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 1,      DA_386IGate, hwint09,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 2,      DA_386IGate, hwint10,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 3,      DA_386IGate, hwint11,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 4,      DA_386IGate, hwint12,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 5,      DA_386IGate, hwint13,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 6,      DA_386IGate, hwint14,               PRIVILEGE_KRNL);
    init_idt_desc(INT_VECTOR_IRQ8 + 7,      DA_386IGate, hwint15,               PRIVILEGE_KRNL);
}

/*======================================================================*
                            init_8259A
 *======================================================================*/
PRIVATE void init_8259A(){
    out_byte(INT_M_CTL, 0x11);                  // Master 8259, ICW1.
    out_byte(INT_S_CTL, 0x11);                  // Slave  8259, ICW1.
    out_byte(INT_M_CTLMASK, INT_VECTOR_IRQ0);   // Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
    out_byte(INT_S_CTLMASK, INT_VECTOR_IRQ8);   // Slave  8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
    out_byte(INT_M_CTLMASK, 0x4);               // Master 8259, ICW3. IR2 对应 '从8259'.
    out_byte(INT_S_CTLMASK, 0x2);               // Slave  8259, ICW3. 对应 '主8259' 的 IR2.
    out_byte(INT_M_CTLMASK, 0x1);               // Master 8259, ICW4.
    out_byte(INT_S_CTLMASK, 0x1);               // Slave  8259, ICW4.
    out_byte(INT_M_CTLMASK, 0xFD);              // Master 8259, OCW1. 
    out_byte(INT_S_CTLMASK, 0xFF);              // Slave  8259, OCW1. 
}

/*======================================================================*
                           spurious_irq
 *======================================================================*/
PUBLIC void spurious_irq(int irq){
    //disp_str("spurious_irq: ");
    //disp_int(irq);
    //disp_str("\n");
    sprintf(buf, "spurious_irq is %d\n", irq);
    print_str(buf);
}

/*======================================================================*
                            exception_handler
 *----------------------------------------------------------------------*
 异常处理
 *======================================================================*/
extern uint32  disp_pos;
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags){
    int i;
    int text_color = 0x74; /* 灰底红字 */
    char err_description[][64] = {  "#DE Divide Error",
                    "#DB RESERVED",
                    "—  NMI Interrupt",
                    "#BP Breakpoint",
                    "#OF Overflow",
                    "#BR BOUND Range Exceeded",
                    "#UD Invalid Opcode (Undefined Opcode)",
                    "#NM Device Not Available (No Math Coprocessor)",
                    "#DF Double Fault",
                    "    Coprocessor Segment Overrun (reserved)",
                    "#TS Invalid TSS",
                    "#NP Segment Not Present",
                    "#SS Stack-Segment Fault",
                    "#GP General Protection",
                    "#PF Page Fault",
                    "—  (Intel reserved. Do not use.)",
                    "#MF x87 FPU Floating-Point Error (Math Fault)",
                    "#AC Alignment Check",
                    "#MC Machine Check",
                    "#XF SIMD Floating-Point Exception"
                };

    /* 通过打印空格的方式清空屏幕的前五行,并把 disp_pos 清零 */
    disp_pos = 0;
    for(i=0;i<80*5;i++){
        print_str(" ");
    }
    disp_pos = 0;

    print_color_str("Exception! --> ", text_color);
    print_color_str(err_description[vec_no], text_color);
    print_color_str("\n\n", text_color);
    print_color_str("EFLAGS:", text_color);
    //disp_int(eflags);
    sprintf(buf, "%d", eflags);
    print_str(buf);
    print_color_str("CS:", text_color);
    //disp_int(cs);
    sprintf(buf, "%d", cs);
    print_str(buf);
    print_color_str("EIP:", text_color);
    //disp_int(eip);
    sprintf(buf, "%d", eip);
    print_str(buf);

    if(err_code != 0xFFFFFFFF){
        print_color_str("Error code:", text_color);
        //disp_int(err_code);
        sprintf(buf, "%d", err_code);
        print_str(buf);
    }
}

代码interrupt.h

#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_

/* 8259A interrupt controller ports. */
#define INT_M_CTL       0x20    /* I/O port for interrupt controller         <Master> */
#define INT_M_CTLMASK   0x21    /* setting bits in this port disables ints   <Master> */
#define INT_S_CTL       0xA0    /* I/O port for second interrupt controller  <Slave>  */
#define INT_S_CTLMASK   0xA1    /* setting bits in this port disables ints   <Slave>  */

/* 中断向量 */
#define INT_VECTOR_IRQ0         0x20
#define INT_VECTOR_IRQ8         0x28

PUBLIC void init_prot();
PUBLIC void spurious_irq(int irq);
PUBLIC void exception_handler(int vec_no, int err_code, int eip, int cs, int eflags);

#endif

代码io.asm

; 导出函数
global  out_byte
global  in_byte

[SECTION .text]
; ========================================================================
;                  void out_byte(t_port port, t_8 value);
; ========================================================================
out_byte:
    mov edx, [esp + 4]      ; port
    mov al, [esp + 4 + 4]   ; value
    out dx, al
    nop ; 一点延迟
    nop
    ret

; ========================================================================
;                  t_8 in_byte(t_port port);
; ========================================================================
in_byte:
    mov edx, [esp + 4]      ; port
    xor eax, eax
    in  al, dx
    nop ; 一点延迟
    nop
    ret

代码print.h

#ifndef _PRINT_H_
#define _PRINT_H_

PUBLIC  void    print_str(char * info);
PUBLIC  void    print_str_fix_pos(int pos, char * info);
PUBLIC  void    print_color_str(char * info, int color);

#endif

代码print_str.asm

; 导出函数
global  print_str
global  print_str_fix_pos
global  print_color_str

; 导出变量
global  disp_pos

[SECTION .data]
disp_pos            dd  0
disp_pos_2          dd  0

[SECTION .text]
; ========================================================================
;                  void print_str(char * info);
; ========================================================================
print_str:
    push    ebp
    mov ebp, esp

    mov esi, [ebp + 8]  ; pszInfo
    mov edi, [disp_pos]
    mov ah, 0Fh
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    mov [disp_pos], edi

    pop ebp
    ret
    
; ========================================================================
;                  void print_str_fix_pos(int pos, char * info);
; ========================================================================
print_str_fix_pos:
    push    ebp
    mov ebp, esp
    
    mov ebx, [ebp + 8]  ; pszPos
    mov [disp_pos_2], ebx

    mov esi, [ebp + 12] ; pszInfo
    mov edi, ebx;[disp_pos_2]
    mov ah, 0Fh
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    ;mov    [disp_pos_2], edi

    pop ebp
    ret 

; ========================================================================
;                  void print_color_str(char * info, int color);
; ========================================================================
print_color_str:
    push    ebp
    mov ebp, esp

    mov esi, [ebp + 8]  ; pszInfo
    mov edi, [disp_pos]
    mov ah, [ebp + 12]  ; color
.1:
    lodsb
    test    al, al
    jz  .2
    cmp al, 0Ah ; 是回车吗?
    jnz .3
    push    eax
    mov eax, edi
    mov bl, 160
    div bl
    and eax, 0FFh
    inc eax
    mov bl, 160
    mul bl
    mov edi, eax
    pop eax
    jmp .1
.3:
    mov [gs:edi], ax
    add edi, 2
    jmp .1

.2:
    mov [disp_pos], edi

    pop ebp
    ret

代码protect.c

#include "type.h"
#include "const.h"
#include "protect.h"
#include "interrupt.h"
#include "string.h"
#include "vsprintf.h"
#include "print.h"

// GDT & IDT
PUBLIC  uint8       gdt_ptr[6];                   // 0~15:Limit  16~47:Base
PUBLIC  DESCRIPTOR  gdt[GDT_SIZE];
PUBLIC  uint8       idt_ptr[6];                   // 0~15:Limit  16~47:Base
PUBLIC  GATE        idt[IDT_SIZE];

// 显示用临时缓冲区,定义在cstart.c中
extern char buf[KernelPrintBufSize]; 

// 将 LOADER 中的 GDT 复制到新的 GDT 中
// _start.asm中_start函数调用cstart.c中start函数前,已经通过:
//     sgdt [gdt_ptr]   ; cstart() 调用的relocate_gdt中将会用到 gdt_ptr
// 将旧的GDT表的地址存放于gdt_ptr中
//
PUBLIC void relocate_gdt() {
    sprintf(buf, "[relocate_gdt], old gdt_ptr is {%d, %d, %d, %d, %d, %d}\n", gdt_ptr[0], gdt_ptr[1], gdt_ptr[2], gdt_ptr[3], gdt_ptr[4], gdt_ptr[5]);
    print_str(buf);
    
    // 将旧的GDT复制到新的GDT中来
    memcpy( &gdt,                              // New GDT
        (void*)(*((uint32*)(&gdt_ptr[2]))),    // Base  of Old GDT
        *((uint16*)(&gdt_ptr[0])) + 1          // Limit of Old GDT
        );
        
    // 此时gdt_ptr中存储的是旧的GDT
    // 将新的GDT地址存放到gdt_ptr中  
    // gdt_ptr[6] 共 6 个字节:0~15:Limit  16~47:Base。用作 sgdt 以及 lgdt 的参数。
    uint16* p_gdt_limit = (uint16*)(&gdt_ptr[0]);
    uint32* p_gdt_base  = (uint32*)(&gdt_ptr[2]);
    *p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR) - 1;
    *p_gdt_base  = (uint32)&gdt;
    // _start.asm中_start函数调用cstart.c中start函数后,将通过:
    //    lgdt  [gdt_ptr]   ; 使用新的GDT
    // 后面并不需要修改cs, ds、es、ss等的值,因为这些只是索引值,loader中之前已经设置了
    
    sprintf(buf, "[relocate_gdt], new gdt_ptr is {%d, %d, %d, %d, %d, %d}\n", gdt_ptr[0], gdt_ptr[1], gdt_ptr[2], gdt_ptr[3], gdt_ptr[4], gdt_ptr[5]);
    print_str(buf);
}

// 打印特权级
//;GDT
//LABEL_GDT:           Descriptor 0, 0, 0                            ;空描述符
//LABEL_DESC_FLAT_C:   Descriptor 0,0fffffh, DA_C + DA_32            ;代码段,32位
//LABEL_DESC_FLAT_RW:  Descriptor 0,0fffffh, DA_DRW | DA_32          ; 0 ~ 4G
//LABEL_DESC_VIDEO:    Descriptor 0B8000h, 0ffffh,  DA_DRW           ;显存首地址
//
// cs下标是1
// ds、es、ss是2
//
// uint8    attr1;                  /* P(1) DPL(2) DT(1) TYPE(4) */
PUBLIC void print_descriptor(){
    int text_color = 0x74; // 灰底红字
    
    sprintf(buf, "[print_descriptor], LABEL_DESC_FLAT_C [attr1: %d]\n", gdt[1].attr1);
    print_color_str(buf, text_color);
    
    sprintf(buf, "[print_descriptor], LABEL_DESC_FLAT_RW [attr1: %d]\n", gdt[2].attr1);
    print_color_str(buf, text_color);
}

// 初始化IDT
PUBLIC void init_idt() {
    init_prot();
    
    // 将idt_ptr指向idt
    // idt_ptr[6] 共 6 个字节:0~15:Limit  16~47:Base。用作 sidt 以及 lidt 的参数。
    uint16* p_idt_limit = (uint16*)(&idt_ptr[0]);
    uint32* p_idt_base  = (uint32*)(&idt_ptr[2]);
    *p_idt_limit = IDT_SIZE * sizeof(GATE) - 1;
    *p_idt_base  = (uint32)&idt;
    // _start.asm中_start函数调用cstart.c中start函数后,将通过:
    //  lidt    [idt_ptr]   ; 使用IDT
}

/*======================================================================*
                             init_idt_desc
 *----------------------------------------------------------------------*
 初始化 386 中断门
 *======================================================================*/
PUBLIC void init_idt_desc(unsigned char vector, uint8 desc_type, t_pf_int_handler handler, unsigned char privilege){
    GATE *  p_gate  = &idt[vector];
    uint32  base    = (uint32)handler;
    p_gate->offset_low  = base & 0xFFFF;
    p_gate->selector    = SELECTOR_KERNEL_CS;
    p_gate->dcount      = 0;
    p_gate->attr        = desc_type | (privilege << 5);
    p_gate->offset_high = (base >> 16) & 0xFFFF;
}

代码protect.h

#ifndef _PROTECT_H_
#define _PROTECT_H_

/* GDT 和 IDT 中描述符的个数 */
#define GDT_SIZE    128
#define IDT_SIZE    256

/* 权限 */
#define PRIVILEGE_KRNL  0
#define PRIVILEGE_TASK  1
#define PRIVILEGE_USER  3

typedef void    (*t_pf_int_handler) ();

/* 存储段描述符/系统段描述符 */
typedef struct s_descriptor {       /* 共 8 个字节 */
    uint16  limit_low;              /* Limit */
    uint16  base_low;               /* Base */
    uint8   base_mid;               /* Base */
    uint8   attr1;                  /* P(1) DPL(2) DT(1) TYPE(4) */
    uint8   limit_high_attr2;       /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
    uint8   base_high;              /* Base */
} __attribute__((packed)) DESCRIPTOR;

/* 门描述符 */
typedef struct s_gate {
    uint16  offset_low;     /* Offset Low */
    uint16  selector;       /* Selector */
    uint8   dcount;         /* 该字段只在调用门描述符中有效。
                               如果在利用调用门调用子程序时引起特权级的转换和堆栈的改变,需要将外层堆栈中的参数复制到内层堆栈。
                               该双字计数字段就是用于说明这种情况发生时,要复制的双字参数的数量。 */
    uint8   attr;           /* P(1) DPL(2) DT(1) TYPE(4) */
    uint16  offset_high;    /* Offset High */
} __attribute__((packed)) GATE;

/* 选择子 */
#define SELECTOR_DUMMY      0           // ┓
#define SELECTOR_FLAT_C     0x08        // ┣ LOADER 里面已经确定了的.
#define SELECTOR_FLAT_RW    0x10        // ┃
#define SELECTOR_VIDEO      (0x18+3)    // ┛<-- RPL=3

#define DA_386IGate         0x8E        /* 386 中断门类型值           */
#define SELECTOR_KERNEL_CS  SELECTOR_FLAT_C
#define SELECTOR_KERNEL_DS  SELECTOR_FLAT_RW

// GDT
PUBLIC  void  relocate_gdt();
PUBLIC  void  print_descriptor();
// IDT
PUBLIC  void  init_idt();
PUBLIC  void  init_idt_desc(unsigned char vector, uint8 desc_type, t_pf_int_handler handler, unsigned char privilege);

#endif

代码stdarg.h

#ifndef _STDARG_H
#define _STDARG_H

typedef char *va_list;

/* Amount of space required in an argument list for an arg of type TYPE.
   TYPE may alternatively be an expression whose type is used.  */

#define __va_rounded_size(TYPE)  \
  (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

#ifndef __sparc__
#define va_start(AP, LASTARG)                       \
 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG)                       \
 (__builtin_saveregs (),                        \
  AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif

void va_end (va_list);      /* Defined in gnulib */
#define va_end(AP)

#define va_arg(AP, TYPE)                        \
 (AP += __va_rounded_size (TYPE),                   \
  *((TYPE *) (AP - __va_rounded_size (TYPE))))

#endif /* _STDARG_H */

代码string.asm

; 导出函数
global  memcpy

[SECTION .text]
; ------------------------------------------------------------------------
; void* memcpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
memcpy:
    push    ebp
    mov ebp, esp

    push    esi
    push    edi
    push    ecx

    mov edi, [ebp + 8]  ; Destination
    mov esi, [ebp + 12] ; Source
    mov ecx, [ebp + 16] ; Counter
.1:
    cmp ecx, 0      ; 判断计数器
    jz  .2      ; 计数器为零时跳出

    mov al, [ds:esi]        ; ┓
    inc esi         ; ┃
                    ; ┣ 逐字节移动
    mov byte [es:edi], al   ; ┃
    inc edi         ; ┛

    dec ecx     ; 计数器减一
    jmp .1      ; 循环
.2:
    mov eax, [ebp + 8]  ; 返回值

    pop ecx
    pop edi
    pop esi
    mov esp, ebp
    pop ebp

    ret         ; 函数结束,返回
; memcpy 结束-------------------------------------------------------------    

代码string.c

#include "string.h"

int strlen(const char * s){
    int i=0;
    while(s[i] != '\0'){
        i++;
    }
    return i;
}

char * strcpy(char * dst, const char *src){
    char *ret = dst;
    while ((*dst++=*src++)!='\0');
    return ret;
}

代码sting.h

#ifndef _STRING_H_
#define _STRING_H_

int strlen(const char * s);
char * strcpy(char * dst, const char *src);

#endif

#ifndef _STRING_MEMCPY_H_
#define _STRING_MEMCPY_H_

extern  void*  memcpy(void* pDst, void* pSrc, int iSize);

#endif

代码type.h

#ifndef _TYPE_H_
#define _TYPE_H_

typedef unsigned int    uint32;
typedef unsigned short  uint16;
typedef unsigned char   uint8;
typedef unsigned int    t_port;
typedef int t_bool;
/* Boolean */
#define TRUE    1
#define FALSE   0

#endif

#ifndef NULL
#define NULL ((void *) 0)
#endif

#ifndef _SIZE_T
#define _SIZE_T
typedef unsigned int size_t;
#endif

代码vsprintf.c

#include "stdarg.h"
#include "string.h" 

/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')

// 将字符数字转换成整数。输入是数字串指针的指针,返回是结果的数值,另外指针将前移。
static int skip_atoi(const char **s) {
    int i=0;
    while (is_digit(**s)){
        i = i*10 + *((*s)++) - '0';     // 这里导致指针前移
    }
        
    return i;
}

// 定义常用符号常数
#define ZEROPAD 1       /* pad with zero */
#define SIGN    2       /* unsigned/signed long */
#define PLUS    4       /* show plus */
#define SPACE   8       /* space if plus */
#define LEFT    16      /* left justified */
#define SPECIAL 32      /* 0x */
#define SMALL   64      /* use 'abcdef' instead of 'ABCDEF' */

// 除法操作,输入:n为被除数,base为除数;结果:n为商,函数返回值为余数。
#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })

// 将整数转换为指定进制的字符串。
// 输入:num-整数;base-进制;size-字符串长度;precision-数字长度(精度);type-类型选项。
// 输出:str字符串指针
static char * number(char * str, int num, int base, int size, int precision, int type){
    char c,sign,tmp[36];
    const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i;

    // 根据类型定义字母集,默认是大写字母
    if (type&SMALL) {
        digits="0123456789abcdefghijklmnopqrstuvwxyz";
    }
    // 如果类型指出要左调整(靠左边界),则屏蔽类型中的填零标志。
    // TODO: 这句没看懂?? 为什么要把最低位置为0
    if (type&LEFT) {
        type &= ~ZEROPAD;
    }
    // 本程序只能处理的进制范围:2-36
    if (base<2 || base>36) {
        return 0;
    }
    // TODO: 这句跟LEFT相关??
    c = (type & ZEROPAD) ? '0' : ' ' ;
    if (type&SIGN && num<0) {
        sign='-';
        num = -num;
    } else{
        sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
    }
    // 如果是带符号的,字符串宽度就减一
    if (sign) {
        size--;
    }
    if (type&SPECIAL) {
        if (base==16) {
            size -= 2; 
        }   // 16进制需要减2,用于前面的0x
        else if (base==8) {
            size--; 
        }  // 8进制的减1,因为前面的0
    }
    i=0;
    if (num==0){
        tmp[i++]='0';
    } else {
        while (num!=0){
            tmp[i++]=digits[do_div(num,base)];
        }
    }
    
    if (i>precision) {
        precision=i;
    }       // 若字符个数大于精度值,精度值扩展为字符个数
    
    size -= precision;                  // 宽度再减去用于存放数值字符的个数
    // 将转换结果放在str中,如果类型中没有填零(ZEROPAD)和左靠齐标志,
    // 则在str中首先填放剩余宽度值指出的空格数。若需要带符号位,则存入符号
    if (!(type&(ZEROPAD+LEFT))){
        while(size-->0){
            *str++ = ' ';
        }
    }
    if (sign){
        *str++ = sign;
    }
    // 如果是特殊转换的处理,8进制和16进制分别填入0/0x/0X
    if (type&SPECIAL) {
        if (base==8){
            *str++ = '0';
        } else if (base==16) {
            *str++ = '0';
            *str++ = digits[33];    // 'x' or 'X'
        }
    }
    // 如果类型么有左靠齐标志,则在剩余的宽度中存放c字符(‘0’或者空格)
    if (!(type&LEFT)){
        while(size-->0){
            *str++ = c;
        }
    }
    // 若i存有数值num的数字个数,若数字个数小于精度值,则str中放入 精度值-i 个'0'
    while(i<precision--){
        *str++ = '0';
    }
    // 将数值转换好的数字字符填入str中,共i个
    while(i-->0){
        *str++ = tmp[i];
    }
    // 若宽度值仍大于零,则表达类型标志中有左靠齐标志,则在剩余宽度中放入空格
    while(size-->0){
        *str++ = ' ';
    }
    
    return str;
}

// 格式化输出
int vsprintf(char *buf, const char *fmt, va_list args){
    int len;
    int i;
    char * str;     // 用于存放转换过程中的字符串
    char *s;
    int *ip;

    int flags;      /* flags to number() */

    int field_width;    /* width of output field */
    int precision;      /* min. # of digits for integers; max
                   number of chars for from string */
    int qualifier;      /* 'h', 'l', or 'L' for integer fields */

    // 扫描格式字符串,对于不是 % 的就依次存入str
    for (str=buf ; *fmt ; ++fmt) {
        if (*fmt != '%') {
            *str++ = *fmt;
            continue;
        }
        // 取得格式指示字符串中的标志域,并将标志常量放入flags变量中
        /* process flags */
        flags = 0;
        repeat:
            ++fmt;      /* this also skips first '%' */
            switch (*fmt) {
                case '-': flags |= LEFT; goto repeat;
                case '+': flags |= PLUS; goto repeat;
                case ' ': flags |= SPACE; goto repeat;
                case '#': flags |= SPECIAL; goto repeat;
                case '0': flags |= ZEROPAD; goto repeat;
                }
        // 取当前参数数字段宽度域值,放入field_width变量中,如果宽度域中是数值则直接取其为宽度值。
        // 如果宽度域中是字符'*',表示下一个参数指定宽度。因此调用va_arg取宽度值。若此时宽度值
        // 小于0,则该负数表示其带有标志域'-'标志(左靠齐),因此还需要在标志变量中填入该标志,并
        // 将字段宽度值取为其绝对值。
        /* get field width */
        field_width = -1;
        if (is_digit(*fmt))
            field_width = skip_atoi(&fmt);
        else if (*fmt == '*') {
            ++fmt;
            /* it's the next argument */
            field_width = va_arg(args, int);        // 这里有个bug,应插入++fmt。// TODO: 不懂
            if (field_width < 0) {
                field_width = -field_width;
                flags |= LEFT;
            }
        }

        // 取格式转换串的精度域,并放入precision变量中。精度域开始的标志是'.'.
        // 其处理过程与上面宽度域的类似。如果精度域中是数值则直接取其为精度值。如果精度域中是
        // 字符'*',表示下一个参数指定精度。因此调用va_arg取精度值。若此时宽度值小于0,则将
        // 字段精度值取为0.
        /* get the precision */
        precision = -1;
        if (*fmt == '.') {
            ++fmt;  
            if (is_digit(*fmt))
                precision = skip_atoi(&fmt);
            else if (*fmt == '*') {
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0)
                precision = 0;
        }

        /* get the conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
            qualifier = *fmt;
            ++fmt;
        }

        // 分析转换指示符
        switch (*fmt) {
            // ‘c’ 表示对应参数应是字符。此时如果标志域表明不是左靠齐,则该字段前面
            // 放入'宽度域值-1'个空格字符,然后再放入参数字符。如果宽度域大于0,
            // 则表示为左靠齐,则在参数字符后面添加'宽度值-1'个空格字符
        case 'c':
            if (!(flags & LEFT))
                while (--field_width > 0)
                    *str++ = ' ';
            *str++ = (unsigned char) va_arg(args, int);
            while (--field_width > 0)
                *str++ = ' ';
            break;

            // 's'表示对应参数是字符串。首先取参数字符串的长度,若其超过了精度域值,
            // 则扩展精度域=字符串长度。此时如果标志域表明不是左靠齐,则该字段前放入
            // '宽度值-字符串长度'个空格字符。然后再放入参数字符串。如果宽度域还大于0,
            // 则表示为左靠齐,则在参数字符串后面添加'宽度值-字符串长度'个空格字符。
        case 's':
            s = va_arg(args, char *);
            len = strlen(s);
            if (precision < 0)
                precision = len;
            else if (len > precision)
                len = precision;

            if (!(flags & LEFT))
                while (len < field_width--)
                    *str++ = ' ';
            for (i = 0; i < len; ++i)
                *str++ = *s++;
            while (len < field_width--)
                *str++ = ' ';
            break;

            // 'o'表示8进制,通过number函数处理
        case 'o':
            str = number(str, va_arg(args, unsigned long), 8,
                field_width, precision, flags);
            break;

            // 'p'表示一个指针类型,此时若该参数没有设置宽度域,默认宽度域为8
            // 并且需要添零,然后用number函数处理
        case 'p':
            if (field_width == -1) {
                field_width = 8;
                flags |= ZEROPAD;
            }
            str = number(str,
                (unsigned long) va_arg(args, void *), 16,
                field_width, precision, flags);
            break;

            // 'x'-转成16进制
        case 'x':
            flags |= SMALL;
        case 'X':
            str = number(str, va_arg(args, unsigned long), 16,
                field_width, precision, flags);
            break;

            // 'd' & 'i'  表示带符号整数;'u' 表示无符号整数
        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            str = number(str, va_arg(args, unsigned long), 10,
                field_width, precision, flags);
            break;

            // 'n'-表示要把到目前为止转换输出字符数保存到对应参数指针指定的位置中。
            // 首先利用va_arg()取得该参数指针,然后将已经转换好的字符数存到指向的位置
        case 'n':
            ip = va_arg(args, int *);
            *ip = (str - buf);
            break;

            // 若格式转换不是 % ,则表示格式字符串有错,直接将一个“%”写入输出串中。
            // 如果格式转换符的位置还有字符,则也直接将该字符写入输入串中,并返回继续处理
            // 格式字符串,否则表示已经处理到格式字符串的结尾处,退出循环。
        default:
            if (*fmt != '%')
                *str++ = '%';
            if (*fmt)
                *str++ = *fmt;
            else
                --fmt;
            break;
        }
    }
    *str = '\0';        // 字符串结尾字符:'\0'
    return str-buf;     // 返回转换好的长度值
}

// zhangkai
int sprintf(char *buf, const char *fmt, ...){
    va_list args;       // va_list是一个字符指针类型
    int i;

    va_start(args, fmt);
    i=vsprintf(buf,fmt,args);
    va_end(args);
        
    return i;      
}

代码vsprintf.h

#ifndef _VSPRINTF_H_
#define _VSPRINTF_H_

PUBLIC int  sprintf(char *buf, const char *fmt, ...);

#endif

相关文章

  • 异常和模块

    异常 目标 了解异常 捕获异常 异常的else 异常finally 异常的传递 自定义异常 一. 了解异常 当检测...

  • python多线程

    异常基础知识 -异常简介: 运行时错误 -异常类: 异常数据 异常名称,异常数据,异常类型 -自定义异常 clas...

  • dart 异常

    dart中的异常 异常处理 抛出异常 异常捕获

  • Java基础之异常

    Java基础之异常 目录 异常简单介绍 ThrowableErrorException 异常分类 如何处理异常异常...

  • python核心编程-错误与异常

    本章主题:什么是异常Python中的异常探测和处理异常上下文管理引发异常断言标准异常创建异常相关模块 什么是异常 ...

  • motan(RPC)系统梳理知识点

    异常分类: 业务异常(bizException) 服务异常(serviceException) 框架异常(fram...

  • 异常

    Java异常体系 异常的分类 Java的异常分为两大类:Checked异常和Runtime异常(运行时异常)。所有...

  • 从零构架个人博客网站(二)-全局异常处理

    中间件的异常 全局异常中间件全局异常监听定义异常的返回结果定义常见的异常状态开发环境 异常查看 对于异常,我们可以...

  • Node.js异常处理

    Node.js异常分类: 变量异常 函数异常 调用异常 变量异常 未定义变量 未包含对象 变量类型错误 函数异常 ...

  • python 异常

    异常 目标 异常的概念 捕获异常 异常的传递 抛出异常 01. 异常的概念 程序在运行时,如果 Python 解释...

网友评论

      本文标题:【异常】

      本文链接:https://www.haomeiwen.com/subject/bwblwktx.html