Jumping to GRUB module adress causes rpl !=CPL in bochs

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

Jumping to module adress in multiboot header in GRUB causes load_seg_reg(SS): rpl != CPL. I don`t have userspace now and of course it is kernel space. Paging is disabled, if I enable it I have page fault. Obviously, rpl != CPL and not jumping to garbage exception means I reach that code. But why doesn`t it work? Module is compiled using nasm -fbin and just sets eax to 0xDEADBEEF. I believe that gdt and idt are doing fine to since everything else is working.
source: https://github.com/nergzd723/ethanium
Octocontrabass
Member
Member
Posts: 5578
Joined: Mon Mar 25, 2013 7:01 pm

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by Octocontrabass »

nergzd723 wrote:Module is compiled using nasm -fbin and just sets eax to 0xDEADBEEF.
Does it do anything else? The CPU isn't going to stop when it reaches the end of the file unless you tell it to.
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

Yes, jmp $ intended to stop the cpu. It seems like it`s something wrong with segments but I am sure that my GDT is okay
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by bzt »

Hi,
nergzd723 wrote:Yes, jmp $ intended to stop the cpu. It seems like it`s something wrong with segments but I am sure that my GDT is okay
From the Multiboot specification:
the OS image must not load any segment registers (even just reloading the same values!) until it sets up its own ‘GDT’.
I think you should set up GDT here and the segment registers before you make a call to kmain (even if that's just a temporary hardcoded GDT and not the final one). Thay way you'll have a known-to-be-good segment registers, regardless what the loader passed to you. According to the spec, Multiboot does not guarantee anything about privileges (for instance that RPL and CPL will match), it only guarantees base of 0 and limit of 0xFFFFFFFF, but that's all. As a matter of fact, the Multiboot spec states: "The exact values are all undefined".

Cheers,
bzt
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by iansjack »

I believe your multiboot boot module should be an elf object file rather than a raw binary.
User avatar
nielsd
Member
Member
Posts: 31
Joined: Sun Apr 05, 2015 3:15 pm

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nielsd »

After taking a quick look, this doesn't seem right: https://github.com/nergzd723/ethanium/b ... ty.asm#L20
osdev project, goal is to run wasm as userspace: https://github.com/kwast-os/kwast
nullplan
Member
Member
Posts: 1794
Joined: Wed Aug 30, 2017 8:24 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nullplan »

Your jmp_tomod function references GDT descriptors four and five, but you only have three in the GDT.
Carpe diem!
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

Thank you all for replies!
I think you should set up GDT here and the segment registers before you make a call to kmain (even if that's just a temporary hardcoded GDT and not the final one).
If I do that, the multiboot info would be wrong somewhy, magic is bad.
Then, I tried initializing GDT and IDT and set up syscall handler and nothing more, then jumping to app. I got error:

Code: Select all

check_cs: confronting code seg descriptor dpl > cpl, dpl=3, cpl=2
Does it mean that kernel runs at cpl2? It should be zero. Qemu just throw GPF.
And if I don`t have user code and user data segs, I get loading null selector error. I have no idea why is it wrong.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by bzt »

Hi,
nergzd723 wrote:Thank you all for replies!
I think you should set up GDT here and the segment registers before you make a call to kmain (even if that's just a temporary hardcoded GDT and not the final one).
If I do that, the multiboot info would be wrong somewhy, magic is bad.
Don't do anything complex that changes registers. Just a single "lgdt" instruction with a static GDT. Then you can use segments in your jmp_tomod.

Calling a function uses SS register, don't do that until you're sure you have a valid one. For example:

Code: Select all

; hardcoded temporary GDT until we set it up properly
            align       16
GDT_table:  dd          0, 0                ;null descriptor
            dd          0000FFFFh,008F9200h ;32 bit prot mode flat ds
            dd          0000FFFFh,00CF9A00h ;32 bit prot mode ring0 cs
GDT_value:  dw          GDT_value-GDT_table
            dd          GDT_table

start:
    cld                             ; Clear the direction flag for string operations
    mov esp, eax                    ; save eax in esp register which we will set anyway
    lgdt  [GDT_value]               ; set up segment registers
    mov ax, 8
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov eax, esp                    ; restore eax with magic
    mov esp, kernel_stack_top       ; Set up the stack
    ; ...
(note: this is just a simple example I wrote from memory, could be typos in it. You also need to tailor it to your needs. Some assembler needs "$8" etc. To set up CS as well, you'll need a far call to 16:kmain.)
nergzd723 wrote:Then, I tried initializing GDT and IDT and set up syscall handler and nothing more
Don't complicate your life, it's enough if you set up IDT later. Just focus on GDT.

Cheers,
bzt
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

Thank you, bzt! I`ve done something like that:

Code: Select all

start:
    cld                             ; Clear the direction flag for string operations
    call gdt_setup
    mov esp, kernel_stack_top       ; Set up the stack
    push eax;                       ; Push multiboot header
    push ebx;                       ; Push multiboot magic
    extern kmain
    call kmain                      ; Jump to kmain (never to return)

    cli                             ; We should not return here, but if we do:
    hlt
    jmp $                            ;   clear all interrupts and halt
gdt_setup:
    mov esp, eax ; save eax
    lgdt[GDT_V] ; set up hardcoded GDT
    mov ax, 8
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov eax, esp ; restore it
    jmp 0x10:.fl
.fl: 
    ret
align 16
GDT_T: 
    dd 0, 0 ;null descriptor
    dd 0000FFFFh, 008F9200h
    dd 0000FFFFh, 00CF9A00h
GDT_V:
    dw GDT_V-GDT_T
    dd GDT_T
Now I have triple fault, but I am sure that it even doesn`t reach kmain because COM1 is not initialized. QEMU says that I am trying to execute code outside of the RAM or the ROM. Ooops, forgot about ESP, where EAX is stored: it is 2BADB006, and eax originaly was 2BADB002.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by bzt »

nergzd723 wrote:Now I have triple fault
No wonder. I told you not to use "call". Inline the whole gdt_setup thing! Calling a function requires accessing SS as well as ESP, neither is at a known state, you should not rely on them. Also you're saving EAX in ESP inside the function body, so you lost the return address for sure.

Inlining that function will remove the stack dependency and with that it will solve your issue.

Code: Select all

start:
    cld                             ; Clear the direction flag for string operations
    mov esp, eax ; save eax
    lgdt[GDT_V] ; set up hardcoded GDT
    mov ax, 8
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    mov eax, esp ; restore it
    mov esp, kernel_stack_top       ; Set up the stack
    push eax;                       ; Push multiboot header
    push ebx;                       ; Push multiboot magic
    call 0x10:kmain                 ; Far call to kmain (never to return) sets up CS as well
I think far call works just like that (depends on Assembler). If not, then you have to use "db" to manually construct the instruction, or to show you a neat trick, you could also use

Code: Select all

    push eax;                       ; Push multiboot header
    push ebx;                       ; Push multiboot magic
    push .retaddr
    jmp 0x10:kmain
.retaddr:
But your assembler should have no problem with far call, so this workaround should not be needed.

Cheers,
bzt
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

Oops, forgot to say that I actually tried inlining that, but it just loops.
According to bochs:

Code: Select all

push ebp
mov ebp, esp
sub esp, 0x0000004
mov eax, dword ptr ss:[ebp+8]
mov word ptr ss:[ebp-20], ax
mov eax, dword ptr ss:[ebp-20]
mov edx, eax
in al, dx
mov byte ptr ss:[ebp-1], al
mov al, byte ptr ss:[ebp-1]
leave
ret
add esp, 0x00000004
movzx eax, al
and eax, 0x00000020
leave
ret
test eax, eax
jz .-9 (0x00100540)
call .-42 (0x0010051b)
push ebp
mov ebp, esp
push 0x000003fd
call .-469(0x00100353) 
Why does it just loop that? call -469 returns execution to beginning of it. But now we are running in 0010: selector according to bochs.
MichaelPetch
Member
Member
Posts: 797
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by MichaelPetch »

Can you update your git repo with the exact version of your code that is giving you troubles. The version you have in Git has missing build directory, usr.o seems to be in the wrong place and can't be found and even when linked there are a bunch of undefined references. I'd like to build your project and take a look.

Some of what bzt has told you is inaccurate about what you can do without loading your own GDT. He seems to be under the mistaken impression that you can't put things on the stack without setting your own SS (it is correct you do need to set ESP).

Although the GDTR may be invalid per the Multiboot spec you are able to use CS, DS, ES, SS, FS, and GS for all types of memory accesses as long as you don't attempt to reload any of those segment registers. You can't assume the selectors in those segment registers are a particular value (that is true). You are able to continue using the Multiboot segment registers because the CPU uses the value in the hidden descriptor cache which is only changed when a segment is reloaded with new values. If the CPU is using an invalid GDTR and you do load a value into a segment register the processor will likely fault.

Without your own GDT you can't reliably do FAR calls; FAR jmp; FAR Returns; POP to segment registers; IRET; or any mov instruction that updates a segment register etc. A side effect is without your own GDT you can't reliably do interrupts which would severely limit your OS.
nergzd723
Posts: 10
Joined: Thu Oct 24, 2019 7:01 am

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by nergzd723 »

missing build directory
The build directory is empty, just create it. Also to build it you'll need my toolchain in home directory.
https://github.com/nergzd723/i686-elf
You'll also need nasm. Have fixed usr.o location. And then it should build well. Bochsrc.txt in git repo isn't valid, but it just loads os.iso as cdrom.
But actually, at the time I am calling module I have a GDT. It loads gdt table and returns with new segment descriptor 0x10. Maybe GRUB puts the module where the GDT thinks data segment is located. So i need to move it to code segment? Thank you all for help!
MichaelPetch
Member
Member
Posts: 797
Joined: Fri Aug 26, 2016 1:41 pm
Libera.chat IRC: mpetch

Re: Jumping to GRUB module adress causes rpl !=CPL in bochs

Post by MichaelPetch »

nergzd723 wrote:Oops, forgot to say that I actually tried inlining that, but it just loops.
According to bochs:

Code: Select all

push ebp
mov ebp, esp
sub esp, 0x0000004
mov eax, dword ptr ss:[ebp+8]
mov word ptr ss:[ebp-20], ax
mov eax, dword ptr ss:[ebp-20]
mov edx, eax
in al, dx
mov byte ptr ss:[ebp-1], al
mov al, byte ptr ss:[ebp-1]
leave
ret
add esp, 0x00000004
movzx eax, al
and eax, 0x00000020
leave
ret
test eax, eax
jz .-9 (0x00100540)
call .-42 (0x0010051b)
push ebp
mov ebp, esp
push 0x000003fd
call .-469(0x00100353) 
Why does it just loop that? call -469 returns execution to beginning of it. But now we are running in 0010: selector according to bochs.
The sequence of code that is looping repeatedly is the while statement here:

Code: Select all

void put_serial(char a) {
   while (is_transmit_empty() == 0);   // Stall while transit is filled

   outb(COM1_BASE,a);
}
Post Reply