首页 > 代码库 > MINIX3 保护模式分析

MINIX3 保护模式分析

3.1 INTEL 保护模式概要

先要说明一个问题:不是 MINIX3 一定要设置这个保护模式,但是在 386 平台上, 引入了这个保护模式机制,MINIX3 不得不设立相关保护模式的内容。由于 386 平台的保护模式是一个极其复杂的过程,建议读者参考相关文献如 INTEL 编程 手册卷 3A,我在这里做一个简要的介绍:

首先理解什么是保护模式,说道保护模式,我们马上应该想到实模式,何谓实模
式呢?实模式是 386 一个历史包袱,在当年开发 8088、8086 时,为了能够在 16
位的寄存器的情况下,能够达到 1M 的寻址空间,我们采用了一个非常重要的思
想:段的思想,用 CS:IP 找出下一个指令在哪里!本来我个人认为既然在 386 平
台寄存器已经有 32 位,就没有必要在引入这个段思想,但是 386 为保持以前的
兼容性和市场占有率的考虑,它兼容了以前,只是将其扩展,越扩越复杂,把整
个架构扩张的非常复杂的局面。保护模式的含义就是能够达到 4G 的寻址空间而
设立的一种寻址方法。对于 386 而言,一般把 A20 打开以后,整个保护模式就
打开,我们知道,机器打开后,在很段一段时间内,BIOS 加载了引导区,引导
区可能会直接加载内核,或者加载一个更大的引导区,来加载内核,一般对用现
在通用的操作系统而言,在加载内核之前,就会把 A20 打开,因为很可能内核
不止 1M 这么大。

现在我们看下保护模式下的寻址方式;

CS 我们现在认为是一个段选择器,它是选择一个段,同时有另外一个寄存器 GDT/LDT,这  2  个寄存器是指向 GDT/LDT 描述符的首地址,CS 选择的就是 GDT/LDT 数组的哪个描述符。描述符一般格式如下图:

 

wps5691.tmp

之后通过 EIP 和 BASE 的逻辑加就是所要的目标地址,当然在这里我们没有考虑分页机制, MINIX3 为了简单起见并没有开启分页机制,所以我在这里就不会来说明这个问题。
之后保护模式设立了一个等级特权,分别是 0 1 2 3.0 1 2 是内核态,3 是应用态。如果发出
等级特权不一致时,又会引起另外一些堆栈问题。同时也涉及另外一些门和寄存器。
首先看门,涉及到特权变化时,一般会触发一个门,MINIX3 用的是调用门来解决这个问题。 门描述符入下面所示,

IR 指向的中断门,事实上还用陷入门,同时还涉及到 TS 寄存器和 TSS 结构,这些内容都 忽略掉,建议读相关文献。源码涉及到,我就予以适当的分析。

MINIX3 保护模式源码分析

现在我们还是直接进入源码吧。事实上,源码比较少,主要包括 protect.h 和 protect.c。现在 看看头文件

/* Constants for protected mode. */

/* Table sizes. */这个是全局描述表的大小

#define GDT_SIZE (FIRST_LDT_INDEX + NR_TASKS + NR_PROCS)

/* spec. and LDT‘s */

#define IDT_SIZE (IRQ8_VECTOR + 8) /* only up to the highest vector */中断门描述符大小

#define LDT_SIZE (2 + NR_REMOTE_SEGS) /* CS, DS and remote segments */

//局部描述符大小

/* Fixed global descriptors. 1 to 7 are prescribed by the BIOS. */

//下面全局描述符的前 7 个描述符宏定义。后面看到 gdt[]数组的每一项就是对应下面每一项

#define GDT_INDEX 1  /* GDT descriptor *///全局描述符

#define IDT_INDEX 2 /* IDT descriptor *///局部描述符

#define DS_INDEX 3 /* kernel DS *///内核 DS 描述符地址

#define ES_INDEX 4 /* kernel ES (386: flag 4 Gb at startup) *///内核 ES 描述

//符地址

#define SS_INDEX 5 /* kernel SS (386: monitor SS at startup) */

//内核 SS 描述符地址

41

#define CS_INDEX 6 /* kernel CS */

//内核 CS 描述符地址

#define MON_CS_INDEX 7 /* temp for BIOS (386: monitor CS at startup) */

