--------------------------------------------------------
用 C 重新设置 GDT
--------------------------------------------------------
上节我们 jmp 到了 kernel ,那是用汇编写的 kernel ,只是编译成了 elf 格式的目标文件,为什么要将一个汇编 kernel 编译成 elf 呢?因为我们打算汇编和 C 一起结合编译,而这种编译方法最终生成的就是 elf 格式的目标文件!这节我们来用一用 C 语言,C 语言和汇编语言能相互调用是一件特别爽的事情...
一,重新设置 GDT 表的位置
本来 GDT 在 loader.bin 中,因为跳转到保护模式需要用到 GDT,所以临时设置到了 loader.bin 中,但那仅仅是个 loader,因此我们来将 GDT ‘正名’到 C 内核中!——GDT 的内容还是一样的,只是位置挪到了 kernel 中!
; ----------------------------
; <kernel.asm>
; Jack Zheng 11.27
; ----------------------------
SELECTOR_KERNEL_CS equ 8
; ----
; in C file
; ----
extern cstart
extern gdt_ptr
[SECTION .bss]
StackSpace resb 2 * 1024
StackTop:
[section .text]
global _start
_start:
; ----
; ss regidter is already set orign 0
; ----
mov esp, StackTop
sgdt [gdt_ptr]
call cstart
lgdt [gdt_ptr]
jmp SELECTOR_KERNEL_CS:csinit
csinit:
mov ah, 0fh
mov al, 'N'
mov [gs:(80*2*17)], ax
jmp $
// ----------------------------
// <protect.h>
// Jack Zheng 11.27
// ----------------------------
#ifndef _TINIX_PROTECT_H_
#define _TINIX_PROTECT_H_
typedef struct s_descriptor
{
t_16 limit_low;
t_16 base_low;
t_8 base_mid;
t_8 attr1;
t_8 limit_high_attr2;
t_8 base_high;
}DESCRIPTOR;
#endif
// ----------------------------
// <type.h>
// Jack Zheng 11.27
// ----------------------------
#ifndef _TINIX_TYPE_H_
#define _TINIX_TYPE_H_
typedef unsigned int t_32;
typedef unsigned short t_16;
typedef unsigned char t_8;
#endif
// ----------------------------
// <const.h>
// Jack Zheng 11.27
// ----------------------------
#ifndef _TINIX_CONST_H_
#define _TINIX_CONST_H_
#define PUBLIC
#define PRIVATE static
#define GDT_SIZE 128
#endif
// ----------------------------
// <start.c>
// Jack Zheng 11.27
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
// asm func
PUBLIC void * memcpy(void * pDst, void * pSrc, int iSize);
PUBLIC t_8 gdt_ptr[6];
PUBLIC DESCRIPTOR gdt[GDT_SIZE];
PUBLIC void cstart()
{
// copy old gdt to new gdt
memcpy(&gdt, (void *)(*(t_32 *)(&gdt_ptr[2])), *((t_16 *)(&gdt_ptr[0])));
// prepare to assign new gdt info to gdt register!
t_16 * p_gdt_limit = (t_16 *)(&gdt_ptr[0]);
t_32 * p_gdt_base = (t_32 *)(&gdt_ptr[2]);
*p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR);
*p_gdt_base = (t_32)&gdt;
}
; ----------------------------
; <string.asm>
; Jack Zheng 11.27
; ----------------------------
[SECTION .text]
global memcpy
; ----------------------------
; 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 结束 ----------------------------
可以看到我们的新 GDT 设置成功了,并且最后打印了N !
二,添加一个打印函数 disp_str
; ----------------------------
; <klib.asm>
; Jack Zheng 11.27
; ----------------------------
[SECTION .data]
disp_pos dd 0
[SECTION .text]
global disp_str
; ----------------------------
; void disp_str(char * info);
; ----------------------------
disp_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
// ----------------------------
// <start.c>
// Jack Zheng 11.27
// ----------------------------
#include "type.h"
#include "const.h"
#include "protect.h"
// asm func
PUBLIC void * memcpy(void * pDst, void * pSrc, int iSize);
PUBLIC void disp_str(char * pszInfo);
PUBLIC t_8 gdt_ptr[6];
PUBLIC DESCRIPTOR gdt[GDT_SIZE];
PUBLIC void cstart()
{
disp_str("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n-----\"cstart\" begins-----\n");
// copy old gdt to new gdt
memcpy(&gdt, (void *)(*(t_32 *)(&gdt_ptr[2])), *((t_16 *)(&gdt_ptr[0])));
// prepare to assign new gdt info to gdt register!
t_16 * p_gdt_limit = (t_16 *)(&gdt_ptr[0]);
t_32 * p_gdt_base = (t_32 *)(&gdt_ptr[2]);
*p_gdt_limit = GDT_SIZE * sizeof(DESCRIPTOR);
*p_gdt_base = (t_32)&gdt;
}
disp_str successful!!!
OK ,到现在我们算是正式进入内核开发的主要阶段了,但是在大展身手之前,我们先要组织一下我们的文件,因为它看起来太乱了...