#define YCORG 0x90000 - 0x2000
#include "ycio.cpp"
static_assert(YCORG == DATA_POS - ychead_SIZE, "YCORG Error!");
char kernel_stack[1024 * 2];
asm void main()
{
mov ax, KERNEL_DS
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
mov esp, &kernel_stack + sizeof(kernel_stack) //设置堆栈
cli
cld
call head_init
jmp KERNEL_POS //跳入ycker.cpp的main()函数
}
void head_init()
{
init_8259A();
printk(0,80*1,0x17,"Initializing 8259A 8253 ...... ychead.cpp");
init_8253();
printk(0,80*2,0x5f,"Setting memory page ...... ychead.cpp");
page_init();
printk(1,80*3,0x2f,"Loading: ycker.cpp,ycfs.cpp,ycmm.cpp ... ychead.cpp");
YCEXE *pKer = (YCEXE*)ycker_POS; //ycboot.cpp把ycker.cpp读到此处
YCEXE *pFs = (YCEXE*)(ycker_POS + pKer->length); //ycfs.cpp代码
YCEXE *pMm = (YCEXE*)(ycker_POS + pKer->length + pFs->length);//ycmm.cpp代码
code_copy((byte*)KERNEL_POS, pKer); //将ycker.cpp重定位拷贝到KERNEL_POS处
code_copy((byte*)(KERNEL_POS + pKer->codelen), pFs); //拷贝ycfs.cpp
code_copy((byte*)(KERNEL_POS + pKer->codelen + pFs->codelen), pMm); //ycmm.cpp
*(int*)(KERNEL_POS+DATA_POS+0) = pKer->codelen; //ycfs.cpp的代码位置
*(int*)(KERNEL_POS+DATA_POS+4) = pKer->codelen+pFs->codelen;//ycmm.cpp位置
printk(1,80*4,0x3f,"Kernel has been loaded! - ychead.cpp");
}
void init_8259A()
{
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x4);
outb(0xA1, 0x2);
outb(0x21, 0x1);
outb(0xA1, 0x1);
outb(0x21, 0xFF);
outb(0xA1, 0xFF);
}
void init_8253()
{
outb(0x43, 0x34);
outb(0x40, 1193182/100);
outb(0x40, (1193182/100) >> 8);
}
void page_init()
{
e820map *pMem = (e820map*)(DATA_POS + e820_POS);
e820map::e820entry *pRam = pMem->map; //系统内存信息由ycboot通过int 15h获得
int ii,maxPos;
for(maxPos=ii=0; ii<pMem->nr_map; ii++,pRam++) //计算可用内存最大位置maxPos
{
if(pRam->type != 1) continue;
if(maxPos < pRam->addr + pRam->size) maxPos = pRam->addr + pRam->size;
}
unsigned int PDE_num = (maxPos + DIR_SIZE - 1) / DIR_SIZE; //计算页目录项数
unsigned int PGT_endpos = PageTable_pos + PDE_num * PAGE_SIZE; //页表位置尾
pRam = pMem->map; //内存信息对象指针
unsigned int phys_pos = 0; //物理内存地址计数
unsigned int *pgDir = (unsigned int*)PageDir_POS; //页目录首地址
unsigned int *pgTable = (unsigned int*)PageTable_pos; //页表首地址
YMEM *maxHead = (YMEM*)freeMemLINK_POS; //空闲内存链表首地址
YMEM *phsHead = nullptr; //空闲内存链表头
YMEM *pLink;
#define PAGE_PRESENT 0x001
#define PAGE_RW 0x002
#define PAGE_USER 0x004
int memflag,memAttr;
for(memflag=ii=0; ii<PDE_num; ii++)
{
for(int jj=0; jj<1024; jj++)
{
while(pRam->type!=1 || phys_pos>=pRam->addr + pRam->size) pRam++;
if(phys_pos >= pRam->addr) //内存可用
{
memAttr = PAGE_PRESENT | PAGE_RW | PAGE_USER; //系统使用内存尾
if(phys_pos < ycker_SIZE || (phys_pos>=freeMemLINK_POS &&
phys_pos<PGT_endpos)) goto sl_p_000;
if(memflag == 0)
{
pLink = maxHead++; //空闲内存链表指针
pLink->addr = phys_pos; //设置空闲内存链表项代表的内存地址
yc_add_link(pLink,phsHead); //空闲内存链表增加一项
memflag = 1;
}
}
else //内存不可用
{
memAttr = PAGE_PRESENT;
sl_p_000:
if(memflag) //当memflag!=0时, 结束空闲内存计数
{
pLink->size = phys_pos - pLink->addr; //空闲链表内存大小
memflag = 0;
}
}
pgTable[jj] = phys_pos | memAttr; //页表项的内存物理地址和内存属性
phys_pos += PAGE_SIZE; //物理内存地址计数增加 1000h 字节
}
pgDir[ii] = (unsigned int)pgTable + (PAGE_PRESENT | PAGE_RW | PAGE_USER);
if(KERNEL_POS / DIR_SIZE + ii < 1024)
pgDir[KERNEL_POS / DIR_SIZE + ii] = pgDir[ii];
pgTable += 1024;
}
if(memflag) pLink->size = phys_pos - pLink->addr;
*(YMEM**)(DATA_POS + 8) = phsHead; //空闲内存链表表头
*(YMEM**)(DATA_POS + 12) = maxHead; //可用内存链表最大地址
asm
{
mov eax, PageDir_POS //页目录位置
mov cr3, eax //设置页目录寄存器 cr3
mov eax, cr0 //读页目录状态寄存器 cr0
or eax, 80000000h //内存分页状态位
mov cr0, eax //内存分页功能被启动
}
}
C/C++代码文件: ychead.cpp
源代码分析
语句#define YCORG 0x90000 - 0x2000设置执行代码在内存中的位置
语句static_assert(…)检查YCORG的值是否正确
32位地址可访问4G字节内存,4G内存按下列方式被划分成1K个页表,1M个物理页框
2^32字节 = 2^10 * 2^10 * 2^12字节 = 1024 * 1024 * 4196字节
= 1024页表 * 1024页框/页表 * 4096字节/页框
执行语句:
mov eax, PageDir_POS
mov cr3, eax
……
后,
内存分页功能被启动,线性地址与物理地址可能不再相等,需进行转换。
如线性地址aa将按下列程序被转换为物理地址rr
int ii = aa >> 22; //aa的第22-31位,第ii个页目录
int jj = (aa >> 12) & 0x3ff; //aa的第12-21位,第jj个页表
unsigned int pgDir = (unsigned int)PageDir_POS; //寄存器cr3中的页目录地址
unsigned int pgTable = (unsigned int)(pgDir[ii] & ~0xfff); //页表地址
unsigned int rr = (pgTable[jj] & ~0xfff) + (aa & 0xfff); //物理地址
语句jmp KERNEL_POS 使程序跳入ycker.cpp的main()函数