//是 MONITOR 监控器描述符地址,这个监控器主要存放机器的硬件相关信息

#define TSS_INDEX 8 /* kernel TSS */

//这个主要存放 TSS 描述符地址,我们认为,整个内核公用一个 TSS

#define DS_286_INDEX 9 /* scratch 16-bit source segment */

//这个描述符不在 386 范围之内,不考虑

#define ES_286_INDEX 10

//同样不考虑

#define A_INDEX 11

#define B_INDEX 12

#define C_INDEX 13

#define D_INDEX 14

//第一个进程的 LDT 描述符地址

/* scratch 16-bit destination segment */

/* 64K memory segment at A0000 */
/* 64K memory segment at B0000 */
/* 64K memory segment at C0000 */
/* 64K memory segment at D0000 */

#define FIRST_LDT_INDEX 15  /* rest of descriptors are LDT‘s */

//下面是选择子宏定义分别是 GDT,IDT,DS,ES 等等选择子

#define GDT_SELECTOR

#define IDT_SELECTOR
#define DS_SELECTOR
#define ES_SELECTOR

#define FLAT_DS_SELECTOR
#define SS_SELECTOR
#define CS_SELECTOR
#define MON_CS_SELECTOR
#define TSS_SELECTOR
#define DS_286_SELECTOR
*/

#define ES_286_SELECTOR
*/

/* Fixed local descriptors. */
#define CS_LDT_INDEX
#define DS_LDT_INDEX

#define EXTRA_LDT_INDEX

/* Privileges. */

0x08 /* (GDT_INDEX * DESC_SIZE) bad for asld */

0x10  /* (IDT_INDEX * DESC_SIZE) */

0x18  /* (DS_INDEX * DESC_SIZE) */

0x20  /* (ES_INDEX * DESC_SIZE) */

0x21 /* less privileged ES */

0x28  /* (SS_INDEX * DESC_SIZE) */

0x30  /* (CS_INDEX * DESC_SIZE) */

0x38 /* (MON_CS_INDEX * DESC_SIZE) */

0x40  /* (TSS_INDEX * DESC_SIZE) */

0x49  /* (DS_286_INDEX*DESC_SIZE+TASK_PRIVILEGE)

0x51  /* (ES_286_INDEX*DESC_SIZE+TASK_PRIVILEGE)

0  /* process CS */

1 /* process DS=ES=FS=GS=SS */

2 /* first of the extra LDT entries */

//这个特权对应,MINIX3 采用了 3 个级别,分别是 0 1 3 0 主要用于内核和中断处理  1 主

//要用于内核任务  3 主要用于服务器和用户进程

#define INTR_PRIVILEGE 0 /* kernel and interrupt handlers */

#define TASK_PRIVILEGE 1  /* kernel tasks */

#define USER_PRIVILEGE 3  /* servers and user processes */

/* 286 hardware constants. */

42

/* Exception vector numbers. */

//下面是异常向量的宏定义。由于 386 对于异常是保持一个已经建立好的顺序,MINIX3 只

//处理以下几个异常

#define BOUNDS_VECTOR 5 /* bounds check failed */

#define INVAL_OP_VECTOR 6/* invalid opcode */

#define COPROC_NOT_VECTOR 7 /* coprocessor not available */

#define DOUBLE_FAULT_VECTOR 8

#define COPROC_SEG_VECTOR 9 /* coprocessor segment overrun */

#define INVAL_TSS_VECTOR 10 /* invalid TSS */

#define SEG_NOT_VECTOR 11 /* segment not present */

#define STACK_FAULT_VECTOR 12 /* stack exception */

#define PROTECTION_VECTOR 13 /* general protection */

/* Selector bits. */

#define TI 0x04 /* table indicator */

#define RPL 0x03 /* requester privilege level */

/* Descriptor structure offsets. */

//由于 386 描述符非常诡异,在这里涉及一些宏来定义描述符的部分字节

#define DESC_BASE 2 /* to base_low */

#define DESC_BASE_MIDDLE 4 /* to base_middle */

#define DESC_ACCESS 5  /* to access byte */

#define DESC_SIZE 8 /* sizeof (struct segdesc_s) */

/* Base and limit sizes and shifts. */

#define BASE_MIDDLE_SHIFT 16 /* shift for base --> base_middle */

/* Access-byte and type-byte bits. */

