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 数组的哪个描述符。描述符一般格式如下图:

之后通过 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

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);

}

}

时间: 2024-11-08 23:24:58

MINIX3 保护模式分析的相关文章

MINIX3 导读分析

一个操作系统的分析是属于一个非常庞大的工程,操作系统就像是一个人造的 人,每一个模块想完全发挥功效,很有可能需要很多模块的支持才能够实现.所 以在分析 MINIX3 时,我认为同时看多个模块对于理解 MINIX3 是有好处的,特 别是因为 MINIX3 是采用微内核结构,也就造成阅读源码的一个比较大的障碍. 在此我统领的描述下 MINIX3 和 PM 部分内容,给读者 MINIX3 形成一个整体的 导读框架: 1 MINIX3 采用微内核结构,什么是微内核呢?MINIX3 设计者认为除了部分非

视频网站的盈利模式分析

一. 由于各种客观非客观因素,我国的视频网站和欧美发达国家的视频网站的盈利模式相比还是有很大差别的,接下来就简单的分析介绍一下欧美和国内视频网站盈利模式. 因为对国外的视频网站不是很了解,所以在这里只能简单地介绍一下国外视频网站不同于国内的盈利方式,欧美在版权这方面做得很完善,所以国外的视频网站可以通过购买某部(或某些)电影/剧集的网络播放权/网络首播权,之后通过收费收看这样的方式,为自己的网站的盈利渠道并提升浏览量,比如<越狱><冰与火之歌>等十分热门,但确只能在收费频道播放的剧

MINIX3 内核整体架构回顾及内核定 性分析

MINIX3  内核整体架构回顾及内核定 性分析 12.1 注意事项 由于本文档不对 I/O 文件系统做出分析,所以在此不对 MINIX3 整体做出一个分 析,本章主要是针对内核进程分析.并且这里的模型建立是非常理想化的. 12.2 MINIX3 架构 MINIX3 的设计理念就是设计一个比当前主流的系统更加稳定和可靠系统.从而 MINIX3 也就是提出一个非常经典的模式:就是系统服务器进程的概念.这些系 统服务器进程是外核的一部分,但是可以和内核通信.最为重要的设计理念是这 些服务器进程既然作

对Minix3中进程模型的简要分析

简单介绍 Minix(Mini UNIX)原来是荷兰阿姆斯特丹的Vrije大学计算机科学系的Andrew S. Tanenbaum教授所发展的一个类Unix操作系统. 目前的Minix版本为Minix 3,是一个免费.开源的操作系统,设计目标是实现高可靠性.灵活性及安全性. 其系统主要包括在核心模式下运作的微核心和在用户模式下作为一系列独立.受保护的进程运行的其余所有操作系统组件. Minix3的整体认识 MINIX3本身就是一组进程的集合 第一层的主要功能是为上层驱动程序和服务器提供一组特权内

多线程设计模式——Read-Write Lock模式和Future模式分析

目录 多线程程序评价标准 任何模式都有一个相同的"中心思想" Read-Write Lock 模式 RW-Lock模式特点 冲突总结 手搓RW Lock模式代码 类图 Data类 P.S. Before/After模式 ReadWriteLock类 正确运行结果 适用场合 "逻辑锁"vs"物理锁" 性能对比 "中心思想"分析 Future 模式 Future模式特点 手搓Future模式代码 类图 Main类 Host类 Fu

人工智能PK透明加密,数据安全保护谁能技高一筹

人工智能一直是全球的一大热点话题,从很久之前谷歌研发的人工智能机器人击败柯洁.李世石等众多围棋高手,到最近特斯拉CEO埃隆o马斯克投资的人工智能研究机构OpenAI研发的机器人击败了国外顶尖Dota 2玩家之一的Danil Ishutin.而重磅消息却是美国电商巨头亚马逊,已准备将人工智能技术用于商业服务中,计划将其用于云存储方面,用于保护用户数据的安全. 亚马逊的这一计划,主要是通过机器学习技术,自动识别.分类和保护用户保存在亚马逊云计算平台上的敏感数据.但引入人工智能技术之后,亚马逊的云存储

如何有效的对PDF文件进行加密保护

PDF是办公中保存资料数据文件不可或缺的一类电子文件工具软件,它的优势在于清晰的位图显示形式和良好的阅读体验,所以很多合同报告.电子书.技术文档.设计图纸等都越来越倾向这种存储方式.和普通的电子文档一样,如Word.Excel,PDF文件也存在信息安全泄漏风险,因此加密保护也是必不可少的.下面就来分享下如何对PDF进行权限设置和PDF文件加密操作. PDF格式的官方编辑器Adobe acrobat 软件为我们提供的口令加密包含"打开文档的口令"和"限制文档编辑打印口令&quo

在win10 LTSB版本中使用UWF组件,实现影子保护功能,提供稳定,高速的开发环境

Win10 LTSB,比win8好用,又没有APP功能,老机运行非常稳定.流畅.安装运行一段时间后,安装了UWF组件,提高安全性和流畅性. Windows UWF(统一写保护)组件,功能: 1.当内存盘用,加强性能,提高速度: 2.省SSD写入: 3.当还原卡用,防熊孩子 开启方法: 1.程序和功能→启用或关闭Windows功能,勾选"统一写入筛选器" 2.UWF设置 手动启动及设定 (要重新启动系统才生效,关机再开是没用的) : 用管理员模式打开控制台,挑选下列可使用命令行 uwfm

.htaccess保护目录与文件

一般来说很多虚拟主机预设是没有开启保护网站主机目录下的文件,其实很危险的,假若你的目录下忘记放置index文件,那很可能您目录就被看光,一个不小心很可能重要资料就被拿走,这是蛮严重的一件事情.如果是Linux主机,我们可以透过简易的.htaccess语法来保护网站主机目录下的文件,让别人无法轻易看见. 平常我们使用的最简单保护目录的方法,莫过于使用「index.php」.「index.html」等等之类的网站index文件,因为主机内预设的设定在目录中预设就是要抓index文件,而index文件