Page 1 of 2

Cant get interrupts to function in ring3 in my kernel

Posted: Mon Jul 22, 2024 12:05 pm
by overlook17385
Note: Some code has been changed since I has taken a few user's suggestions. I have uploaded updated code after the first one
Full code is uploaded at the very bottom.

I've recently been developing a small Kernel in assembly language (NASM).
I chose to do this because I believe assembly is much easier to maintain and read than C when it comes to low-level functionality.
The Kernel is for x86 computers. It currently only has functionality suitable for an old IBM Thinkpad, however I plan on adding more to it in the future.


So far, all of its functions seem to be working correctly, however I have not been able to successfully get interrupts working in user mode.

Right now the problem seems to be caused by either the TSS and/or TSS-descriptor or the routine to enter into user mode.

I apologize for unconventional methods used in code.


Here is what they look like:

The GDT, which has the TSS descriptor in it, looks like this:

Code: Select all

        GDT:
dq 0
;   System Reserved Segment
    SYS_CODE1:
dw 0x001f
dw 0x0
db 0x0
db 0b10011010
db 0b11001111
db 0x0
    SYS_DATA1:
dw 0x001f
dw 0x0
db 0x0
db 0b10010010
db 0b11001111
db 0x0

;   General Memory Segment
    GEN_CODE:
dw 0xdfff
dw 0x0020 ; base
db 0x00   ; base
db 0b11111010
db 0b11001111
db 0x00   ; base
    GEN_DATA:
dw 0xdfff
dw 0x0020 ; base
db 0x00   ; base
db 0b11110010
db 0b11001111
db 0x00   ; base

;   TSS Descriptor
tss_desc:
    dq 0
tss_desc_end:

        GDT_END:

gdt_desc:
    dw GDT_END - GDT - 1
    dd GDT

CSEG_1: equ SYS_CODE1 - GDT
DSEG_1: equ SYS_DATA1 - GDT

CSEG_G: equ GEN_CODE - GDT
DSEG_G: equ GEN_DATA - GDT
(at ln 1 in kernel/gdt.asm)

The TSS itself looks like this:

Code: Select all

; An Actual TSS
tss:
    dd 0        ; previous TSS
    dd 0x0010ffff ; ESP0
    dd 0x10 ; SS0
    ;dw 0
    times 0x15 dd 0
    dw 0
    dw tss_end - tss
    dd 0
tss_end:
(at ln 14 in kernel/sr.asm)

The routine to get into user mode looks like this:

Code: Select all

enter_usermode: ;;
    mov ax, (4 * 8) | 3 ; ring 3 data with bottom 2 bits set for ring 3
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax ; SS is handled by iret

	; set up the stack frame iret expects
	mov eax, esp
	push (4 * 8) | 3 ; data selector
	push eax ; current esp
	pushf ; eflags
	push (3 * 8) | 3 ; code selector (ring 3 code with bottom 2 bits set for ring 3)
	push 0x200000 ; instruction address to return to
	iret
(at ln 205 in kernel/sr.asm)


The function to setup the tss:

Code: Select all

set_tss_desc: ; Sets the TSS descriptor
; ecx   = base
; edx   = limit
; Finally got this thing working
;   after hours of researching
;   and thinking.