//获取权限的宏定义

#define PRESENT 0x80 /* set for descriptor present */

#define DPL 0x60 /* descriptor privilege level mask */

#define DPL_SHIFT 5

#define SEGMENT 0x10  /* set for segment-type descriptors */

/* Access-byte bits. */

#define EXECUTABLE 0x08 /* set for executable segment */

#define CONFORMING 0x04/* set for conforming segment if executable */

#define EXPAND_DOWN 0x04 /* set for expand-down segment if !executable*/

#define READABLE 0x02 /* set for readable segment if executable */

#define WRITEABLE 0x02  /* set for writeable segment if !executable */

#define TSS_BUSY 0x02 /* set if TSS descriptor is busy */

#define ACCESSED 0x01 /* set if segment accessed */

43

/* Special descriptor types. */

//一些特殊的描述符类型,这里主要是 LDT 其他都不是主流描述符。涉及一般是模拟 286 //以下平台程序

#define AVL_286_TSS

#define LDT

#define BUSY_286_TSS #define CALL_286_GATE #define TASK_GATE

#define INT_286_GATE
#define TRAP_286_GATE

1 /* available 286 TSS */

2 /* local descriptor table */

3 /* set transparently to the software */

4  /* not used */

5 /* only used by debugger */

6 /* interrupt gate, used for all vectors */

7 /* not used */

/* Extra 386 hardware constants. */

/* Exception vector numbers. */

#define PAGE_FAULT_VECTOR

#define COPROC_ERR_VECTOR

/* Descriptor structure offsets. */ #define DESC_GRANULARITY #define DESC_BASE_HIGH

/* Base and limit sizes and shifts. */ #define BASE_HIGH_SHIFT

14

16 /* coprocessor error */

6 /* to granularity byte */

7 /* to base_high */

24  /* shift for base --> base_high */

#define BYTE_GRAN_MAX 0xFFFFFL /* maximum size for byte granular segment */

#define GRANULARITY_SHIFT 16 /* shift for limit --> granularity */

#define OFFSET_HIGH_SHIFT 16 /* shift for (gate) offset --> offset_high */

#define PAGE_GRAN_SHIFT 12 /* extra shift for page granular limits */

/* Type-byte bits. */

#define DESC_386_BIT 0x08 /* 386 types are obtained by ORing with this */

/* LDT‘s and TASK_GATE‘s don‘t need it */

/* Granularity byte. */

#define GRANULAR 0x80 /* set for 4K granularilty */

#define DEFAULT 0x40 /* set for 32-bit defaults (executable seg) */

#define BIG 0x40 /* set for "BIG" (expand-down seg) */

#define AVL 0x10 /* 0 for available */

#define LIMIT_HIGH 0x0F /* mask for high bits of limit */

现在我们深入源码分析: 源码是 protect.c

现在暂且看下 INTEL386 架构下,minix3 怎么使用这个机制:

MINIX3 会构造一个全局描述符数组,里面放的具体内容可以参考前面的内容。这个数组主 要是用存放一些全局描述符,比如 TSS 描述符段,以及 IDT 描述段。
现在看看图示:

44

wps56B1.tmp

gate_talbe[]是一个已经定义好的数组,主要用复制到 idt[]数组中。

/* This file contains code for initialization of protected mode, to initialize * code and data segment descriptors, and to initialize global descriptors * for local descriptors in the process table.

*/

//这个文件主要用于保护模式的初始化,其实在 boot 之后,保护模式已经开启,当然那里的 操作时暂时的初始化,

//这里的初始化主要是为了 MINIX 而进行的

//例如初始化代码段描述符,全局段描述符等等

//这个文件主要是针对于 Intel 公司的 386 平台所使用 #include "kernel.h"

#include "proc.h"

#include "protect.h"

#if _WORD_SIZE == 4

#define INT_GATE_TYPE (INT_286_GATE | DESC_386_BIT)

#define TSS_TYPE (AVL_286_TSS | DESC_386_BIT)

#else

#define INT_GATE_TYPE   INT_286_GATE
#define TSS_TYPE AVL_286_TSS
#endif

//为了保持兼容 286 平台,上面做了一些的宏定义,在此略去 struct desctableptr_s {

char limit[sizeof(u16_t)];

char base[sizeof(u32_t)]; /* really u24_t + pad for 286 */

};

