第五天完成了文字显示与GDT/IDT初始化。
字符是以8*16的长方形点阵实现的,所以一个字符有16B。16个十六进制数便表示出这一个字符。
字库由hankaku.txt提供。
GDT(global (segment) descriptor table)全局段号记录表:
为了解决内存使用范围重叠等问题,可以将内存分为许多块(block),每一块起始地址都看做0来处理。
为了表示一个段,需要有以下信息:
段的大小;
段的起始地址;
段的管理属性;
CPU用8B来表示这些信息,然而用于指定段的寄存器(GDTR)只有16位。因此这里再次用了索引的方法。由于CPU设计的原因,段寄存器的低3位不能使用,因此能够使用的段号只有13位,从0-8191。所对应的内容共有8192B(64KB),这64KB的数据就是GDT。
选择器构成简图:
索引号(13位)|T1(1位)|RPL(2位)
IDT(interrupt descriptor table)中断记录表:和GDT原理差不多。
----------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------
struct SEGMENT_DESCRIPTOR {
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};
struct GATE_DESCRIPTOR {
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};
以上两个结构体分别子路了GDT与IDT的内容。
limit_low
段限长的0~15bit
base_low
段基址的0~15bit
base_mid 段基址的16~23bit access_right(0~3bit TYPE字段, 4bit S字段 5~6 DPL字段 7bit P字段)
limit_high 段限长的16~19bit+AVL+D/B+G域 base_high 段基址的24~31bit
base_mid 段基址的16~23bit access_right(0~3bit TYPE字段, 4bit S字段 5~6 DPL字段 7bit P字段)
limit_high 段限长的16~19bit+AVL+D/B+G域 base_high 段基址的24~31bit
offset_low 过程偏移地址的低16bit selector 段选择子
dw_count 参数个数, 只是调用门中有效
access_right 0~3bit:TYPE字段, 4bit:S字段, 5~6bit:DPL字段, 7bit:P字段
offset_high 过程偏移地址的高16bit
dw_count 参数个数, 只是调用门中有效
access_right 0~3bit:TYPE字段, 4bit:S字段, 5~6bit:DPL字段, 7bit:P字段
offset_high 过程偏移地址的高16bit
void init_gdtidt(void)
{
/* GDT的位置紧随在IDT之后 */
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) 0x0026f800;
int i;
/* GDT初始化 */
for (i = 0; i < 8192; i++) {
set_segmdesc(gdt + i, 0, 0, 0);
}
/* 编号为0的描述符是空描述符 是Intel要求必须这样设置的 */
set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092); /* 内核数据段
32位可读写数据段描述符 段限长4G-1 段基址0 DPL = 0*/
set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a); /* 内核代码段
32位可读可执行代码段描述符 段限长512KB 段基址0x280000 DPL = 0*/
load_gdtr(0xffff, 0x00270000); /* 加载GDT */
/* IDT初始化 */
for (i = 0; i < 256; i++) {
set_gatedesc(idt + i, 0, 0, 0);
}
load_idtr(0x7ff, 0x0026f800); /* 加载IDT */
return;
}
/* 设置段描述符 */
/*
sd 段描述符结构指针 limit 段限长 base 段基址 ar 段描述符属性
*/
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
if (limit > 0xfffff) { /* 如果限长大于1M (20bit的段限长若以字节为单位最多只能表示1M的内存)*/
ar |= 0x8000; /* G_bit = 1 */ /* 描述符中有一个G位, 若G = 1则表示段限长是以4KB为单位
若G = 0 则表示段限长是以字节为单位的 */
limit /= 0x1000; /* 调整段限长的值即除以4K */
}
/* 将各值填入相应的域中 可参考上面给出的段描述符图 */
sd->limit_low = limit & 0xffff;
sd->base_low = base & 0xffff;
sd->base_mid = (base >> 16) & 0xff;
sd->access_right = ar & 0xff;
sd->limit_high = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
sd->base_high = (base >> 24) & 0xff;
return;
}
/* 设置门描述符 */
/*
gd 门描述符结构指针 offset 过程入口偏移
selector 段选择子 ar 门描述符属性
*/
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->offset_low = offset & 0xffff;
gd->selector = selector;
gd->dw_count = (ar >> 8) & 0xff;
gd->access_right = ar & 0xff;
gd->offset_high = (offset >> 16) & 0xffff;
return;
}
将0x00270000-0x0027ff00设为GDT是随便设的。
感谢宅的中文注释,这些东西日后再深入理解。