Author: Trevor article taken from: open systems world - SEOUL 2002 December 4 http://developer.ccidnet.com/pub/disp/Article?columnID=322&articleID=32660&pageNO=1
In the two phases (
Write your operating system one,
2), I tell you how to write some code in the boot sector provided by Linux, and how to call the BIOS problem. Now, this operating system is getting closer to the Linux kernel of Linus Torvalds that is "Historical Significance". Therefore, to switch this system immediately below the protection mode.
What is protection mode
Since the first microprocessor has been launched in 1969, the Intel processor has been constantly updating, from 8086, 8088, 80286, to 80386, 80486, Pentium, Pentium II, Pentium 4, etc., its architecture is constantly changing . After 80386, some new features have been provided to compensate for some of the 8086 defects. This includes memory protection, multitasking and memory using more than 640KB or more, and still maintains compatibility with 8086 families. That is to say that 80386 still has all the features of 8086 and 80286, but there is a lot of enhancements. Early processors were working under real mode, and 80286 was introduced in the protection mode, while 80386 was greatly improved. In 80386, the protection mode provides programmers with better protection and provides more memory. In fact, the purpose of the protection mode is not to protect the program, but to protect all programs other than the program (including the operating system).
Briefly, the protection mode is the most natural mode of the processor. In this mode, all features of the processor and all features of the architecture are available, and the highest performance can be achieved.
Protection mode and real mode
From the surface, the protection mode and real model do not differ much, both use memory segments, interrupts, and device drivers to handle hardware, but there are many differences. We know that memory in real mode is divided into segments, each segment is 64kB, and such segment addresses can be represented by 16. The processing of memory segments is processed by internal mechanisms associated with segment registers, and content of these segments (CS, DS, SS, and ES) form part of the physical address. Specifically, the final physical address consists of 16-bit segment addresses and 16-bit segment offset addresses. Expressed with formulas:
Physical address = Left transfer 4 bits segment offset address.
In protection mode, the segment is defined by a set of tables called "descriptor table". Segment registers are stored in pointers to these tables. There are two tables for defining the memory segments: Global Descriptive Table (GDT) and Local Descriptive Table (LDT). GDT is a segment descriptor array, which contains basic descriptors that all applications can be used. In the real mode, the length is fixed (64kb), and in the protection mode, the length is variable, and its maximum is 4GB. LDT is also an array of segment descriptors. Unlike GDT, the LDT is a segment, which is stored, and a segment descriptor that does not require global sharing. Each operating system must define a GDT, and each running task has a corresponding LDT. The length of each descriptor is 8 bytes, and the format is shown in Figure 3. When the segment register is loaded, the segment base address is obtained from the corresponding table entry. The contents of the descriptor are stored in a SHDOW Register in a programmer so that the next time the same segment can be used without each time to extract each time. The physical address is composed of the base address in the image register from a 16-bit or 32-bit offset. The difference in real mode and protection mode can be seen clearly from Figures 1 and 2. In addition, there is an interrupt descriptor table (IDT). These interrupt descriptors tell the processor to find the interrupt handler. Like the real model, each interrupt has an entry, but the format of these entrance is completely different. Because I didn't use the IDT during the process of switching to the protection mode, there is not much introduction.
Enter protection mode
80386 has 4 32-bit control registers, named CR0, CR1, CR2, and CR3, respectively. CR1 is reserved in future processors, and is not defined in 80386. The CR0 contains the control flag of the system for controlling the operation mode and status of the processor. CR2 and CR3 are used to control paging mechanisms. Here, we are concerned about the PE bit control of the CR0 register, which is responsible for switching between real mode and protection mode. When PE = 1, the processor is operated under the protection mode, which corresponds to the segment mechanism used to correspond to the corresponding content described above. If PE = 0, then the processor works below the real mode.
Switch to the protection mode, actually put the PE position to 1. In order to switch the system to the protection mode, you have to do some other things. The program must initialize the segment register and control register of the system. After the PE bit 1, the jump instruction is also performed. The process is briefly described as follows:
1. Create a GDT table;
2. Enter the protection mode by setting the PE bit.
3. Perform jump to clear any instructions read in real mode.
The following is used to implement this switching process.
Needable
◆ A blank floppy disk
◆ NASM compiler
Below is the source code for the entire program:
ORG 0x07C00; start address is 0000: 7C00
JMP short begin_boot; skip other data, jump to the beginning of the boot program
Bootmesg DB "OOS Boot Sector Loading ..."
PM_MESG DB "Switch to protected mode ...."
DW 512; bytes of each sector
DB 1; number of sectors of each cluster
DW 1; reserved sector number
DB 2
DW 0x00e0
DW 0x0B40
DB 0x0F0
DW 9
DW 18
DW 2; reading and writing sector number
DW 0; hidden fan area number
Print_mesg:
MOV AH, 0X13; use the function 13 of the interrupt 10h, write a string on the screen
MOV Al, 0x00; Decide the location of the cursor after calling the function
MOV BX, 0x0007; Set Display Properties
MOV CX, 0x20; this string length is 32
MOV DX, 0x0000; starting line and column of cursor
INT 0x10; calling BIOS interrupt 10h
Ret; return to the caller get_key:
MOV AH, 0x00
INT 0x16; GET_KEY uses the function of 16h, reads the next character
RET
CLRSCR:
MOV AX, 0x0600; functions of interrupt 10h, implement the scroll screen, if Al = 0 is clear
MOV CX, 0x0000; clear screen
MOV DX, 0x174F; Volume Screen to 23, 79
MOV BH, 0; use color 0 to fill
INT 0x10; call 10h interrupt
RET
Begin_boot:
Call CLRSCR; first clear screen
MOV BP, Bootmesg; provide string address
Call print_mesg; output information
Call get_key; waiting for the user to press any one
BITS 16
Call clrsCr; clear screen
MOV AX, 0xB800; make GS point to display memory
MOV GS, AX; display a brown A in real mode
MOV Word [GS: 0], 0x641; Display
Call get_key; call get_key waiting for the user to press any one
MOV BP, PM_MESG; set string pointer
Call print_mesg; call print_mesg subroutine
Call get_key; wait button
Call clrsCr; clear screen
CLI;
LGDT [GDTR]; loading GDT
MOV Eax, Cr0
OR Al, 0x01; Set the protection mode bit
MOV CR0, EAX; send changes to the word to the control register
JMP CodeSel: Go_PM
BITS 32
Go_PM:
Mov AX, Datasel
MOV DS, AX; initialize DS and ES to point to data segments
Mov ES, AX
MOV AX, VIDEOSEL; Initializing GS, pointing it to display memory
MOV GS, AX
MOV Word [GS: 0], 0x741; display a white character A in the protection mode
Spin: JMP Spin; cycle
BITS 16
GDTR:
DW GDT_END-GDT-1; GDT length
DD GDT; GDT physical address
GDT
NULLSEL EQU $ -Gdt; $ pointing to the current location, so nullsel = 0H
GDT0; empty descriptor
DD 0
DD 0; all segment descriptors are 64-bit
CodeSel EQU $ -GDT; this is 8h is also the second descriptor of GDT
Code_GDT
DW 0x0fffffff; segment descriptor boundary is 4GB
DW 0x0000
DB 0x00
DB 0x09A
DB 0x0cf
DB 0x00
Datasel Equ $ -GDT
Data_GDT
DW 0x0ffff
DW 0x0000
DB 0x00
DB 0x092
DB 0x0cf
DB 0x00
Videoel EQU $ -GDT
DW 3999
DW 0x8000; base address is 0xB8000
DB 0x0B
DB 0x92
DB 0x00
DB 0x00
GDT_END
Times 510 - ($ - $$) DB 0
DW 0x0AA55
The above code exists in a file named abc.asm, uses the command NASM ABC.ASM, will give a file called ABC. Then insert the floppy disk, enter the command: DD if = ABC of = / dev / fd0. This command will write the file ABC into the first sector of the floppy disk. Then reboot the system, you will see the following information:
* Oric ..............
* A (brown)
* Switch to protected mode ....
* A (white)
Interpretation of the code
All of the code is given, and I will explain some of the above code.
◆ Use functions
Here is a description of some functions in the code:
Print_mesg This subroutine uses the feature of the BIOS interrupt 10h, which is to write a string to the screen. Property control is achieved by sending different values to some registers. Interrupt 10h is used in a variety of string operations, we send the subunit number 13H to the AH, which is used to specify a string to print. 0 in the Al register illustrates the starting position returned by the cursor, 0 indicates that the cursor returns to the next row after the call function. If Al is 1 indicates that the cursor is located at the last character.
The memory is divided into a few pages, only one page can be displayed at the same time. BH indicated by the page; BL indicates the color of the character to display; CX indicates the length of the string; DX refers to the position of the cursor (ie the starting row and column). After all relevant registers are initialized, you can call the BIOS interrupt for 10h.
Get_key uses the sub-function 00h of the interrupt 16h to get the next character from the screen.
The CLRSCR This function uses another sub-function of the interrupt 10h for 06h, which is used to output the start-up screen. When it is initialized, it is sent to Al. Registers CX and DX indicate the screen range of the screen to clear, in this case the entire screen. The register BH indicates the color of the screen fill, in this case.
◆ Other content
The program starts to be a short jump instruction, jump to the begin_boot. In the real mode, you print a brown "A" here and set a GDT. Switch to the protection mode and print a white "A". Both models are used by their own addressing methods.
In the real mode, use the segment register GS indicate the memory location, we use the CGA graphics card (the default base address is 0xB8000). Is it a 0 in the code? No, it will provide an additional 0 because of the real mode. This approach is also inherited by 80386. A ASCII is 0x41, 0x06 indicates a brown character. This display will continue until any key is pressed. Below you want to display a sentence on the screen, tell the user that the following is right away.
Start to protection mode, do not want to have an interrupt in the time of switching, so close all interrupts (using the CLI). Then initialize GDT. During the entire switching process, 4 descriptors were initialized. These descriptors initialize the display segments for the code segment (Code_GDT), data, and stack segment, and to access the memory. In addition, an empty descriptor will be initialized.
The base address of the GDT should be loaded into the GDTR system register. The first word of the GDTR segment is loaded is the size of the GDT, and the base address is loaded in the next double word. Then, the LGDT command loads the GDT segment into the GDTR register. Now there is all preparations for switching to the protection mode. The last thing is to put the PE bit of the CR0 register 1. However, even this is not under the state of protection mode.
After setting the PE bit, you will also need to clear the processor instruction prefetch queue by performing JMP instructions. In 80386, it is always taken out from memory before using the instruction, and decoding and addressing it. However, after entering the protection mode, prefetching command information (it is still in the real address mode) is invalid. The purpose of using JMP instructions is to force the processor to give up invalid information.
Now, it is already in the protection mode. So, how do I detect under the protection mode status? Let's take a look at this white letter A on the screen. Here, the data segment selector (DATASE1) is used to initialize the data segment and the additional segment, and the GS is initialized using the display segment selector (videose1). The character "a" of the notice "A" Its ASCII value and attribute are located at [GS: 0000], which is B8000: 0000. The loop statement makes the character are always displayed on the screen until the system is restarted.
The next step