I have the following code performing task switch to jump to user mode:
Code:
global jump_usermode
extern usermode
jump_usermode:
mov ax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push dword 0x23
push eax
pushfd
;; make sure NT flag is set
or word [esp], 0x4000
popfd
pushfd
push 0x1B
push usermode
iret
My TSS is initialized like this (setting back link field to the TSS segment selector):
Code:
tss = (struct tss_entry){0};
tss.link = 0x28;
tss.ss0 = 0x10 | 0;
tss.esp0 = (uint32_t)kernel_int_stack_end;
ltr();
where ltr() basically executes ltr instruction with 0x28 segment selector.
However after landing in "usermode" function I still can execute privileged instruction like cli and I still can read cs segment selector equals 0x8.
Code:
void usermode(void) {
asm volatile ("cli");
asm volatile("int $0x80");
while(1);
}
I can not imagine why this happens, but basically iret with values loaded for Ring 3 has no effect at all.
Thank you for pointing where should I search it.
P.S.:
My GDT descriptor for TSS is defined as:
Code:
gdt_fill_entry(5, (uintptr_t)&tss, sizeof(tss) - 1,
(1 << 0) | (1 << 3) | (1 << 7),
0x00);
Where the third field is the limit, the fourth is the access byte, and the fifth are Flags (0x00).