//上面这个字段是 386 平台的描述符定义
// 门描述符

struct gatedesc_s {
u16_t offset_low;

45

u16_t selector;

u8_t pad; /* |000|XXXXX| ig & trpg, |XXXXXXXX| task g */

u8_t p_dpl_type; /* |P|DL|0|TYPE| */

u16_t offset_high;

};

//tss 结构的段描述符

struct tss_s {

reg_t backlink;

reg_t sp0; /* stack pointer to use during interrupt */

reg_t ss0; /* " segment " " " " */

reg_t sp1;
reg_t ss1;
reg_t sp2;
reg_t ss2;

#if _WORD_SIZE == 4
reg_t cr3;

#endif

reg_t ip;

reg_t flags;
reg_t ax;
reg_t cx;
reg_t dx;
reg_t bx;
reg_t sp;
reg_t bp;
reg_t si;
reg_t di;
reg_t es;
reg_t cs;
reg_t ss;
reg_t ds;

#if _WORD_SIZE == 4
reg_t fs;

reg_t gs;
#endif

reg_t ldt;

#if _WORD_SIZE == 4
u16_t trap;

u16_t iobase;

/* u8_t iomap[0]; */
#endif

};

PUBLIC struct segdesc_s gdt[GDT_SIZE];  /* used in klib.s and mpx.s */

46

// gdt 是个非常重要的数组,因为在后面的保护模式的设定中已经涉及到,这个数组主要用 于存储全局段描述符,可以参考前面的头文件来看这个。

// idt 在这里定义成私有的数据段,这点非常值得一提!这是中断门的描述符 PRIVATE struct gatedesc_s idt[IDT_SIZE];  /* zero-init so none present */

PUBLIC struct tss_s tss; /* zero init */

//这里 tss_s 也是作为公共的变量而存在,主要是储存内核 TSS

FORWARD _PROTOTYPE( void int_gate, (unsigned vec_nr, vir_bytes offset,
unsigned dpl_type) );

FORWARD _PROTOTYPE( void sdesc, (struct segdesc_s *segdp, phys_bytes base,
vir_bytes size) );

/*==================================================================== =======*

* prot_init  这个函数是被 CSTART 文件调用 *

*===================================================================== ======*/

