Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2462684
  • 博文数量: 392
  • 博客积分: 7040
  • 博客等级: 少将
  • 技术积分: 4138
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-17 13:03
个人简介

范德萨发而为

文章分类

全部博文(392)

文章存档

2017年(5)

2016年(19)

2015年(34)

2014年(14)

2013年(47)

2012年(40)

2011年(51)

2010年(137)

2009年(45)

分类:

2009-12-14 18:46:51

一下是从skelix/06/中的代码总结出来的,网上很多资料讲的东西比较分散,这里从该代码的角度来总结的,一些不足的地方待以后添加:

skelix/06/init.c文件。
程序在load.s进入保护模式之后立即调用call init进入该函数,功能请看注释。
/* Skelix by Xiaoming Mo (xiaoming.mo@skelix.org)
 * Licence: GPLv2 */
#include
#include
#include
#include
#include
#include
#include
#include
#include

unsigned long long *idt = ((unsigned long long *)IDT_ADDR);
unsigned long long *gdt = ((unsigned long long *)GDT_ADDR);

static void
isr_entry(int index, unsigned long long offset) {
    unsigned long long idt_entry = 0x00008e0000000000ULL |
            ((unsigned long long)CODE_SEL<<16);
    idt_entry |= (offset<<32) & 0xffff000000000000ULL;
    idt_entry |= (offset) & 0xffff;
    idt[index] = idt_entry;
}

static void
idt_install(void) {
    unsigned int i = 0;
    struct DESCR {
        unsigned short length;
        unsigned long address;
    } __attribute__((packed)) idt_descr = {256*8-1, IDT_ADDR};
    for (i=0; i        isr_entry(i, (unsigned int)(isr[(i<<1)+1]));
    for (++i; i<256; ++i)
        isr_entry(i, (unsigned int)default_isr);
    __asm__ ("lidt    %0\n\t"::"m"(idt_descr));
}

static void
pic_install(void) {
    outb(0x11, 0x20);
    outb(0x11, 0xa0);
    outb(0x20, 0x21);    /* 这里注册始终中断处理和键盘中断处理函数,他们的中断向量号分别是
                          0x20, 0x21,调用do_timer()和do_kb()函数 */
    outb(0x28, 0xa1);
    outb(0x04, 0x21);
    outb(0x02, 0xa1);
    outb(0x01, 0x21);
    outb(0x01, 0xa1);
    outb(0xff, 0x21);
    outb(0xff, 0xa1);
}

extern void task1_run(void);
extern void task2_run(void);
/* task1和task2的堆栈, 为什么有两个还不清楚 */
static long task1_stack0[1024] = {0xf, };
static long task1_stack3[1024] = {0xf, };
static long task2_stack0[1024] = {0xf, };
static long task2_stack3[1024] = {0xf, };

static void
new_task(struct TASK_STRUCT *task, unsigned int eip,
                unsigned int stack0, unsigned int stack3) {
    memcpy(task, &TASK0, sizeof(struct TASK_STRUCT));
    task->tss.esp0 = stack0;
    task->tss.eip = eip;
    task->tss.eflags = 0x3202;
    task->tss.esp = stack3;

    task->priority = INITIAL_PRIO;

    task->state = TS_STOPPED;
    task->next = current->next;
    current->next = task;
    task->state = TS_RUNABLE;
}

void
do_task1(void) {
    __asm__ ("incb 0xb8000+160*24+2");
}

void
do_task2(void) {
    __asm__ ("incb 0xb8000+160*24+4");
}

void
init(void) {
    char wheel[] = {'\\', '|', '/', '-'};
    int i = 0;
    struct TASK_STRUCT task1;
    struct TASK_STRUCT task2;

    idt_install();
    pic_install();
    kb_install();
    timer_install(100);
    set_tss((unsigned long long)&TASK0.tss);
    set_ldt((unsigned long long)&TASK0.ldt);
    __asm__ ("ltrw    %%ax\n\t"::"a"(TSS_SEL));
    __asm__ ("lldt    %%ax\n\t"::"a"(LDT_SEL)); /* 重要,load LDT_SEL to LDT register,LDT_SEL值是0x20,是一个GDT中的描述符的索引值,从这里可以看出LDT寄存器中的值的作用,是存储GDT中描述符的索引值的 */
    sti();   /* 开中断,这样时钟和键盘中断可以被执行 */

    /* 将taks1和task2加入到task链表中,这样在执行do_timer中的skeduler()函数是就会调度到
       这两个函数 */
    new_task(&task1,
            (unsigned int)task1_run,
            (unsigned int)task1_stack0+sizeof task1_stack0,
            (unsigned int)task1_stack3+sizeof task1_stack3);
    new_task(&task2,
            (unsigned int)task2_run,
            (unsigned int)task2_stack0+sizeof task2_stack0,
            (unsigned int)task2_stack3+sizeof task2_stack3);
//    __asm__ ("lop:\tjmp lop\n\t");

    /* it works fine if the fllowing __asm__ is commented, what function it
       does?  这里的汇编去掉之后程序能正常运行,将一些数据压入堆栈之后又用iret中断返回,
       重新获得压入堆栈的值,是什么意思呢?   */
    __asm__ ("movl %%esp,%%eax\n\t" \
             "pushl %%ecx\n\t" \
             "pushl %%eax\n\t" \
             "pushfl\n\t" \     
             "pushl %%ebx\n\t" \
             "pushl $1f\n\t" \   
             "iret\n" \
             "1:\tmovw %%cx,%%ds\n\t" \
             "movw %%cx,%%es\n\t" \
             "movw %%cx,%%fs\n\t" \
             "movw %%cx,%%gs" \
             ::"b"(USER_CODE_SEL),"c"(USER_DATA_SEL));
    for (;;) {
        __asm__ ("movb    %%al,    0xb8000+160*24"::"a"(wheel[i]));
        if (i == sizeof wheel)
            i = 0;
        else
            ++i;
    }
}

所以LDT寄存器中存储的是一个索引,用于在GDT表中查找描述符。
每个struct task中有unsigned long long ldt[2],存储属于该任务的ldt描述符。这里面的两个描述符的索引是由USER_CODE_SEL(0x07)和USER_DATA_SEL(0x0f)来索引,低三位是DPL等级别,第四为以上才是索引值,所以0x70索引ldt[0],0x0f索引ldt[2]。
至于USER_CODE_SEL和USER_DATA_SEL是怎样索引到ldt的有待研究。
阅读(3170) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~