; STEPS OF SETTING THE TSS DESCRIPTOR:
;   Set WORD to the first 16 bits of Limit
;   Set WORD to the first 16 bits of Base
;   Set BYTE to the next byte of Base
;   Set BYTE to 0b10010001
;   Set BYTE to final hex digit of Limit + 0000
;   Set BYTE to final byte of Base
; STEPS OF RETRIEVING PORTIONS OF ADDRESSES:
;   For getting LIMIT[0..15]
;       Start at BIT 0 of LIMIT.
;       shl limit, 0
;       GET word limit
;   For getting BASE[0..15]
;       Start at BIT 0 of BASE.
;       shl base, 0
;       GET word base
;   For getting BASE[16..23]
;       Start at BIT 16 of BASE.
;       shl base, 16
;       GET byte base
;   For getting LIMIT[16..19]
;       Start at BIT 16 of LIMIT.
;       shl limit, 16
;       AL = GET byte limit
;       and al, 0b11110000
;   For getting BASE[24..31]
;       Start at BIT 24 of BASE.
;       shl base, 24
;       GET byte base
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;mov [tss_desc_base], ecx
    ;mov [tss_desc_limit], edx
    ;mov ecx, tss_desc_base
    ;mov edx, tss_desc_limit
    ; EDX=LIMIT
    ; ECX=BASE

    mov eax, tss_desc   ; Load the address of the TSS descriptor
    mov ecx, tss        ; Load the base address of the TSS
    mov edx, tss_end    ; Load the limit of the TSS
    sub edx, ecx

    ;;; ecx is base, set base to base
    ;;; edx is limit, set limit to ecx-end_addr


    mov ebx, tss_desc
    ;---------------------
    ;       LIMIT [15..0];
    mov word [ebx], dx
    ;---------------------
    ;---------------------
    ;       BASE [15..0] ;
    add ebx, 2
    mov word [ebx], cx
    ;---------------------
    ;---------------------
    ;       BASE [23..16];
    add ebx, 2
    shr ecx, 16
    mov byte [ebx], cl
    ;---------------------
    ;---------------------
    ;       ...          ;
    inc ebx
    mov byte [ebx], 0x89 ; 0b10001001
    ;---------------------
    ;---------------------
    ;       LIMIT [19..16]
    inc ebx
    shr edx, 16
    mov al, dl
    ;and al, 0b11110000
    mov byte [ebx], al
    ;---------------------
    ;---------------------
    ;       BASE [31..24]
    inc ebx
    shr ecx, 7
    mov byte [ebx], cl
    ;  ----- DONE -----
    ret
(at ln 106 in kernel/sr.asm)



I have tried printing the TSS descriptor to debug, and I have gotten some interesting results.
When I change the TSS descriptor, after jumping into userland I get a general protection fault, however, when I keep it the way it must be, it seems to continue fine, but interrupts do not work.

This is what displayed: 6C00178000890000

Routine to print the TSS Descriptor:

Code: Select all

disp_tss_desc: ; function to display the tss descriptor
    mov ebx, tss_desc
    mov esi, 0
.loop:
    mov al, [ebx]
    call byteout
    inc ebx
    inc esi
    cmp esi, 8
    jne .loop
    ret
(at ln 199 in kernel/sr.asm)

The function to set the stack portion (esp0) of the tss:

Code: Select all

tss_set_stack:
    pop esi
    mov eax, tss
    add eax, 4
    mov [eax], esp
    push esi
    ret
(at ln 78 in kernel/sr.asm)

The code that sets this whole thing up:

Code: Select all

call incrow ; go onto the next row of text to print
mov eax, setup4
call strout ; strout is the print function

mov ecx, tss
mov eax, tss
mov edx, tss_end
sub edx, eax
call set_tss_desc
call incrow

mov eax, msg_tss_desc ; "TSS Descriptor: "
mov bl, 0x07
call strout_color
call disp_tss_desc

call tss_set_stack ; set esp0
mov ax, 0x28
ltr ax
call loads ; Load a sector from after the Kernel
(at ln 289 in kernel/main.asm)

Full code is here as needed:
akern3.zip
(24.53 KiB) Downloaded 100 times
(included so that people who have downloaded this one can tell the difference)
akern3 (updated).zip
(24.96 KiB) Downloaded 77 times

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 12:12 pm
by iansjack
Don’t apologize for messy code - write code that isn’t messy.

I think you are making a big mistake by not using a high-level language, the big mistake that many beginners make. A good choice of language makes code so much more readable, both for yourself and for others, and so much easier to maintain.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 12:15 pm
by overlook17385
-erased-

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 12:18 pm
by overlook17385
iansjack wrote: Mon Jul 22, 2024 12:12 pm I think you are making a big mistake by not using a high-level language, the big mistake that many beginners make. A good choice of language makes code so much more readable, both for yourself and for others, and so much easier to maintain.
That was simply a small preference that I had for this project. I am fully aware of what I am doing.
I do this because I am prioritizing a certain amount of control over my Kernel code.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 12:23 pm
by iansjack
Fair enough.

But you are, IMO, making it more difficult for others to help you. And I’m not convinced that your code will be more efficient than well written C code. Modern compilers are extremely good at optimizing code.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 12:44 pm
by Octocontrabass
overlook17385 wrote: Mon Jul 22, 2024 12:05 pmassembly is much easier to maintain and read than C.
Most programmers will disagree with you, but it's your OS, you can do whatever you like.
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

