"Write the operating system yourself" read Sense http://blog.csdn.net/zgh1988/article/details/7059936
Comprehensive analysis of the first chapter of "self-writing Operating System" http://blog.csdn.net/zgh1988/article/details/7060032
Comprehensive Analysis of the "self-writing Operating System" Chapter 2 http://blog.csdn.net/zgh1988/article/details/7062065
1; ========================================================== = 2; pmtest1.asm 3; Compilation Method: NASM pmtest1.asm-O pmtest1.com 4; ========================================================== = 5 6% include "PM. INC "; constants, macros, and some descriptions 7 8 or1_100h 9jmplabel_begin1011 [section. gdt] 12; gdt13; Segment Base Address, segment boundary, attribute 14 label_gdt: descriptor 0, 0, 0; empty descriptor 15 label_desc_code32: descriptor 0, segcode32len-1, da_c + da_32; inconsistent code segment, 3216 label_desc_video: descri Ptor 0b8000h, 0 ffffh, da_drw; memory first address 17; gdt end 1819 gdtlenequ $-label_gdt; gdt end 20 gdtptrdwgdtlen-1; gdt boundary 21dd0; gdt base address 2223; gdt selects sub 24 selectorcode32equlabel_desc_code32-label_gdt25 selectorvideoequlabel_desc_video-label_gdt26; end of [section. gdt] 2728 [section. s16] 29 [bits16] 30 label_begin: 31 movax, cs32 movds, ax33 moves, ax34 movss, ax35 movsp, 0100h36 37; initialize 32-bit code segment descriptor 38 xoreax, eax3 9 movax, cs40 shleax, 441 addeax, clerk [clerk + 2], ax43shreax, 1644 movbyte [label_desc_code32 + 4], al45movbyte [label_desc_code32 + 7], ah4647; prepare 48 xoreax, eax49movax, ds50shleax, 451 addeax, label_gdt; eax <-gdt base address 52 movdword [gdtptr + 2], eax; [gdtptr + 2] <-gdt base address 5354; load gdtr55lgdt [gdtptr] 5657; disconnect 58cli5960 from Guanzhong; open address line a2061inal, 92h62oral, 20170010b63 Out92h, al6465; prepare to switch to protection mode 66 moveax, cr067oreax, 168movcr0, eax6970; enter protection mode 71 jmpdword selectorcode32: 0; execute this statement to load selectorcode32 into CS, and jump to code32selector: 72 at 0; end of [section. s16. 737475 [section. s32]; 32-bit code segment. jump from the real mode. 76 [bits32] 7778 label_seg_code32: 79 movax, selectorvideo80movgs, ax; video segment Selection Sub-(Purpose) 8182 movedi, (80*10 + 0) x 2; screen 10th rows, 0th columns. 83 movah, 0ch; 0000: Black Bottom 1100: red letter 84 moval, 'P' 85mov [GS: EDI], ax8687; stop 88jmp $8990 segcode32lenequ $-label_seg_code3291; end of [section. s32]
In the following section, I will describe the problems and confusions encountered in section 3.1. Take pmtest1.asm as an example to analyze and explain the following aspects.
1) [section. xxx] Why?
2) What are the Global Descriptor Table (gdt), Descriptor (descriptor), Global Descriptor Table register (GDTR), and selectorxxx? What is used?
3) What is the difference between addressing in real mode and protection mode?
4) descriptor macro definition and initialization segment descriptor
5) Prepare for loading GDTR
6) Others
1. [section. xxx] Why?
The functions of section and segment are similar, that is, the meaning of "segment". From the perspective of the whole program, this program is divided into three modules, namely [section. gdt], [seciton. s16], [section. s32. We can easily see that [section. gdt] should be a data segment, and the other two are code segments. [Section. xxx] divides programs into different modules to complete different functions, making the program clear and clear.
2. What are Descriptor (descriptor), Global Descriptor Table (gdt), Global Descriptor Table register (GDTR), and selectorxxx? What is used?
Segment. In, the segmentation mechanism divides the memory space into one or more linear regions. We call these linear regions segments. We need to distinguish these segments, so the segmentation mechanism assigns three attributes to each segment: 1. The segment base address ): specify the start address of the segment in the linear address space. 2. segment limit (Limit): indicates the maximum available offset within a segment, that is, it defines the length of the segment. 3. attribute: Specifies the features of a segment, including: readable, writable, executable, and privileged level.
Segment descriptor (descriptor). In a program, we need to define a Data Structure to represent a segment. It also contains three elements: the base and the limit ), segment attribute, which is called a segment descriptor ). Segment descriptors and segments are the same concept. Each segment descriptor occupies 8 bytes of space.
Segment descriptor table, in a program, there is not only one segment (segment descriptor ). So we need to organize these segment descriptors, So we define an array of the storage segment descriptors, called the segment descriptor table.
Segment selectorxxx stores all segment descriptors in the segment descriptor table. When we use a segment, we do not direct it to the segment, it is accessed through the location of the segment descriptor in the segment descriptor table. The segment selection sub is a 16-bit identifier used to identify the position of the segment descriptor in the descriptor table.
Segment descriptor tables can be divided into two categories: Global Descriptor Table gdt (Global Descriptor Table) and Local Descriptor Table LDT (Local Descriptor Table ). In the system, all tasks share the Global Descriptor Table, while different tasks use their own local descriptor table.
Then, how can we let the system know where the segment descriptor table is? The processor provides memory management registers, including Global Descriptor Table register (GDTR) and Local Descriptor Table register (ldtr ). The GDTR register is used to store the 32-bit linear base address of the gdt Global Descriptor Table and the 16-bit table length value. The ldtr register stores the 32-bit linear base address of the Local Descriptor Table LDT and the 16-bit table length value. Through system commands, lgdt loads the linear base address and length value of gdt into the GDTR register, and lldt loads the linear base address and length value of LDT into the ldtr register.
Next we will analyze the code in the program:
11 [section. gdt] 12; gdt13; Segment Base Address, segment boundary, attribute 14 label_gdt: descriptor 0, 0, 0; empty descriptor 15 label_desc_code32: descriptor 0, segcode32len-1, da_c + da_32; non-consistent code segment, 3216 label_desc_video: descriptor 0b8000h, 0 ffffh, da_drw; memory first address 17; gdt end 1819 gdtlenequ $-label_gdt; gdt length 20 Gbit/s-1; gdt Bo; gdt base address 2223; gdt Select Sub 24 selectorcode32equlabel_desc_code32-label_gdt25 selectorvideoequlabel_desc_video-label_gdt26; end of [section. gdt]
In the program, lines 14, 15, and 16 define three segment descriptors, label_gdt (empty descriptor), label_desc_code32 (32-bit segment descriptor), and label_sesc_video (Display memory descriptor ). Each descriptor contains three attributes: Segment Base Address, segment boundary, and segment attribute.
Organize the three descriptors together to form a global segment descriptor table (gdt ). Line 12-17 defines the gdt.
Gdtlen is the length of gdt.
Gdtptr is a data structure that contains two elements. The first element is the gdt boundary of 2 bytes. The second element is the base address of the 4 bytes gdt. The data structure is the same as that of the Global Descriptor Table register (GDTR). Therefore, when GDTR is loaded (55 lines of source code), the gdtptr is loaded into GDTR.
Because label_gdt in the first segment is an empty descriptor, it only represents the initial address of the gdt. Therefore, this descriptor is an empty Descriptor and generally does not create a child for it. Then, the program creates two selectorcode32 and selectorvideo sub-sets (rows 24 and 25), which correspond to the label_sesc_code32 and label_desc_video segments respectively.
You should understand the structure of this Code. Next I will introduce the segment descriptor, Global Descriptor Table register, and select the sub-data structure.
The Structure of the segment descriptor is as follows:
The segment descriptor occupies 8 bytes. Here I just want to remind you that the segment base address occupies byte2, byte3, byte4, and byte7 respectively. We need to use these when initializing the segment descriptor below.
The structure of the sub-segment is shown in:
Here is a brief introduction, it uses (15... 3) To represent the index. Therefore, each Descriptor Table can only use up to 213 descriptors. Except for the first null descriptor, 8191 descriptors can be used.
Ti indicates whether the segment descriptor to which the Selection Sub points is a Global Descriptor or a local descriptor. When Ti = 0, it indicates the Global Descriptor. When Ti = 1, it indicates the local descriptor.
RPL Request priority, which will be mentioned later in the next section.
The structure of the Global Descriptor Table register (gdt) is as follows:
Here, you can compare it with the gdtptr data structure in the above program to see if the format is the same. Two bytes indicate the gdt boundary, and four bytes indicate the gdt base address.
3. What are the differences between the addressing mode in real mode and the addressing mode in protection mode?
In the real mode, that is, the addressing mode in the 8086 system. Intel 8086 is a 16-bit CPU. It has 16-bit registers, 16-bit data bus, and 20-bit address bus) and 1 MB addressing capability. An address consists of two parts: Segment and offset. The physical address follows the formula below:
Physical address (physical address) = segment value (segment) * 16 + offset)
Both the segment value and offset are 16 bits. Therefore, the addressing range is 1 MB.
In the protection mode, the segment mechanism is enabled, so its addressing mode has changed greatly. As shown in:
In protection mode, first use the segment selector to find the corresponding segment descriptor in the segment descriptor table, find the base address of the 32-bit segment, and then add the offset with the 32-bit, obtain the linear address. The base address and offset are both 32-bit, so the addressing range is 4 GB. In the program, the role of jmp dword slectorcode32: 0 is to enter the addressing mode in protection mode. When a segment is used, its segment Selection Sub-is stored in the segment register.
There is a problem here. Do we need to go to the Global Descriptor Table register (GDTR) to find the base address of the Global Descriptor Table (gdt, next, jump to the location where the descriptor is located based on the selected sub-index, and then obtain the base address in the segment descriptor. If so, we interviewed the memory several times, which is a waste of time. In fact, the segment register structure is as follows:
The advantage is that we can directly obtain the segment descriptor.
4. descriptor macro definition and initialization segment descriptor
1; descriptor 2; usage: descriptor base, limit, ATTR 3; base: dd 4; Limit: dd (low 20 bits available) 5; ATTR: DW (lower 4 bits of higher byte are always 0) 6% macro descriptor 3 7dw % 2 & 0 ffffh; Segment boundary 1 (2 bytes) 8dw % 1 & 0 ffffh; segment Base Address 1 (2 bytes) 9db (% 1> 16) & 0ffh; Segment Base Address 2 (1 byte) 10dw (% 2> 8) & 0f00h) | (% 3 & 0f0ffh); Attribute 1 + segment Boundary 2 + attribute 2 (2 bytes) 11db (% 1> 24) & 0ffh; Segment Base Address 3 (1 byte) 12% endmacro; 8 bytes in total
The comments of lines 2, 3, 4, and 5 tell us that the macro definition requires three variables: Segment Base Address (4 bytes), segment boundary (4 bytes ), segment attribute (DW ).
Looking back at the segment descriptor structure, this macro defines that the variables base, limit, and ATTR are separately inserted to the corresponding positions in the descriptor. Base is 1, limit is 2, ATTR is 3
7 is to assign a 16-bit low limit value to byte0 and byte1 of the descriptor.
8 is to assign 16 bits of base to byte2 and byte3 of the descriptor.
9 is to assign the low 8 bits (that is, the 16-23 bits of the original base) after the base is shifted to the byte4 of the descriptor.
10 is to combine the 8-11 bits after the right shift of limit and the 0-7 and 12-15 bits of ATTR to store the byte5 and byte6 of the descriptor.
11 is to assign the base's low 8 bits (that is, the original base's 24-32 bits) after the right shift to the byte7 descriptor.
Initialization segment descriptor code:
37; initialize 32-bit code segment descriptor 38 xoreax, eax39 movax, cs40 shleax, 441 addeax, runtime [label_desc_code32 + 2], ax43shreax, 1644 movbyte [label_desc_code32 + 4], al45movbyte [label_desc_code32 + 7], ah
Why is initialization required? You will find that only the byte2, byte4, and byte7 of the segment descriptor label_desc_code32 are modified here. Did you suddenly realize it? Because the base address of the label_desc_code32 descriptor is initialized to 0 when we initialize it, we need to modify the base address of the descriptor to its actual address. This is also a part of attention when we first introduced the segment descriptor, that is, the base address of the descriptor occupies byte2, byte4, byte7.
5. Prepare for loading GDTR
47. Prepare 48 xoreax, eax49movax, ds50shleax, 451 addeax, label_gdt; eax <-gdt base address 52 movdword [gdtptr + 2], eax; [gdtptr + 2] <-gdt base address 5354; load gdtr55lgdt [gdtptr]
This is easy to understand. We assign values to gdtptr, mainly to initialize the base address of gdt. That is, assign the initial gdt address to byte2, byte3, byte4, and byte5 of gdtptr. Make the gdtptr Data Structure exactly match GDTR, and then execute lgdt [gdtptr] to load the Global Descriptor Table register. Assign the base address and Boundary Value of gdt to GDTR.
6. Others
For a series of problems such as the next disconnection, opening the address line A20, switching to the protection mode, entering the protection mode, and redirecting to the 32-bit code segment, you can find a proper explanation from the book.
Reference books: Linux kernel full analysis based on 0.12 Kernel