PUBLIC void prot_init()
{

/* Set up tables for protected mode.

* All GDT slots are allocated at compile time.
*/

struct gate_table_s *gtp;
struct desctableptr_s *dtp;
unsigned ldt_index;
register struct proc *rp;

static struct gate_table_s {

_PROTOTYPE( void (*gate), (void) );
unsigned char vec_nr;

unsigned char privilege;

}

//中断门 包括异常和中断的向量地址,在这里就是作为常量来设置,注意这里除了系统 调用以外,都是 INTR_PRIVLEGE 特权,就是内核最高特权,只有系统调用是用户特权
gate_table[] = {

{ divide_error, DIVIDE_VECTOR, INTR_PRIVILEGE },

{ single_step_exception, DEBUG_VECTOR, INTR_PRIVILEGE }, { nmi, NMI_VECTOR, INTR_PRIVILEGE },

{ breakpoint_exception, BREAKPOINT_VECTOR, USER_PRIVILEGE },
{ overflow, OVERFLOW_VECTOR, USER_PRIVILEGE },
{ bounds_check, BOUNDS_VECTOR, INTR_PRIVILEGE },
{ inval_opcode, INVAL_OP_VECTOR, INTR_PRIVILEGE },
{ copr_not_available, COPROC_NOT_VECTOR, INTR_PRIVILEGE },
{ double_fault, DOUBLE_FAULT_VECTOR, INTR_PRIVILEGE },

47

{ copr_seg_overrun, COPROC_SEG_VECTOR, INTR_PRIVILEGE },
{ inval_tss, INVAL_TSS_VECTOR, INTR_PRIVILEGE },
{ segment_not_present, SEG_NOT_VECTOR, INTR_PRIVILEGE },
{ stack_exception, STACK_FAULT_VECTOR, INTR_PRIVILEGE },
{ general_protection, PROTECTION_VECTOR, INTR_PRIVILEGE },
#if _WORD_SIZE == 4

{ page_fault, PAGE_FAULT_VECTOR, INTR_PRIVILEGE },
{ copr_error, COPROC_ERR_VECTOR, INTR_PRIVILEGE },
#endif

{ hwint00, VECTOR( 0), INTR_PRIVILEGE },
{ hwint01, VECTOR( 1), INTR_PRIVILEGE },
{ hwint02, VECTOR( 2), INTR_PRIVILEGE },

{ hwint03, VECTOR( 3), INTR_PRIVILEGE },
{ hwint04, VECTOR( 4), INTR_PRIVILEGE },
{ hwint05, VECTOR( 5), INTR_PRIVILEGE },

{ hwint06, VECTOR( 6), INTR_PRIVILEGE },
{ hwint07, VECTOR( 7), INTR_PRIVILEGE },
{ hwint08, VECTOR( 8), INTR_PRIVILEGE },

{ hwint09, VECTOR( 9), INTR_PRIVILEGE },
{ hwint10, VECTOR(10), INTR_PRIVILEGE },
{ hwint11, VECTOR(11), INTR_PRIVILEGE },

{ hwint12, VECTOR(12), INTR_PRIVILEGE },
{ hwint13, VECTOR(13), INTR_PRIVILEGE },
{ hwint14, VECTOR(14), INTR_PRIVILEGE },

{ hwint15, VECTOR(15), INTR_PRIVILEGE }, #if _WORD_SIZE == 2

{ p_s_call, SYS_VECTOR, USER_PRIVILEGE }, /* 286 system call */

#else

//这里是 386 以及其后的系统调用描述符

{ s_call, SYS386_VECTOR, USER_PRIVILEGE }, /* 386 system call */

#endif

{ level0_call, LEVEL0_VECTOR, TASK_PRIVILEGE },

};

/* Build gdt and idt pointers in GDT where the BIOS expects them. */

//首先在 GDT 描述符中构造 gdt 和 idt 描述符,也就是在 gdt[]数组中把 gdt 和 idt 描述符设 立好,将 dtp 所指向的各个属性都设置好,以供后面的操作使用

dtp= (struct desctableptr_s *) &gdt[GDT_INDEX];

* (u16_t *) dtp->limit = (sizeof gdt) - 1;//大小为 GDT 表大小 * (u32_t *) dtp->base = vir2phys

(gdt);

dtp= (struct desctableptr_s *) &gdt[IDT_INDEX];

* (u16_t *) dtp->limit = (sizeof idt) - 1;//大小为 IDT 表大小

48

* (u32_t *) dtp->base = vir2phys(idt);

/* Build segment descriptors for tasks and interrupt handlers. */ //以下一些代码主要给 gdt[]数组设立相关描述符
//给 CS_INDEX  也就是代码段设置段描述符,
init_codeseg(&gdt[CS_INDEX],

kinfo.code_base, kinfo.code_size, INTR_PRIVILEGE); //给 DS_INDEX 也就是数据段设置段描述符
init_dataseg(&gdt[DS_INDEX],

kinfo.data_base, kinfo.data_size, INTR_PRIVILEGE); //给 ES_INDEX 设置段描述符

init_dataseg(&gdt[ES_INDEX], 0L, 0, TASK_PRIVILEGE);

/* Build scratch descriptors for functions in klib88. */

init_dataseg(&gdt[DS_286_INDEX], 0L, 0, TASK_PRIVILEGE);
init_dataseg(&gdt[ES_286_INDEX], 0L, 0, TASK_PRIVILEGE);

/* Build local descriptors in GDT for LDT‘s in process table.

* The LDT‘s are allocated at compile time in the process table, and
* initialized whenever a process‘ map is initialized or changed.
*/

//这个操作主要是在 GDT 初始化局部描述符

for (rp = BEG_PROC_ADDR, ldt_index = FIRST_LDT_INDEX;
rp < END_PROC_ADDR; ++rp, ldt_index++) {

init_dataseg(&gdt[ldt_index], vir2phys(rp->p_ldt),

sizeof(rp->p_ldt), INTR_PRIVILEGE);

gdt[ldt_index].access = PRESENT | LDT;

rp->p_ldt_sel = ldt_index * DESC_SIZE;

}

/* Build main TSS.

* This is used only to record the stack pointer to be used after an * interrupt.

* The pointer is set up so that an interrupt automatically saves the * current process‘s registers ip:cs:f:sp:ss in the correct slots in the * process table.

*/

//接下来主要是构造 主 TSS,关于 TSS 的详细信息参考 Intel 相关文献

//记住这个主 TSS 的作用:仅仅是在经过一个中断之后,记录这个被使用的堆栈指针
//这个指针被设定用来一个中断自动储存当前进程的寄存器的值到当前槽
tss.ss0 = DS_SELECTOR;//将 tss 的 ss0 设置成为数据段选择器
//这里的 TSS 结构描述符设立在 GDT 相关位置。

init_dataseg(&gdt[TSS_INDEX], vir2phys(&tss), sizeof(tss), INTR_PRIVILEGE);

gdt[TSS_INDEX].access = PRESENT | (INTR_PRIVILEGE << DPL_SHIFT) | TSS_TYPE;

49

/* Build descriptors for interrupt gates in IDT. */ // 用于初始化也是建造这个中断门
for (gtp = &gate_table[0];

gtp < &gate_table[sizeof gate_table / sizeof gate_table[0]]; ++gtp) { int_gate(gtp->vec_nr, (vir_bytes) gtp->gate,

PRESENT | INT_GATE_TYPE | (gtp->privilege << DPL_SHIFT));

}

#if _WORD_SIZE == 4

/* Complete building of main TSS. */

tss.iobase = sizeof tss; /* empty i/o permissions map */ #endif

}