;   System Reserved Segment
Did you intentionally set the limit of these segments to 0xF001F? Why are you using segments for anything other than switching between protection levels?
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

;   General Memory Segment
Did you intentionally set the base of these segments to 0x20? Why is the limit 0xFFFFFFFF?
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

    dd 0x10ffff ; ESP0
This stack pointer is outside your stack segment. This stack pointer is misaligned.
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

    times 0x17 dd 0
Setting the IOPB offset to 0 does not disable the IOPB!
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

	push eax ; current esp
You're switching to a different stack segment with a different base, so the stack will end up at a different address even though you're reusing the current value of ESP.
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

	pushf ; eflags
You probably want to push a new value for EFLAGS here instead of reusing the current value of EFLAGS.
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

set_tss_desc:
This function sets the descriptor limit to whatever value was in EDX when it was called, and it sets the base to the value that should have been the limit.

I haven't downloaded your code to see what else might be wrong.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 1:18 pm
by overlook17385
Octocontrabass wrote: Mon Jul 22, 2024 12:44 pm ...
Thank you. I have begun seeing to these issues, this will probably take a while though.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 7:47 pm
by overlook17385
Octocontrabass wrote: Mon Jul 22, 2024 12:44 pm
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

;   System Reserved Segment
Did you intentionally set the limit of these segments to 0xF001F? Why are you using segments for anything other than switching between protection levels?
overlook17385 wrote: Mon Jul 22, 2024 12:05 pm

Code: Select all

;   General Memory Segment
Did you intentionally set the base of these segments to 0x20? Why is the limit 0xFFFFFFFF?
I had meant to make the limit if the System Reserved segments to 0x1FFFFF, but I guess that wont work.
The limit on the other segments was set to 0xFFFFFF because I supposed it would allow for using the rest of the memory.

I believed that I was setting the base of the second segments to 0x200000.

As for the routine that sets up the TSS Descriptor, I fixed it this way:

Code: Select all

mov eax, tss_desc   ; Load the address of the TSS descriptor
    mov ecx, tss        ; Load the base address of the TSS
    mov edx, tss_end    ; Load the limit of the TSS
    sub edx, ecx

    ;;; ecx is base, set base to base
    ;;; edx is limit, set limit to ecx-end_addr


    mov ebx, tss_desc
    ;---------------------
(ln 152 in kernel/sr.asm)

And the TSS itself:

Code: Select all

tss:
    dd 0        ; previous TSS
    dd 0x10ffff ; ESP0
    dd 0x10 ; SS0
    ;dw 0
    times 0x15 dd 0
    dd tss_end - tss
    dd 0
tss_end:

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 8:36 pm
by Octocontrabass
overlook17385 wrote: Mon Jul 22, 2024 7:47 pmI had meant to make the limit if the System Reserved segments to 0x1FFFFF, but I guess that wont work.
You can make it work if you use the granularity bit. But why do you want it to work? Why not use paging instead of segmentation?
overlook17385 wrote: Mon Jul 22, 2024 7:47 pmThe limit on the other segments was set to 0xFFFFFF because I supposed it would allow for using the rest of the memory.
It would allow access to all memory, not just memory above the base address. Is that really what you want?
overlook17385 wrote: Mon Jul 22, 2024 7:47 pm

Code: Select all

    dd 0x10ffff ; ESP0
This stack pointer is still misaligned.
overlook17385 wrote: Mon Jul 22, 2024 7:47 pm

Code: Select all

    dd tss_end - tss
This location is the LDTR value, not the IOPB offset. The IOPB offset is only 16 bits.

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 9:37 pm
by overlook17385
Octocontrabass wrote: Mon Jul 22, 2024 8:36 pm This stack pointer is still misaligned.
I believe I'm not familiar. Can't the stack be set by the developer? What would be the location of the stack segment?

Edit: I think I've figured it out.

Code: Select all

dd 0x0010ffff ; ESP0

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 10:00 pm
by overlook17385
Alright, after some extra tweaking, here are the changes I've made:

For the TSS:

Code: Select all

tss:
    dd 0        ; previous TSS
    dd 0x0010ffff ; ESP0
    dd 0x10 ; SS0
    ;dw 0
    times 0x15 dd 0
    dw 0
    dw tss_end - tss
    dd 0
tss_end:
For the GDT:

