Page 1 of 1
How to enter userspace
Posted: Thu Feb 18, 2021 2:14 pm
by austanss
I've been working on my kernel for a bit now, and I really want to enter userspace.
I have a few functions down, and I believe my OS would last... at least a reasonable amount of time in userspace.
So, I know in order for me to return to ring 0 from ring 3, I need a TSS.
I don't plan on doing that quite yet, I just want to get into userspace.
I know I need entries in my GDT for user code/data. I don't know what the entries should look like, or differ from the kernel entries, or what order the GDT needs to be laid out in.
And then finally, what do I do to officiate my entry into userspace?
Re: How to enter userspace
Posted: Thu Feb 18, 2021 3:12 pm
by AndrewAPrice
rizxt wrote:So, I know in order for me to return to ring 0 from ring 3, I need a TSS.
I don't plan on doing that quite yet, I just want to get into userspace.
You probably want a way to syscall back into the kernel unless you map 0xb8000 to usermode. Are you in protected mode or long mode?
I found the TSS straight foward to work with in long mode. The most difficult part is splitting up the TSS's address + size to write it into the GDT. The only thing you'd need in the actual TSS right now is the stack pointer to use for when you enter an interrupt. But, I don't know if you actually need a TSS if you keep interrupts disabled.
rizxt wrote:I know I need entries in my GDT for user code/data. I don't know what the entries should look like, or differ from the kernel entries, or what order the GDT needs to be laid out in.
This is for long mode. I have 5 segments in my GDT (kernel + userspace, code + data), and a reference to the TSS.
Code: Select all
[GLOBAL Gdt64]
Gdt64:
; Invalid segment
DQ 0x0000000000000000 ; 0x0
; Kernel code: RW, executable, code/data segment, present, 64-bit, ring 0
DQ 0x00209A0000000000 ; 0x8
; Kernel data: RW, data, code/data segment, present, ring 0
DQ 0x0000920000000000 ; 0x10
; User data: RW, data, code/data segment, present, ring 3
DQ 0x0020F20000000000 ; 0x18
; User code: RW, executable, code/data segment, present, 64-bit, ring 3
DQ 0x0020FA0000000000 ; 0x20
[GLOBAL TSSEntry]
TSSEntry:
DQ 0x0000000000000000 ; 0x28
DQ 0x0000000000000000
rizxt wrote:And then finally, what do I do to officiate my entry into userspace?
Set your segments, then jump into it. See
Getting to Ring 3#Entering_Ring_3
Re: How to enter userspace
Posted: Thu Feb 18, 2021 3:21 pm
by austanss
AndrewAPrice wrote:rizxt wrote:So, I know in order for me to return to ring 0 from ring 3, I need a TSS.
I don't plan on doing that quite yet, I just want to get into userspace.
You probably want a way to syscall back into the kernel unless you map 0xb8000 to usermode. Are you in protected mode or long mode?
I found the TSS straight foward to work with in long mode. The most difficult part is splitting up the TSS's address + size to write it into the GDT. The only thing you'd need in the actual TSS right now is the stack pointer to use for when you enter an interrupt. But, I don't know if you actually need a TSS if you keep interrupts disabled.
rizxt wrote:I know I need entries in my GDT for user code/data. I don't know what the entries should look like, or differ from the kernel entries, or what order the GDT needs to be laid out in.
This is for long mode. I have 5 segments in my GDT (kernel + userspace, code + data), and a reference to the TSS.
Code: Select all
[GLOBAL Gdt64]
Gdt64:
; Invalid segment
DQ 0x0000000000000000 ; 0x0
; Kernel code: RW, executable, code/data segment, present, 64-bit, ring 0
DQ 0x00209A0000000000 ; 0x8
; Kernel data: RW, data, code/data segment, present, ring 0
DQ 0x0000920000000000 ; 0x10
; User data: RW, data, code/data segment, present, ring 3
DQ 0x0020F20000000000 ; 0x18
; User code: RW, executable, code/data segment, present, 64-bit, ring 3
DQ 0x0020FA0000000000 ; 0x20
[GLOBAL TSSEntry]
TSSEntry:
DQ 0x0000000000000000 ; 0x28
DQ 0x0000000000000000
rizxt wrote:And then finally, what do I do to officiate my entry into userspace?
Set your segments, then jump into it. See
Getting to Ring 3#Entering_Ring_3
Long mode. Also, reading that article, I noticed it uses IRET. The difference between x86 and x86_64 IRET is important, I believe. And it appears that article is written for x86.
Re: How to enter userspace
Posted: Thu Feb 18, 2021 3:27 pm
by kzinti
rizxt wrote:Long mode. Also, reading that article, I noticed it uses IRET. The difference between x86 and x86_64 IRET is important, I believe. And it appears that article is written for x86.
It's pretty much the same thing, but you use "iretq" instead of "iret", Also you don't need to set %fs and %gs (and likely don't want to).
x86_64:
Code: Select all
JumpToUserMode:
# rdi = user args
# rsi = entry point in user space
# rdx = user space stack
movq $0x1b, %rax # Selector 0x18 (User Data) + RPL 3
movw %ax, %ds
movw %ax, %es
# Build a fake iret frame
pushq %rax # Selector 0x18 (User Data) + RPL 3
pushq %rdx # User space stack
pushq $0x202 # rflags = interrupt enable + reserved bit
pushq $0x23 # Selector 0x20 (User Code) + RPL 3
pushq %rsi # Entry point in user space
iretq
Re: How to enter userspace
Posted: Thu Feb 18, 2021 4:11 pm
by austanss
Right, so with your help I managed to get CPL to 3, and iret to my userspace entrypoint. I now simply need to align my userspace code to it's own page, and then I can easily set it's page to user privileges.
Re: How to enter userspace
Posted: Thu Feb 18, 2021 5:52 pm
by foliagecanine
Just curious, is this the kernel, a program, or test code that you are trying to execute in userspace?
Re: How to enter userspace
Posted: Thu Feb 18, 2021 6:25 pm
by austanss
foliagecanine wrote:Just curious, is this the kernel, a program, or test code that you are trying to execute in userspace?
This is part of the kernel executable. I'm trying to get to userspace so I can implement syscalls + test them, and also implement a UI outside of ring 0.
Re: How to enter userspace
Posted: Fri Feb 19, 2021 11:55 am
by thewrongchristian
rizxt wrote:foliagecanine wrote:Just curious, is this the kernel, a program, or test code that you are trying to execute in userspace?
This is part of the kernel executable. I'm trying to get to userspace so I can implement syscalls + test them, and also implement a UI outside of ring 0.
Just don't do what I did, which was to return via iret having first pushed 0 for the eflags value. Ended up with no interrupts when running in user mode and didn't even notice until I started running CPU heavy user code