/*==================================================================== =======*

* init_codeseg 这个函数主要写好一个代码段描述符*

*===================================================================== ======*/

PUBLIC void init_codeseg(segdp, base, size, privilege) register struct segdesc_s *segdp;

phys_bytes base;

vir_bytes size;
int privilege;
{

/* Build descriptor for a code segment. */
sdesc(segdp, base, size);

segdp->access = (privilege << DPL_SHIFT)

| (PRESENT | SEGMENT | EXECUTABLE | READABLE); /* CONFORMING = 0, ACCESSED = 0 */

}

/*=============================================================== ===== =======*

* init_dataseg 写好一个数据段描述符  *

*===================================================================== ======*/

PUBLIC void init_dataseg(segdp, base, size, privilege) register struct segdesc_s *segdp;

phys_bytes base;

vir_bytes size;
int privilege;

50

{

/* Build descriptor for a data segment. */
sdesc(segdp, base, size);

segdp->access = (privilege << DPL_SHIFT) | (PRESENT | SEGMENT | WRITEABLE);
/* EXECUTABLE = 0, EXPAND_DOWN = 0, ACCESSED = 0 */
}

/*==================================================================== =======*

* sdesc 简历描述符的核心操作,

*是由于 386 的特性,导致了未来描述符的操作的设立非常的麻烦

*===================================================================== ======*/

PRIVATE void sdesc(segdp, base, size)
register struct segdesc_s *segdp;
phys_bytes base;

vir_bytes size;
{

/* Fill in the size fields (base, limit and granularity) of a descriptor. */
segdp->base_low = base;

segdp->base_middle = base >> BASE_MIDDLE_SHIFT; segdp->base_high = base >> BASE_HIGH_SHIFT;

#if _WORD_SIZE == 4

--size; /* convert to a limit, 0 size means 4G */

if (size > BYTE_GRAN_MAX) {

segdp->limit_low = size >> PAGE_GRAN_SHIFT;

segdp->granularity = GRANULAR | (size >>

(PAGE_GRAN_SHIFT + GRANULARITY_SHIFT));

} else {

segdp->limit_low = size;

segdp->granularity = size >> GRANULARITY_SHIFT;

}

segdp->granularity |= DEFAULT;

#else

segdp->limit_low = size - 1; #endif

}

/* means BIG for data seg */

/*==================================================================== =======*

* seg2phys *

51

*===================================================================== ======*/

PUBLIC phys_bytes seg2phys(seg)
U16_t seg;

{

/* Return the base address of a segment, with seg being either a 8086 segment * register, or a 286/386 segment selector.

*/

phys_bytes base;

struct segdesc_s *segdp;

if (! machine.protected) {

base = hclick_to_physb(seg);
} else {

segdp = &gdt[seg >> 3];

base = ((u32_t) segdp->base_low << 0)

| ((u32_t) segdp->base_middle << 16)

| ((u32_t) segdp->base_high << 24);

}

return base;

}