Code: Select all

;   System Reserved Segment
    SYS_CODE1:
dw 0x001f
dw 0x0
db 0x0
db 0b10011010
db 0b11001111
db 0x0
    SYS_DATA1:
dw 0x001f
dw 0x0
db 0x0
db 0b10010010
db 0b11001111
db 0x0

;   General Memory Segment
    GEN_CODE:
dw 0xdfff
dw 0x0020 ; base
db 0x00   ; base
db 0b11111010
db 0b11001111
db 0x00   ; base
    GEN_DATA:
dw 0xdfff
dw 0x0020 ; base
db 0x00   ; base
db 0b11110010
db 0b11001111
db 0x00   ; base

Re: Cant get usermode to function in my kernel

Posted: Mon Jul 22, 2024 10:21 pm
by Octocontrabass
overlook17385 wrote: Mon Jul 22, 2024 9:37 pmCan't the stack be set by the developer?
This is your kernel's stack! You are the developer!
overlook17385 wrote: Mon Jul 22, 2024 9:37 pmEdit: I think I've figured it out.

Code: Select all

dd 0x0010ffff ; ESP0
You didn't change anything. It's still exactly the same as it was before. It's still misaligned.
overlook17385 wrote: Mon Jul 22, 2024 10:00 pm

Code: Select all

    dw tss_end - tss
It's closer, but still in the wrong place.
overlook17385 wrote: Mon Jul 22, 2024 10:00 pm

Code: Select all

;   System Reserved Segment
Now the limit is 0xF001FFFF. Why are you using segmentation instead of paging?
overlook17385 wrote: Mon Jul 22, 2024 10:00 pm

Code: Select all

;   General Memory Segment
The base is still 0x20. The limit is 0xFDFFFFFF. Why are you using segmentation instead of paging?

Re: Cant get usermode to function in my kernel

Posted: Tue Jul 23, 2024 4:56 am
by rdos
I agree that x86 assembly is more readable than C for most low level code. Also, you don't need to port the C library if you don't rely on C in the kernel. Actually, the work needed to be able to use C in a kernel is a lot more than what can be saved by coding in C instead of assembly. Essentially, with assembly you know what the CPU will do, with C you can only guess.

Re: Cant get usermode to function in my kernel

Posted: Tue Jul 23, 2024 6:14 am
by eekee
rdos wrote: Tue Jul 23, 2024 4:56 amEssentially, with assembly you know what the CPU will do, with C you can only guess.
I recall Chuck Moore ditching assembly because MASM moved his code around! :D He switched to machine code, which I guess is all very well for someone who probably learned to program before the first assembler was developed. But now we have CPUs which are more emulator than fact, optimizing on the fly, having over 100 hidden registers which are used in a functional-programming manner rather than the rigorously procedural nature of their machine code. If we can trust such CPUs to behave as expected, why can't we trust C too? Personally, I lose trust when specifications are so complex, I find correct understanding harder to achieve. I've also chosen to switch to a language I find easier to understand because I want to read all my library code. It still took quite some work to find the lowest level of one feature, (memory allocation,) and the compiler source remains challenging. It's probably noteworthy that the first people to code an OS in a high-level language were also the creators and developers of that language -- Ken Thompson and Dennis Ritchie.

Re: Cant get usermode to function in my kernel

Posted: Tue Jul 23, 2024 12:06 pm
by thewrongchristian
overlook17385 wrote: Mon Jul 22, 2024 12:18 pm
iansjack wrote: Mon Jul 22, 2024 12:12 pm Don’t apologize for messy code - write code that isn’t messy.

I think you are making a big mistake by not using a high-level language, the big mistake that many beginners make. A good choice of language makes code so much more readable, both for yourself and for others, and so much easier to maintain.
That was simply a small preference that I had for this project. I am fully aware of what I am doing.
I do this because I am prioritizing speed and efficiency in my Kernel.
If this was 50 years ago, you may have a point. But it's 2024, and modern compilers would certainly outperform my hand-crafted assembler, and be more correct to boot.

Don't underestimate the effort it will take to verify your code in assembler.

I would suggest that for anything other than very simple leaf functions, the compiler will outperform anything you can write.
overlook17385 wrote: I decided to do this because assembly is much easier to maintain and read than C.
Doesn't really tally with:
overlook17385 wrote: I apologize for messy code.