Page 1 of 2
Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Thu Jan 23, 2020 2:14 pm
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
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 2:11 am
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.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 6:34 am
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
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 7:39 am
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
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 7:49 am
by iansjack
I believe your multiboot boot module should be an elf object file rather than a raw binary.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 8:58 am
by nielsd
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Fri Jan 24, 2020 11:02 pm
by nullplan
Your jmp_tomod function references GDT descriptors four and five, but you only have three in the GDT.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 2:56 am
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.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 6:38 am
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
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 8:56 am
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.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 10:46 am
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
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 12:42 pm
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.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 2:53 pm
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.
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sat Jan 25, 2020 3:19 pm
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!
Re: Jumping to GRUB module adress causes rpl !=CPL in bochs
Posted: Sun Jan 26, 2020 2:55 am
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);
}