/*==================================================================== =======*

* phys2seg *

*===================================================================== ======*/

PUBLIC void phys2seg(seg, off, phys)
u16_t *seg;

vir_bytes *off;

phys_bytes phys;
{

/* Return a segment selector and offset that can be used to reach a physical

* address, for use by a driver doing memory I/O in the A0000 - DFFFF range.
*/

#if _WORD_SIZE == 2

if (! machine.protected) {

*seg = phys / HCLICK_SIZE;
*off = phys % HCLICK_SIZE;
} else {

unsigned bank = phys >> 16;

unsigned index = bank - 0xA + A_INDEX;

init_dataseg(&gdt[index], (phys_bytes) bank << 16, 0, TASK_PRIVILEGE);

52

*seg = (index * 0x08) | TASK_PRIVILEGE; *off = phys & 0xFFFF;

}

#else

*seg = FLAT_DS_SELECTOR;
*off = phys;

#endif

}

/*==================================================================== =======*

* int_gate *

这里初始化中断门描述符,事实上前面的门描述符已经涉及到中断门,但是这个门路就是一 个      专      门      用      于      处      理     中      断      的      门      路 。

*===================================================================== ======*/

PRIVATE void int_gate(vec_nr, offset, dpl_type) unsigned vec_nr;

vir_bytes offset;

unsigned dpl_type;
{

/* Build descriptor for an interrupt gate. */
register struct gatedesc_s *idp;

idp = &idt[vec_nr];

idp->offset_low = offset;

idp->selector = CS_SELECTOR;
idp->p_dpl_type = dpl_type;
#if _WORD_SIZE == 4

idp->offset_high = offset >> OFFSET_HIGH_SHIFT; #endif

}

/*==================================================================== =======*

* alloc_segments *

*===================================================================== ======*/

PUBLIC void alloc_segments(rp)
register struct proc *rp;

{

/* This is called at system initialization from main() and by do_newmap().
* The code has a separate function because of all hardware-dependencies.

53

* Note that IDLE is part of the kernel and gets TASK_PRIVILEGE here.
*/

//这个函数主要是做分配段的目的
phys_bytes code_bytes;

phys_bytes data_bytes;
int privilege;

//首先判断 CPU 芯片是不是保护模式,如果不是,也就没有分配段这个说法
if (machine.protected) {

//首先计算要分配多少数据字节数,计算的信息其实是由可执行文件的重定位信息提供,在 //MINIX3 中,支持.out 文件,.out 文件头有相关信息.

data_bytes = (phys_bytes) (rp->p_memmap[S].mem_vir +
rp->p_memmap[S].mem_len) << CLICK_SHIFT;
if (rp->p_memmap[T].mem_len == 0)

code_bytes = data_bytes; /* common I&D, poor protect */

else

//计算出进程 往往是用户进程代码段字节数。

code_bytes = (phys_bytes) rp->p_memmap[T].mem_len << CLICK_SHIFT; //特权是属于任务特权还是用户特权,主要是因为系统任务进程也需要段。
privilege = (iskernelp(rp)) ? TASK_PRIVILEGE : USER_PRIVILEGE;
//将段描述符的各个信息写入到相应应的 p_ldt[]数组中,这是一个局部描述符
init_codeseg(&rp->p_ldt[CS_LDT_INDEX],

(phys_bytes) rp->p_memmap[T].mem_phys << CLICK_SHIFT, code_bytes, privilege);

init_dataseg(&rp->p_ldt[DS_LDT_INDEX],

(phys_bytes) rp->p_memmap[D].mem_phys << CLICK_SHIFT, data_bytes, privilege);

rp->p_reg.cs = (CS_LDT_INDEX * DESC_SIZE) | TI | privilege; #if _WORD_SIZE == 4

rp->p_reg.gs =

rp->p_reg.fs =

#endif

rp->p_reg.ss =
rp->p_reg.es =

rp->p_reg.ds = (DS_LDT_INDEX*DESC_SIZE) | TI | privilege; } else {

rp->p_reg.cs = click_to_hclick(rp->p_memmap[T].mem_phys); rp->p_reg.ss =

rp->p_reg.es =

rp->p_reg.ds = click_to_hclick(rp->p_memmap[D].mem_phys);

}

}

MINIX3 保护模式分析