一个操作系统的实现-6_保护模式5

来源:互联网 发布:周杰伦的床边故事知乎 编辑:程序博客网 时间:2024/06/11 05:19
对于中断部分,8259A的具体各种命令字不关注,只关注整个中断向量的组织,从结构上把握中断的处理过程.

1:复习下段描述符和门描述符
Descriptor:  段基址,界限,属性
Gate      :     目标选择子,偏移,DCount,属性

2:IDT(中断描述符表)中的描述符可以是下面三种之一:
中断门描述符
陷阱门描述符
任务门描述符
任务门在Linux中没有用到,中断门与陷阱门的区别在于对中断允许标志IF的影响.由中断门向量引起的中断会复位IF,这样做可以避免其他中断干扰当前中断的处理.在中断处理程序最后返回的iret指令会从堆栈上恢复IF的原值.而通过陷阱门产生的中断不会改变IF.另外,根据门结构的属性中的TYPE字段区分中断门还是陷阱门.

3:IDT的建立
IDT的建立和GDT类似,,只是IDT中的表项是Gate门描述符,通过门描述符里的目标选择子找到GDT中相应的段描述符,根据门描述符里的偏移和段描述符里的段基址找到对应中断处理代码.IdtPtr的加载和GdtPtr的加载类似,通过lidt [IdtPtr]将IDT基址装入IDTR寄存器.可以看成IDT中是中断向量号与中断程序处理入口的对应.

4:中断向量号总共有256个,0-255,其中32(20h)-255是用户定义中断(即外部中断或int n指令).

对于程序的一点说明:

在初始化8259A时,将8259A外部中断的各个引脚设置成与相应的中断向量号对应,即:IRQ0-IRQ7对应中断向量20h-27h,IRQ8-IRQ15对应中断向量28h-2fh.这些都是处于用户定义中断范围.同时8259A这些中断属于外部中断.8259A的IRQ0就对应了8253发出的100MHZ时钟中断,这才有了开启8259A的IRQ0就对应了时钟中断的开启,进而执行了定时中断的代码,与int 80h不同,int 80h属于int n指令产生的软件中断.


程序运行结果如下:(定时器中断太快导致显示太快,有点乱码)


头文件和前面的头文件相同,不再列出.

pmtest9.asm:

