We can finally look at the code of the 14th chapter by doing so much of the groundwork.
For the boot code and user program, still use the 13th chapter, for the kernel program (C14_CORE.ASM), compile a few lines of error, as long as the addition dword
can be resolved.
1. Why use the call gate
In the 13th chapter, in order to be able to use the kernel-provided routines, the user program is call far
transferred directly to the kernel routines (non-Uniform Code snippets) with instructions. Because CPL=目标代码段描述符的DPL=RPL=0
, in accordance with the conditions of the table below, so the transfer is not a problem.
However, in this chapter, the user program works at 3 privilege level, not 0 privilege level, so it cannot be transferred directly. But there is no need to be pessimistic, we still have the means, can be transferred by calling the door.
2. What is the call gate
For the knowledge of calling the door, refer to my blog: The Call Door
The format of the calling gate is as follows:
3. Installation of the calling door
811 The following begins the installation of the call gate for the entire system service. Control transfers between privileged levels must use the door812 movEdi,salt; Starting position of the C-salt table813 movEcx,salt_items; Number of entries in the C-salt table814 . b3:815 PushEcx816 moveax,[edi+ the]The 32-bit offset address of the entry point817 movbx,[edi+260]; The segment selector for the entry point818 movCx1_11_0_1100_000_00000b, privilege Level 3 call gate (more than 3 of privilege levels819 allow access), 0 parameters (because of the register820 ; pass a parameter without a stack)821 PagerSys_routine_seg_sel:make_gate_descriptor822 PagerSys_routine_seg_sel:set_up_gdt_descriptor823 mov[edi+260],cxThe returned door descriptor is selected as a child backfill824 AddEdi,salt_item_len; point to next C-salt entry825 PopEcx826Loop. b3
Review the process first make_gate_descriptor
.
331 make_gate_descriptor: ;构造门的描述符(调用门等)332 ;输入:EAX=门代码在段内偏移地址333 ; BX=门代码所在段的选择子 334 ; CX=段类型及属性等(各属335 ; 性位都在原始位置)336 ;返回:EDX:EAX=完整的描述符
816~821: Call make_gate_descriptor
The procedure construction call Gate (refer to the format of the call Gate), p=1,dpl=3, the number of parameters = 0;
822: The calling procedure set_up_gdt_descriptor
installs the constructed call door into the GDT and returns the corresponding selector (ti=0,rpl=0);
264 set_up_gdt_descriptor: ;在GDT内安装一个新的描述符265 266 ;输出:CX=描述符的选择子
823: The returned call gate is selected as a sub-backfill, overwriting the original segment selector. As shown in (a table entry in the kernel symbol table):
4. Test of the call gate
828 ;对门进行测试 829 mov ebx,message_2830 call far [salt_1+256] ;
On the surface, this is an indirect absolute far call, through the memory address in the instruction, can indirectly obtain the 32-bit offset and 16 bits of the code segment selector; However, when the processor executes this instruction, it will use the Select child to access the GDT, and the result is a call gate, so the 32-bit offset (the green part) is ignored.
After the call door installation is complete, the GDT is as follows:
Not only the indirect absolute far call is so, the direct absolute far call is also the case, if the selector is pointing to the call gate, the offset is also ignored. For example
call 0x0040:0x00001234
In combination, the offset 0x00001234 is ignored because the 0x40 is called the gate.
5. Loading the user program with the Create User Task 5.1 task control block (BLOCK,TCB) 5.1.1 TCB format
Loading the program and creating a task requires a lot of data, such as program size, loading location, and so on. The kernel should create a memory area for each task that records the information and status of the task, which is called the task control BLOCK,TCB.
It should be stated that the TCB is not the processor's requirement, but that we invented it for our own convenience.
About the structure of the TCB, such as the original book 14-12 (P264). For the convenience of the reader, I will draw the picture again here.
Note that this format was invented by the author, not that the TCB has to be this format.
5.1.2. TCB Chain List
In order to be able to track all tasks, each TCB can be strung together to form a linked list.
In the core data segment of the code, the label is declared tcb_chain
and a double word is initialized with a value of 0.
413 ;任务控制块链414 tcb_chain dd 0
In fact, this is equivalent to a pointer to the TCB that points to the first task. When it is 0 o'clock, it indicates no task. All tasks are linked together in the order in which they are created to form a headless one-way non-circular linked list.
835 ;创建任务控制块。这不是处理器的要求,而是我们自己为了方便而设立的836 mov ecx,0x46837 call sys_routine_seg_sel:allocate_memory838 call append_to_tcb_link ;
The above three lines are used to allocate the TCB space (0x46 bytes) and then hang the TCB on the linked list (tail interpolation method).
5.2. Load User Tasks
840 push50 ;用户程序位于逻辑50扇区841 push ecx ;压入任务控制块起始线性地址 842 843 call load_relocate_program
The above three lines are used to load and relocate the user program.
5.2.1. Passing parameters using stacks
464Load_relocate_program:; load and reposition user programs465 ; input: PUSH Logical Sector Code466 ; PUSH task control block base Address467 ; output: None468Pushad469 470 PushDs471 PushEs472 473 movEbp,esp; Prepare for access to parameters passed through the stack
This is a load_relocate_program
few lines at the beginning of the process, after the execution of line No. 473, the status of the stack is as follows:
Here is the main review of how to use the stack to pass parameters. It should be stated that:
1. When addressing with EBP registers, the segment register SS is used by default;
2. In 32-bit mode, the default operand size of the stack operation is double word;
3. When the processor executes the stack instruction, if the operand is found to be a segment register, the 16-bit value of the segment register is expanded to 32 bits (16 bits full 0), then the stack operation is performed, the counter operation is performed, the value of the 32 bits is truncated, and the lower 16 bits are retained, and the corresponding segment registers are transmitted
4. Because load_relocate_program
it is entered by the 32-bit near call (line No. 843), so only the contents of the EIP, not pressed into CS;
475 mov ecx,mem_0_4_gb_seg_sel476 mov es,ecx477 478 mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址
After the above three lines have been executed, ES points to the 0-4GB data segment, and ESI points to the base address of the TCB.
5.2.2. Fill in the TCB with the base address and initial limit values of the LDT
GDT is generally used to store the global space of the segment descriptor. For a task-private segment descriptor, it can also be placed in the GDT, but preferably in its own private LDT.
480 ;以下申请创建LDT所需要的内存481 mov ecx,160 ;允许安装20个LDT描述符482 call sys_routine_seg_sel:allocate_memory483 mov [es:esi+0x0c],ecx ;登记LDT基地址到TCB中484 mov word [es:esi+0x0a],0xffff ;登记LDT初始的界限到TCB中
5.2.3. Loading the user program from the hard disk into memory
- Es points to the 0-4GB data segment; ESI points to the TCB base site;
- Determine how much memory the user program needs based on the header information;
- Allocate memory and register the user program to the TCB from the base address in the storage;
- Cyclic call
read_hard_disk_0
loading user program;
5.2.4. Installing descriptors in the LDT
- A descriptor for the installation header segment, code snippet, data segment, and inherent stack segment (dpl=3);
- These descriptors corresponding to the selector (make its rpl=3) to backfill to the user program head, in the LDT installed descriptor, usually only by the user program himself, so the requestor is the user program itself, so its choice of sub-rpl= user program cpl=3;
- The selection of the head segment (which makes it rpl=3) is also enlisted in the TCB.
The format of the user program's head is exactly the same as the 13th chapter.
5.2.5. Reposition Symbol Table
This process is essentially the same as the 13th chapter. Note that the user symbol table in the call Gate selects the sub, its rpl=3;
5.2.6 creating 0-, 1-, 2-Privilege stacks
By invoking the control transfer of the gate, it is possible to change the CPL. If the control is transferred to a higher privileged non-uniform code segment through the call gate, the CPL is set to the DPL value of the target code segment and causes a stack switch.
To do this, you must define additional stacks for each task. For our user task, we need to create a stack of privilege levels 0, 1, 2 for it. Furthermore, these stacks should have a corresponding segment descriptor in the LDT.
These stacks are created dynamically by the kernel for the user program and need to be registered in the TSS so that the processor firmware can automatically access them. However, we have not yet created TSS, so it is necessary to register the information of these stacks for temporary preservation in the TCB (e.g.).
The steps for creating a stack of the X (x=0,1,2) privilege level are as follows:
1. Apply for memory, allocate space for stack;
2. Create a stack segment descriptor (DPL=X) in the LDT;
3. Register the Stack's information in the TCB, including the stack size, base address, selector (rpl=x), and initial value of espx (=0);
I think the size of the stack and the registration of the base site is not necessary, because these fields are not required in TSS.
Confined to the space, this article is here. Rest and relax ...
"Not to be Continued"
Task and Privilege level protection (ii)--"x86 assembly language: From the actual mode to the protection mode" Reading notes 32