OPERATING SYSTEM #09
Hi friends welcome back this is the Ninth part of the series…………
User mode is now almost within our reach, there are just a few more steps required to get there. Although these steps might seem easy they way they are presented in this chapter, they can be tricky to implement, since there are a lot of places where small errors will cause bugs that are hard to find.
So, we have already gone over a several steps in user mode, and now we need to go over a few more to get there.
Segments for User Mode: We need to add two more segments to the GDT to enable user mode, like we did in the segmentation part.
Here it allows code to execute in PL3.
Setting Up For User Mode
- Code, data, and stack all have their own page frames. For the time being, allotting one page frame for the stack and enough page frames to fit the program’s code is sufficient.
- The binary from the GRUB module has to be copied to the page frames used for the programs code.
- To map the above page frames into memory, you’ll require a page directory and page tables. Because the code and data should be mapped in at 0x00000000 and increasing, and the stack should start just below the kernel, at 0xBFFFFFFB, and increase towards lower addresses, at least two page tables are required. To allow PL3 access, the U/S flag must be set.
Entering User Mode
Executing an iret or lret instruction — interrupt return or long return, respectively — is the primary option to run code with a lower privilege level than the current privilege level (CPL). To enter user mode we set up the stack as following:
[esp + 16] ss ; the stack segment selector we want for user mode
[esp + 12] esp ; the user mode stack pointer
[esp + 8] eflags ; the control flags we want to use in user mode
[esp + 4] cs ; the code segment selector
[esp + 0] eip ; the instruction pointer of user mode code to execute
From the stack iret will then read these values and fill in the appropriate registers. we need to change to the page directory we setup for the user mode process before we execute iret. It is wise to note that to continue executing kernel code after we’ve switched PDT, the kernel needs to be mapped in. One approach to do this is to create a separate PDT for the kernel that maps all data above 0xC0000000 and merge it with the user PDT which only maps below 0xC0000000, when switching.
Interrupts cannot be enabled once user mode is entered if interrupts are disabled upon entering user mode. Because the assembly code instruction iret sets the register eflags to the matching value on the stack, setting the IF flag in the eflags entry on the stack will enable interrupts in user mode.
The value eip on the stack should point to the user code’s entry point, which in our instance is 0x00000000. The value esp on the stack should be 0xBFFFFFFB, which is where the stack begins (0xC0000000–4).
When using iret to enter PL3, the RPL(The Requested Privilege Leve) of cs and ss should be 0x3. Bellow code is one the example of it:
USER_MODE_CODE_SEGMENT_SELECTOR equ 0x18
USER_MODE_DATA_SEGMENT_SELECTOR equ 0x20
mov cs, USER_MODE_CODE_SEGMENT_SELECTOR | 0x3
mov ss, USER_MODE_DATA_SEGMENT_SELECTOR | 0x3
We can execute the iret now.
Using C for User Mode Programs
The produced code arrangement in C language is more unpredictable, and the entry point, main, may not be at binary offset 0 in the binary. One popular solution is to place a few assembly code lines at offset 0 that call main:
After that the bellow given code an example of a linker script that places these instructions first in executable:
OUTPUT_FORMAT("binary") /* output flat binary */ SECTIONS
. = 0; /* relocate to address 0 */ .text ALIGN(4):
} .data ALIGN(4):
} .rodata ALIGN(4):
We need the following GCC flags when compile user programs:
The followings flags need to be used for linking:
-T option tell the linker to use the linker script link.ld .
Now we successfully covered user mode in os part.
I hope everyone understands this blog, and I’ll see you in the next one. Keep reading!!
— — — — — — — — — — THANK YOU — — — — — — — — —