; ==========================================; pmtest9.asm; 编译方法:nasm pmtest9.asm -o pmtest9.com; ==========================================%include"pm.inc"; 常量, 宏, 以及一些说明PageDirBase0equ200000h; 页目录开始地址:2MPageTblBase0equ201000h; 页表开始地址:2M +  4KPageDirBase1equ210000h; 页目录开始地址:2M + 64KPageTblBase1equ211000h; 页表开始地址:2M + 64K + 4KLinearAddrDemoequ00401000hProcFooequ00401000hProcBarequ00501000hProcPagingDemoequ00301000horg0100hjmpLABEL_BEGIN[SECTION .gdt]; GDT;                                         段基址,       段界限     , 属性LABEL_GDT:Descriptor       0,                 0, 0; 空描述符LABEL_DESC_NORMAL:Descriptor       0,            0ffffh, DA_DRW; Normal 描述符LABEL_DESC_FLAT_C:Descriptor             0,           0fffffh, DA_CR | DA_32 | DA_LIMIT_4K; 0 ~ 4GLABEL_DESC_FLAT_RW:Descriptor             0,           0fffffh, DA_DRW | DA_LIMIT_4K; 0 ~ 4GLABEL_DESC_CODE32:Descriptor       0,  SegCode32Len - 1, DA_CR | DA_32; 非一致代码段, 32LABEL_DESC_CODE16:Descriptor       0,            0ffffh, DA_C; 非一致代码段, 16LABEL_DESC_DATA:Descriptor       0,DataLen - 1, DA_DRW; DataLABEL_DESC_STACK:Descriptor       0,        TopOfStack, DA_DRWA | DA_32; Stack, 32 位LABEL_DESC_VIDEO:Descriptor 0B8000h,            0ffffh, DA_DRW; 显存首地址; GDT 结束GdtLenequ$ - LABEL_GDT; GDT长度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 选择子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorFlatCequLABEL_DESC_FLAT_C- LABEL_GDTSelectorFlatRWequLABEL_DESC_FLAT_RW- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorDataequLABEL_DESC_DATA- LABEL_GDTSelectorStackequLABEL_DESC_STACK- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDT; END of [SECTION .gdt][SECTION .data1] ; 数据段ALIGN32[BITS32]LABEL_DATA:; 实模式下使用这些符号; 字符串_szPMMessage:db"In Protect Mode now. ^-^", 0Ah, 0Ah, 0; 进入保护模式后显示此字符串_szMemChkTitle:db"BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0; 进入保护模式后显示此字符串_szRAMSizedb"RAM size:", 0_szReturndb0Ah, 0; 变量_wSPValueInRealModedw0_dwMCRNumber:dd0; Memory Check Result_dwDispPos:dd(80 * 6 + 0) * 2; 屏幕第 6 行, 第 0 列。_dwMemSize:dd0_ARDStruct:; Address Range Descriptor Structure_dwBaseAddrLow:dd0_dwBaseAddrHigh:dd0_dwLengthLow:dd0_dwLengthHigh:dd0_dwType:dd0_PageTableNumber:dd0_SavedIDTR:dd0; 用于保存 IDTRdd0_SavedIMREG:db0; 中断屏蔽寄存器值_MemChkBuf:times256db0; 保护模式下使用这些符号szPMMessageequ_szPMMessage- $$szMemChkTitleequ_szMemChkTitle- $$szRAMSizeequ_szRAMSize- $$szReturnequ_szReturn- $$dwDispPosequ_dwDispPos- $$dwMemSizeequ_dwMemSize- $$dwMCRNumberequ_dwMCRNumber- $$ARDStructequ_ARDStruct- $$dwBaseAddrLowequ_dwBaseAddrLow- $$dwBaseAddrHighequ_dwBaseAddrHigh- $$dwLengthLowequ_dwLengthLow- $$dwLengthHighequ_dwLengthHigh- $$dwTypeequ_dwType- $$MemChkBufequ_MemChkBuf- $$SavedIDTRequ_SavedIDTR- $$SavedIMREGequ_SavedIMREG- $$PageTableNumberequ_PageTableNumber- $$DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; IDT[SECTION .idt]ALIGN32[BITS32]LABEL_IDT:; 门                        目标选择子,            偏移, DCount, 属性%rep 32GateSelectorCode32, SpuriousHandler,      0, DA_386IGate%endrep.020h:GateSelectorCode32,    ClockHandler,      0, DA_386IGate%rep 95GateSelectorCode32, SpuriousHandler,      0, DA_386IGate%endrep.080h:GateSelectorCode32,  UserIntHandler,      0, DA_386IGateIdtLenequ$ - LABEL_IDTIdtPtrdwIdtLen - 1; 段界限dd0; 基地址; END of [SECTION .idt]; 全局堆栈段[SECTION .gs]ALIGN32[BITS32]LABEL_STACK:times 512 db 0TopOfStackequ$ - LABEL_STACK - 1; END of [SECTION .gs][SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[_wSPValueInRealMode], sp; 得到内存数movebx, 0movdi, _MemChkBuf.loop:moveax, 0E820hmovecx, 20movedx, 0534D4150hint15hjcLABEL_MEM_CHK_FAILadddi, 20incdword [_dwMCRNumber]cmpebx, 0jne.loopjmpLABEL_MEM_CHK_OKLABEL_MEM_CHK_FAIL:movdword [_dwMCRNumber], 0LABEL_MEM_CHK_OK:; 初始化 16 位代码段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], axshreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位代码段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 初始化数据段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_DATAmovword [LABEL_DESC_DATA + 2], axshreax, 16movbyte [LABEL_DESC_DATA + 4], almovbyte [LABEL_DESC_DATA + 7], ah; 初始化堆栈段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACKmovword [LABEL_DESC_STACK + 2], axshreax, 16movbyte [LABEL_DESC_STACK + 4], almovbyte [LABEL_DESC_STACK + 7], ah; 为加载 GDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 为加载 IDTR 作准备xoreax, eaxmovax, dsshleax, 4addeax, LABEL_IDT; eax <- idt 基地址movdword [IdtPtr + 2], eax; [IdtPtr + 2] <- idt 基地址; 保存 IDTRsidt[_SavedIDTR]; 保存中断屏蔽寄存器(IMREG)值inal, 21hmov[_SavedIMREG], al; 加载 GDTRlgdt[GdtPtr]; 关中断;cli; 加载 IDTRlidt[IdtPtr]; 打开地址线A20inal, 92horal, 00000010bout92h, al; 准备切换到保护模式moveax, cr0oreax, 1movcr0, eax; 真正进入保护模式jmpdword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:; 从保护模式跳回到实模式就到了这里movax, csmovds, axmoves, axmovss, axmovsp, [_wSPValueInRealMode]lidt[_SavedIDTR]; 恢复 IDTR 的原值moval, [_SavedIMREG]; ┓恢复中断屏蔽寄存器(IMREG)的原值out21h, al; ┛inal, 92h; ┓andal, 11111101b; ┣ 关闭 A20 地址线out92h, al; ┛sti; 开中断movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位代码段. 由实模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorDatamovds, ax; 数据段选择子moves, axmovax, SelectorVideomovgs, ax; 视频段选择子movax, SelectorStackmovss, ax; 堆栈段选择子movesp, TopOfStackcallInit8259Aint080hstijmp$; 下面显示一个字符串pushszPMMessagecallDispStraddesp, 4pushszMemChkTitlecallDispStraddesp, 4callDispMemSize; 显示内存信息callPagingDemo; 演示改变页目录的效果callSetRealmode8259A; 到此停止jmpSelectorCode16:0; Init8259A ---------------------------------------------------------------------------------------------Init8259A:moval, 011hout020h, al; 主8259, ICW1.callio_delayout0A0h, al; 从8259, ICW1.callio_delaymoval, 020h; IRQ0 对应中断向量 0x20out021h, al; 主8259, ICW2.callio_delaymoval, 028h; IRQ8 对应中断向量 0x28out0A1h, al; 从8259, ICW2.callio_delaymoval, 004h; IR2 对应从8259out021h, al; 主8259, ICW3.callio_delaymoval, 002h; 对应主8259的 IR2out0A1h, al; 从8259, ICW3.callio_delaymoval, 001hout021h, al; 主8259, ICW4.callio_delayout0A1h, al; 从8259, ICW4.callio_delay;moval, 11111111b; 屏蔽主8259所有中断moval, 11111110b; 仅仅开启定时器中断out021h, al; 主8259, OCW1.callio_delaymoval, 11111111b; 屏蔽从8259所有中断out0A1h, al; 从8259, OCW1.callio_delayret; Init8259A ---------------------------------------------------------------------------------------------; SetRealmode8259A ---------------------------------------------------------------------------------------------SetRealmode8259A:movax, SelectorDatamovfs, axmoval, 017hout020h, al; 主8259, ICW1.callio_delaymoval, 008h; IRQ0 对应中断向量 0x8out021h, al; 主8259, ICW2.callio_delaymoval, 001hout021h, al; 主8259, ICW4.callio_delaymoval, [fs:SavedIMREG]; ┓恢复中断屏蔽寄存器(IMREG)的原值out021h, al; ┛callio_delayret; SetRealmode8259A ---------------------------------------------------------------------------------------------io_delay:nopnopnopnopret; int handler ---------------------------------------------------------------_ClockHandler:ClockHandlerequ_ClockHandler - $$incbyte [gs:((80 * 0 + 70) * 2)]; 屏幕第 0 行, 第 70 列。moval, 20hout20h, al; 发送 EOIiretd_UserIntHandler:UserIntHandlerequ_UserIntHandler - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, 'I'mov[gs:((80 * 0 + 70) * 2)], ax; 屏幕第 0 行, 第 70 列。iretd_SpuriousHandler:SpuriousHandlerequ_SpuriousHandler - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, '!'mov[gs:((80 * 0 + 75) * 2)], ax; 屏幕第 0 行, 第 75 列。jmp$iretd; ---------------------------------------------------------------------------; 启动分页机制 --------------------------------------------------------------SetupPaging:; 根据内存大小计算应初始化多少PDE以及多少页表xoredx, edxmoveax, [dwMemSize]movebx, 400000h; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小divebxmovecx, eax; 此时 ecx 为页表的个数,也即 PDE 应该的个数testedx, edxjz.no_remainderincecx; 如果余数不为 0 就需增加一个页表.no_remainder:mov[PageTableNumber], ecx; 暂存页表个数; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.; 首先初始化页目录movax, SelectorFlatRWmoves, axmovedi, PageDirBase0; 此段首地址为 PageDirBasexoreax, eaxmoveax, PageTblBase0 | PG_P  | PG_USU | PG_RWW.1:stosdaddeax, 4096; 为了简化, 所有页表在内存中是连续的.loop.1; 再初始化所有页表moveax, [PageTableNumber]; 页表个数movebx, 1024; 每个页表 1024 个 PTEmulebxmovecx, eax; PTE个数 = 页表个数 * 1024movedi, PageTblBase0; 此段首地址为 PageTblBasexoreax, eaxmoveax, PG_P  | PG_USU | PG_RWW.2:stosdaddeax, 4096; 每一页指向 4K 的空间loop.2moveax, PageDirBase0movcr3, eaxmoveax, cr0oreax, 80000000hmovcr0, eaxjmpshort .3.3:nopret; 分页机制启动完毕 ----------------------------------------------------------; 测试分页机制 --------------------------------------------------------------PagingDemo:movax, csmovds, axmovax, SelectorFlatRWmoves, axpushLenFoopushOffsetFoopushProcFoocallMemCpyaddesp, 12pushLenBarpushOffsetBarpushProcBarcallMemCpyaddesp, 12pushLenPagingDemoAllpushOffsetPagingDemoProcpushProcPagingDemocallMemCpyaddesp, 12movax, SelectorDatamovds, ax; 数据段选择子moves, axcallSetupPaging; 启动分页callSelectorFlatC:ProcPagingDemocallPSwitch; 切换页目录,改变地址映射关系callSelectorFlatC:ProcPagingDemoret; ---------------------------------------------------------------------------; 切换页表 ------------------------------------------------------------------PSwitch:; 初始化页目录movax, SelectorFlatRWmoves, axmovedi, PageDirBase1; 此段首地址为 PageDirBasexoreax, eaxmoveax, PageTblBase1 | PG_P  | PG_USU | PG_RWWmovecx, [PageTableNumber].1:stosdaddeax, 4096; 为了简化, 所有页表在内存中是连续的.loop.1; 再初始化所有页表moveax, [PageTableNumber]; 页表个数movebx, 1024; 每个页表 1024 个 PTEmulebxmovecx, eax; PTE个数 = 页表个数 * 1024movedi, PageTblBase1; 此段首地址为 PageTblBasexoreax, eaxmoveax, PG_P  | PG_USU | PG_RWW.2:stosdaddeax, 4096; 每一页指向 4K 的空间loop.2; 在此假设内存是大于 8M 的moveax, LinearAddrDemoshreax, 22movebx, 4096mulebxmovecx, eaxmoveax, LinearAddrDemoshreax, 12andeax, 03FFh; 1111111111b (10 bits)movebx, 4mulebxaddeax, ecxaddeax, PageTblBase1movdword [es:eax], ProcBar | PG_P | PG_USU | PG_RWWmoveax, PageDirBase1movcr3, eaxjmpshort .3.3:nopret; ---------------------------------------------------------------------------; PagingDemoProc ------------------------------------------------------------PagingDemoProc:OffsetPagingDemoProcequPagingDemoProc - $$moveax, LinearAddrDemocalleaxretf; ---------------------------------------------------------------------------LenPagingDemoAllequ$ - PagingDemoProc; ---------------------------------------------------------------------------; foo -----------------------------------------------------------------------foo:OffsetFooequfoo - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, 'F'mov[gs:((80 * 17 + 0) * 2)], ax; 屏幕第 17 行, 第 0 列。moval, 'o'mov[gs:((80 * 17 + 1) * 2)], ax; 屏幕第 17 行, 第 1 列。mov[gs:((80 * 17 + 2) * 2)], ax; 屏幕第 17 行, 第 2 列。retLenFooequ$ - foo; ---------------------------------------------------------------------------; bar -----------------------------------------------------------------------bar:OffsetBarequbar - $$movah, 0Ch; 0000: 黑底    1100: 红字moval, 'B'mov[gs:((80 * 18 + 0) * 2)], ax; 屏幕第 18 行, 第 0 列。moval, 'a'mov[gs:((80 * 18 + 1) * 2)], ax; 屏幕第 18 行, 第 1 列。moval, 'r'mov[gs:((80 * 18 + 2) * 2)], ax; 屏幕第 18 行, 第 2 列。retLenBarequ$ - bar; ---------------------------------------------------------------------------; 显示内存信息 --------------------------------------------------------------DispMemSize:pushesipushedipushecxmovesi, MemChkBufmovecx, [dwMCRNumber];for(int i=0;i<[MCRNumber];i++) // 每次得到一个ARDS(Address Range Descriptor Structure)结构.loop:;{movedx, 5;for(int j=0;j<5;j++)// 每次得到一个ARDS中的成员,共5个成员movedi, ARDStruct;{// 依次显示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type.1:;pushdword [esi];callDispInt;DispInt(MemChkBuf[j*4]); // 显示一个成员popeax;stosd;ARDStruct[j*4] = MemChkBuf[j*4];addesi, 4;decedx;cmpedx, 0;jnz.1;}callDispReturn;printf("\n");cmpdword [dwType], 1;if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2jne.2;{moveax, [dwBaseAddrLow];addeax, [dwLengthLow];cmpeax, [dwMemSize];if(BaseAddrLow + LengthLow > MemSize)jb.2;mov[dwMemSize], eax;MemSize = BaseAddrLow + LengthLow;.2:;}loop.loop;};callDispReturn;printf("\n");pushszRAMSize;callDispStr;printf("RAM size:");addesp, 4;;pushdword [dwMemSize];callDispInt;DispInt(MemSize);addesp, 4;popecxpopedipopesiret; ---------------------------------------------------------------------------%include"lib.inc"; 库函数SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32]; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回实模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0andal, 11111110bmovcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址会在程序开始处被设置成正确的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]





阅读全文
0 0
原创粉丝点击