image from freepik.com

Hello Friends, This is the seventh part of my article series, about building my own OS as an experiment. If you already read my previous articles on this series I think it may be a help to understand this part.

User Mode

User mode is currently nearly inside our scope, there are only a couple of more advances needed to arrive. Albeit these means may appear to be simple in the manner in which they are introduced in this part, they can be interesting to carry out, since there is a great deal of where little mistakes will cause messes with that are elusive.

Segments for User Mode

To enable user mode we need to add two more segments to the GDT. They are very similar to the kernel segments we added when we set up the GDT in the chapter about segmentation:

The segment descriptors are needed for user mode.IndexOffsetNameAddress rangeTypeDPL30x18user code segment0x00000000 - 0xFFFFFFFFRXPL340x20user data segment0x00000000 - 0xFFFFFFFFRWPL3

The difference is the DPL, which now allows code to execute in PL3. The segments can still be used to address the entire address space, just using these segments for user-mode code will not protect the kernel. For that we need paging.

Setting Up For User Mode

There are a few things every user-mode process needs:

Page frames for code, data, and stack. Right now it does the trick to allot a one-page outline for the stack and enough page edges to fit the program’s code. Try not to stress over setting up a stack that can be developed and recoil now, center around getting a fundamental execution work first.

Binary from the GRUB module has to be copied to the page frames used for the program's code.

A page index and page tables are expected to plan the page outlines portrayed above into memory. No less than two-page tables are required, in light of the fact that the code and information ought to be planned in at 0x00000000 and expanding, and the stack should begin just underneath the bit, at 0xBFFFFFFB, developing towards lower addresses. The U/S banner must be set to permit PL3 access.

It might be convenient to store this information in a struct representing a process. This process struct can be dynamically allocated with the kernel’s malloc function.

The best way to execute code with a lower advantage level than the current advantage level (CPL) is to execute an iret or lret guidance — intrude on return or long return, individually.

To enter client mode we set up the stack as though the processor had raised a between advantage level intrude. The stack should resemble the accompanying:

[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

The guidance iret will then, at that point, read these qualities from the stack and fill in the relating registers. Before we execute iret we need to change to the page catalog we arrangement for the client mode measure. Recall that to keep executing piece code after we’ve exchanged PDT, the portion should be planned in. One approach to achieve this is to have a different PDT for the piece, which maps all information at 0xC0000000 or more, and blend it with the client PDT (which just guides underneath 0xC0000000) when playing out the switch. Recollect that actual location of the PDT must be utilized when setting the register cr3.

The register eflags contains a bunch of various banners, indicated in area 2.3 of the Intel manual [33]. Generally significant for us is the hinder empower (IF) banner. The gathering code guidance sti can’t be utilized in advantage level 3 for empowering intrudes. On the off chance that hinders are incapacitated when entering client mode, hinders can’t empowered once client mode is entered. Setting the IF banner in the eflags passage on the stack will empower hinders in client mode, since the get together code guidance iret will set the register eflags to the comparing esteem on the stack.

For the present, we ought to have intrudes on debilitated, as it requires somewhat more work to get between advantage level hinders to work appropriately (see the part “Framework calls”).

The worth eip on the stack should highlight the passage point for the client code — 0x00000000 for our situation. The worth esp on the stack ought to be the place where the stack begins — 0xBFFFFFFB (0xC0000000–4).

The qualities cs and ss on the stack ought to be the section selectors for the client code and client information fragments, separately. As we found in the division section, the most minimal two pieces of a portion selector is the RPL — the Requested Privilege Level. When utilizing iret to enter PL3, the RPL of cs and ss ought to be 0x3. The accompanying code shows a model:

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

The register ds, and the other data segment registers, should be set to the same segment selector as ss. They can be set the ordinary way, with the mov assembly code instruction.

Using C for User Mode Programs

At the point when C is utilized as the programming language for client mode programs, contemplate the construction of the document that will be the aftereffect of the assemblage.

The explanation we can utilize ELF [18] as the document design for the part executable is on the grounds that GRUB realizes how to parse and decipher the ELF record design. In the event that we carried out an ELF parser, we could aggregate the client mode programs into ELF pairs also. We leave this as an activity for the peruser.

One thing we can do to make it simpler to foster client mode programs is to permit the projects to be written in C, however incorporate them to level doubles rather than ELF pairs. In C the design of the produced code is more capricious and the section point, primary, probably won’t be at balanced 0 in the paired. One normal approach to work around this is to add a couple of get together code lines set at offset 0 which calls fundamental:

extern main

section .text
; push argv
; push argc
call main
; main has returned, eax is return value
jmp $ ; loop forever

If this code is saved in a file called start.s, then the following code show an example of a linker script that places these instructions first in executable (remember that start.s gets compiled to start.o):

OUTPUT_FORMAT("binary")    /* output flat binary */

SECTIONS
{
. = 0; /* relocate to address 0 */

.text ALIGN(4):
{
start.o(.text) /* include the .text section of start.o */
*(.text) /* include all other .text sections */
}

.data ALIGN(4):
{
*(.data)
}

.rodata ALIGN(4):
{
*(.rodata*)
}
}

Note: *(.text) will not include the .text section of start.o again.

However, with this content we can compose programs in C or constructing agent (or whatever other language that assembles to protest records linkable with ld), and it is not difficult to load and guide for the part (.rodata will be planned in as writeable).

When we compile user programs we want the following GCC flags:

-m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles
-nodefaultlibs

For linking, the followings flags should be used:

-T link.ld -melf_i386  # emulate 32 bits ELF, the binary output is specified
# in the linker script

The option -T instructs the linker to use the linker script link.ld.

well that’s it for this week, for more reference you can see the original one here.

--

--