OSDev.org https://forum.osdev.org/ |
|
Trying to port OS (IDT) to long mode https://forum.osdev.org/viewtopic.php?f=1&t=33587 |
Page 1 of 2 |
Author: | deleted8917 [ Sat Mar 16, 2019 10:56 am ] |
Post subject: | Trying to port OS (IDT) to long mode |
Hi again. I decided that I should adapt my OS to work in long mode (64 bits), before it grows more and becomes more complicated (I don't want to regret it later). The bootloader works well, now I try to port the IDT (which in part is written in assembly, 50% C and 50% NASM) I'm just getting troubles in assembly (I'm not very good at assembly) I already changed the IDT structure to the AMD64 one Code: idt.inc:320: error: instruction not supported in 64-bit mode idt.inc:321: error: instruction not supported in 64-bit mode idt.inc:336: error: instruction not supported in 64-bit mode idt.inc:337: error: instruction not supported in 64-bit mode idt.inc:475: error: instruction not supported in 64-bit mode idt.inc:476: error: instruction not supported in 64-bit mode idt.inc:494: error: instruction not supported in 64-bit mode idt.inc:495: error: instruction not supported in 64-bit mode idt.inc Code: global idt_load extern fault_handler extern idtp extern kputs extern k_readkb extern getch extern cmd_shell ; Loads the IDT defined in '_idtp' into the processor. idt_load: lidt [idtp] ret global isr0 global isr1 global isr2 global isr3 global isr4 global isr5 global isr6 global isr7 global isr8 global isr9 global isr10 global isr11 global isr12 global isr13 global isr14 global isr15 global isr16 global isr17 global isr18 global isr19 global isr20 global isr21 global isr22 global isr23 global isr24 global isr25 global isr26 global isr27 global isr28 global isr29 global isr30 global isr31 global isr128 %macro pushall 0 push rax push rcx push rdx push rbx push rbp push rsi push rdi %endmacro %macro popall 0 pop rax pop rcx pop rdx pop rbx pop rbp pop rsi pop rdi %endmacro isr0: cli push byte 0 push byte 0 jmp isr_common_stub isr1: cli push byte 0 push byte 1 jmp isr_common_stub isr2: cli push byte 0 push byte 2 jmp isr_common_stub isr3: cli push byte 0 push byte 3 jmp isr_common_stub isr4: cli push byte 0 push byte 4 jmp isr_common_stub isr5: cli push byte 0 push byte 5 jmp isr_common_stub isr6: cli push byte 0 push byte 6 jmp isr_common_stub isr7: cli push byte 0 push byte 7 jmp isr_common_stub isr8: cli push byte 8 jmp isr_common_stub isr9: cli push byte 0 push byte 9 jmp isr_common_stub isr10: cli push byte 10 jmp isr_common_stub isr11: cli push byte 11 jmp isr_common_stub isr12: cli push byte 12 jmp isr_common_stub isr13: cli push byte 13 jmp isr_common_stub isr14: cli push byte 14 jmp isr_common_stub isr15: cli push byte 0 push byte 15 jmp isr_common_stub isr16: cli push byte 0 push byte 16 jmp isr_common_stub isr17: cli push byte 0 push byte 18 jmp isr_common_stub isr18: cli push byte 0 push byte 18 jmp isr_common_stub isr19: cli push byte 0 push byte 19 jmp isr_common_stub isr20: cli push byte 0 push byte 20 jmp isr_common_stub isr21: cli push byte 0 push byte 21 jmp isr_common_stub isr22: cli push byte 0 push byte 22 jmp isr_common_stub isr23: cli push byte 0 push byte 23 jmp isr_common_stub isr24: cli push byte 0 push byte 24 jmp isr_common_stub isr25: cli push byte 0 push byte 25 jmp isr_common_stub isr26: cli push byte 0 push byte 26 jmp isr_common_stub isr27: cli push byte 0 push byte 26 jmp isr_common_stub isr28: cli push byte 0 push byte 28 jmp isr_common_stub isr29: cli push byte 0 push byte 29 jmp isr_common_stub isr30: cli push byte 0 push byte 30 jmp isr_common_stub isr31: cli push byte 0 push byte 31 jmp isr_common_stub ; Int 80h ; Registers: ; eax = service, ecx isr128: cli push byte 0 push byte 80 cmp rax, 1 ; Service 1 (terminate program) je .terminate_prg cmp rax, 3 ; Service 3 (read keyboard) je .read_srv cmp rax, 4 ; Service 4 (prints text) je .write_srv jmp .end .terminate_prg: ; We don't have processes yet, because we still in ; monotasking. Just return to the command shell call cmd_shell jmp isr_common_stub jmp .end .read_srv: xor rax, rax push rdx push rcx call k_readkb pop rdx pop rcx jmp isr_common_stub jmp .end .write_srv: xor rax, rax push rcx call kputs pop rcx jmp isr_common_stub .end: isr_common_stub: pushall push ds push es push fs push gs mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov rax, rsp push rax mov rax, fault_handler call rax pop rax pop gs pop fs pop es pop ds popall add rsp, 8 iretq global irq0 global irq1 global irq2 global irq3 global irq4 global irq5 global irq6 global irq7 global irq8 global irq9 global irq10 global irq11 global irq12 global irq13 global irq14 global irq15 ; 32: IRQ0 irq0: cli push byte 0 push byte 32 jmp irq_common_stub ; 33: IRQ1 irq1: cli push byte 0 push byte 33 jmp irq_common_stub ; 34: IRQ2 irq2: cli push byte 0 push byte 34 jmp irq_common_stub ; 35: IRQ3 irq3: cli push byte 0 push byte 35 jmp irq_common_stub ; 36: IRQ4 irq4: cli push byte 0 push byte 36 jmp irq_common_stub ; 37: IRQ5 irq5: cli push byte 0 push byte 37 jmp irq_common_stub ; 38: IRQ6 irq6: cli push byte 0 push byte 38 jmp irq_common_stub ; 39: IRQ7 irq7: cli push byte 0 push byte 39 jmp irq_common_stub ; 40: IRQ8 irq8: cli push byte 0 push byte 40 jmp irq_common_stub ; 41: IRQ9 irq9: cli push byte 0 push byte 41 jmp irq_common_stub ; 42: IRQ10 irq10: cli push byte 0 push byte 42 jmp irq_common_stub ; 43: IRQ11 irq11: cli push byte 0 push byte 43 jmp irq_common_stub ; 44: IRQ12 irq12: cli push byte 0 push byte 44 jmp irq_common_stub ; 45: IRQ13 irq13: cli push byte 0 push byte 45 jmp irq_common_stub ; 46: IRQ14 irq14: cli push byte 0 push byte 46 jmp irq_common_stub ; 47: IRQ15 irq15: cli push byte 0 push byte 47 jmp irq_common_stub extern irq_handler irq_common_stub: pushall push ds ; Can't push segment register directly in x64 push es push fs push gs mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov rax, rsp push rax mov rax, irq_handler call rax pop rax pop gs pop fs pop es pop ds popall add rsp, 8 iretq The problem is that we can't push segment registers directly, we need to move it to an register, and then push it. But I'm using all the registers. And then I need to pop from stack the segment registers! Porting this is more than just replacing "e" with "r". Thank for you patience. |
Author: | bzt [ Sat Mar 16, 2019 11:37 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
Hi, I don't know your source file entirely, but you have errors in pairs of lines with gaps. That suggests it's not the segment register push failing, but the "push byte" ones. You can't push a byte in 64 bit mode only 64 bit values. Why do you need the segment registers anyway? In long mode there's no segmentation it uses a flat memory modell. The only purpose is to specify access and CPL in case of CS; base and limit is not used by the CPU at all. You can set ES, DS to a user readable, writable but not executable segment and happily use that from kernel mode too. No need to change them, ever. You should not need to use FS/GS, but if you really do, see the SWAPGS instruction instead of pushing/poping selectors on the stack. Finally CS is pushed by the CPU on gate entry and popped by IRETQ, so no need to manually push/pop that either. Cheers, bzt |
Author: | deleted8917 [ Sat Mar 16, 2019 1:08 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
Thanks for replying. I'm getting "bootg.o: file not recognized: File format not recognized" error from the linker. bootg.asm: Code: %include "gdt.inc" %include "idt.inc" global start extern kernelmain extern kputs extern long_mode_start bits 32 ; This is the GRUB multiboot header section .multiboot align 4 mboot: MULTIBOOT_PAGE_ALIGN equ 1 << 0 MULTIBOOT_MEMORY_INFO equ 1 << 1 MULTIBOOT_VIDEO_MODE equ 1 << 2 MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) dd MULTIBOOT_HEADER_MAGIC dd MULTIBOOT_HEADER_FLAGS dd MULTIBOOT_CHECKSUM section .text check_cpuid: pushfd pop eax mov ecx, eax ; Flip the ID bit xor eax, 1 << 21 ; Copy EAX to FLAGS via the stack push eax popfd ; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported) pushfd pop eax ; Restore FLAGS from the old version stored in ECX (i.e. flipping the ; ID bit back if it was ever flipped). push ecx popfd ; Compare EAX and ECX. If they are equal then that means the bit ; wasn't flipped, and CPUID isn't supported. cmp eax, ecx je .no_cpuid ret .no_cpuid: ;mov ecx, no_cpuid_str ;push ecx ;call kputs ;pop ecx cli hlt check_long_mode: ; test if extended processor info in available mov eax, 0x80000000 ; implicit argument for cpuid cpuid ; get highest supported argument cmp eax, 0x80000001 ; it needs to be at least 0x80000001 jb .no_long_mode ; if it's less, the CPU is too old for long mode ; use extended info to test if long mode is available mov eax, 0x80000001 ; argument for extended processor info cpuid ; returns various feature bits in ecx and edx test edx, 1 << 29 ; test if the LM-bit is set in the D-register jz .no_long_mode ; If it's not set, there is no long mode ret .no_long_mode: ;mov ecx, no_long_str ;push ecx ;call kputs ;pop ecx cli hlt set_up_page_tables: ; map first P4 entry to P3 table mov eax, p3_table or eax, 0b11 ; present + writable mov [p4_table], eax ; map first P3 entry to P2 table mov eax, p2_table or eax, 0b11 ; present + writable mov [p3_table], eax mov ecx, 0 .map_p2_table: ; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx mov eax, 0x200000 ; 2MiB mul ecx ; start address of ecx-th page or eax, 0b10000011 ; present + writable + huge mov [p2_table + ecx * 8], eax ; map ecx-th entry inc ecx ; increase counter cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped jne .map_p2_table ; else map the next entry ret enable_paging: ; load P4 to cr3 register (cpu uses this to access the P4 table) mov eax, p4_table mov cr3, eax ; enable PAE-flag in cr4 (Physical Address Extension) mov eax, cr4 or eax, 1 << 5 mov cr4, eax ; set the long mode bit in the EFER MSR (model specific register) mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr ; enable paging in the cr0 register mov eax, cr0 or eax, 1 << 31 mov cr0, eax ret start: mov esp, _sys_stack_t ; Check for CPUID support call check_cpuid call check_long_mode call set_up_page_tables call enable_paging lgdt [gdt64.pointer] jmp gdt64.code:long_mode_start section .data: ;no_cpuid_str: db "CPUID is not supported.", 0x0A, 0x0D, 0 ;no_long_str: db "Long mode (x86-64) not supported", 0x0A, 0x0D, 0 section .bss align 4096 p4_table: resb 4096 p3_table: resb 4096 p2_table: resb 4096 _sys_stack_b: resb 64 _sys_stack_t: I don't know if it is useful, but here is the file bootg.o output: Code: bootg.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped Just in case, link.ld: Code: /* The bootloader will look at this image and start execution at the symbol designated as the entry point. */ ENTRY(start) /* Tell where the various sections of the object files will be put in the final kernel image. */ SECTIONS { /* Begin putting sections at 1 MiB, a conventional place for kernels to be loaded at by the bootloader. */ . = 1M; /* First put the multiboot header, as it is required to be put very early early in the image or the bootloader won't recognize the file format. Next we'll put the .text section. */ .text BLOCK(4K) : ALIGN(4K) { *(.multiboot) *(.text) } /* Read-only data. */ .rodata BLOCK(4K) : ALIGN(4K) { *(.rodata) } /* Read-write data (initialized) */ .data BLOCK(4K) : ALIGN(4K) { *(.data) } /* Read-write data (uninitialized) and stack */ .bss BLOCK(4K) : ALIGN(4K) { *(COMMON) *(.bss) __endkernel = .; } } Ok, so I tried to use elf32 instead of elf64 in nasm flags, added an bits 64 in the start of idt.inc and gdt.inc. No more linker problems, but triple faults now. As spected. |
Author: | bzt [ Sun Mar 17, 2019 7:05 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
hextakatt wrote: Ok, so I tried to use elf32 instead of elf64 in nasm flags, added an bits 64 in the start of idt.inc and gdt.inc. No more linker problems, but triple faults now. As spected. You can't just mix those, you must know precisely in which mode the CPU is at a given time during boot, otherwise a fault is guaranteed.You need a pure 64 bit toolchain, generating elf64 with long mode instructions in the text sections. There's only one exception to this, the startup code, so you should only use "bits 32" in your multiboot code (you should have no "bits" directive anywhere else): Code: ; multiboot header with start32 as entry point This source, the C code (and everything) must be compiled as elf64, and you should not include idt.inc (as your boot up code mixes modes, but your idt must be purely 64 bit). You should define the idt descriptor (lidt's argument) as extern to access it and that's all. There should be absolutely no code in gdt.inc, only data, therefore "bits 64" should not matter there anyway, but if you have a "loadgdt()" function there then it must be compiled for the default 64 bit (so no "bits 64" needed in that case either).bits 32 start32: ; multiboot stub ; enable paging, switch to longmode jmp gdt64.code:start64 ; this jump is important as it loads CS and enables long mode bits 64 start64: ; set up long mode environment (stack, segments, lidt etc.) ; jump to 64 bit C code With the triple faults, use bochs debugger to figure out where that happened, but I'm sure this time it's the invalid mixing of CPU modes. Just a sidenote, not related to your issue, but your pushall does not store all registers. In long mode you also have GPRs r8 - r15. Cheers, bzt |
Author: | MichaelPetch [ Sun Mar 17, 2019 10:47 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
On top of the things BZT said, you are also pushing and popping in the same order. You need to pop in reverse order of what you pushed. The popall macro is backwards: Code: %macro pushall 0
push rax push rcx push rdx push rbx push rbp push rsi push rdi %endmacro %macro popall 0 pop rax pop rcx pop rdx pop rbx pop rbp pop rsi pop rdi %endmacro |
Author: | MichaelPetch [ Mon Mar 18, 2019 9:40 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
Can you make your non-functioning 64-bit code as it currently is available in a branch in your git repository? |
Author: | deleted8917 [ Wed Mar 20, 2019 7:24 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
I already commited. Here's the repo Sorry for late answer, I was busy. |
Author: | MichaelPetch [ Thu Mar 21, 2019 10:17 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
Your wipcode branch seems to be lacking files including(but not limited to) bootg.asm. |
Author: | deleted8917 [ Thu Mar 21, 2019 9:47 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
MichaelPetch wrote: Your wipcode branch seems to be lacking files including(but not limited to) bootg.asm. The missing files are now in the repo. Sorry |
Author: | bellezzasolo [ Fri Mar 22, 2019 4:26 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
Also, re the. triple faults, having a bochs trace would be helpful - you can see what exception was fired, and the register state at the time, not to mention the address. |
Author: | deleted8917 [ Fri Mar 22, 2019 7:10 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
bellezzasolo wrote: Also, re the. triple faults, having a bochs trace would be helpful - you can see what exception was fired, and the register state at the time, not to mention the address. I was also thinking about using the Bochs debugger, but since I started using GRUB and an iso file instead of img (floppy) I just can't get my OS to boot into Bochs. Bochs refuses the ISO file. But please, I don't want to introduce another problem, I have a ton of they. I will try to debug using GDB. EDIT: guess what gdb refuses to debug... |
Author: | pat [ Fri Mar 22, 2019 8:18 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
hextakatt wrote: bellezzasolo wrote: Also, re the. triple faults, having a bochs trace would be helpful - you can see what exception was fired, and the register state at the time, not to mention the address. I was also thinking about using the Bochs debugger, but since I started using GRUB and an iso file instead of img (floppy) I just can't get my OS to boot into Bochs. Bochs refuses the ISO file. But please, I don't want to introduce another problem, I have a ton of they. Getting Bochs to work would be very beneficial and save you a lot of time down the road. If you post your bochsrc we can help you sort that out, and that would help get this sorted out. |
Author: | MichaelPetch [ Fri Mar 22, 2019 10:16 pm ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
When I try to build your project there is a linker error. `bootg.o: file not recognized: File format not recognized` . It occurs because you build bootg.o with `@nasm -f elf64 -Fdwarf -g bootg.asm -o bootg.o` (you assemble to a 64-bit ELF object) and then later you use an i686 cross compiler to build an executable with Code: @i686-elf-gcc -std=c99 -nostartfiles -nostdlib -Tlink.ld -lgcc -o os.elf \ My question is this. Is this suppose to be a 64-bit kernel? If so, why aren't you using a 64-bit cross compiler like x86_64-elf-gcc ? You can't build 64-bit code with i686-elf-gcc.
bootg.o bootg2.o api_test.o kmain.o kernel/timer.o kernel/terminal.o kernel/pic.o kernel/serial.o kernel/idt.o kernel$ kernel/keyboard.o kernel/mouse.o kernel/panic.o kernel/cpu.o kernel/cmos.o kernel/fpc.o kernel/rtc.o |
Author: | deleted8917 [ Sat Mar 23, 2019 6:27 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
MichaelPetch wrote: When I try to build your project there is a linker error. `bootg.o: file not recognized: File format not recognized` . It occurs because you build bootg.o with `@nasm -f elf64 -Fdwarf -g bootg.asm -o bootg.o` (you assemble to a 64-bit ELF object) and then later you use an i686 cross compiler to build an executable with Code: @i686-elf-gcc -std=c99 -nostartfiles -nostdlib -Tlink.ld -lgcc -o os.elf \ My question is this. Is this suppose to be a 64-bit kernel? If so, why aren't you using a 64-bit cross compiler like x86_64-elf-gcc ? You can't build 64-bit code with i686-elf-gcc.bootg.o bootg2.o api_test.o kmain.o kernel/timer.o kernel/terminal.o kernel/pic.o kernel/serial.o kernel/idt.o kernel$ kernel/keyboard.o kernel/mouse.o kernel/panic.o kernel/cpu.o kernel/cmos.o kernel/fpc.o kernel/rtc.o I fixed the dumbest error, now I'm using x86_64-elf-gcc. But I'm getting error:no multiboot header found. I don't touched nothing. The multiboot header is aligned to the first 4 kb, as we can see in the linker file: Code: .text BLOCK(4K) : ALIGN(4K) { *(.multiboot) *(.text) } Code: section .multiboot
align 4 mboot: MULTIBOOT_PAGE_ALIGN equ 1 << 0 MULTIBOOT_MEMORY_INFO equ 1 << 1 MULTIBOOT_VIDEO_MODE equ 1 << 2 MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) dd MULTIBOOT_HEADER_MAGIC dd MULTIBOOT_HEADER_FLAGS dd MULTIBOOT_CHECKSUM |
Author: | MichaelPetch [ Sat Mar 23, 2019 9:09 am ] |
Post subject: | Re: Trying to port OS (IDT) to long mode |
The default page size is 2MiB with x86_64-elf-gcc so the bootloader ends up outside the first 8KiB. This is discussed on the Wiki. To fix this when linking to your executable you need to add -z max-page-size=4096. One other significant problem is that you are going to have problems with SIMD instructions (SSE, SSE2, SSE3 etc) when in your kernel. You are going to want to add the compiler options -mno-sse -mno-sse2 -mno-sse3 along with -mno-red-zone . The x86-64 System V ABI passes the first two parameters through RDI and RSI (In that order). You don't push EAX and EBX onto the stack when calling kernelmain. You also clobber EAX when setting the segment registers. You need to ensure the upper 32-bits of RSP are set to zero. Your long_mode_start should probably look like: Code: long_mode_start: In your `start` routine you clobber EAX and EBX that are passed by GRUB when you call your functions to do initializing. You are going to have to temporarily save them. You can do something like: start:; All segment registers to zero mov cx, 0 mov ss, cx mov ds, cx mov es, cx mov fs, cx mov gs, cx mov esp,esp ; Move ESP to itself will clear the upper 32 bits of RSP call idt_load ; Pass GRUB registers. RDI=1st parameter of kernelmain, RSI=2nd parameter mov edi, ebx mov esi, eax ; Calls kernel entry point call kernelmain ; Jumps to the end routine, thats halts the system jmp end Code: mov esp, _sys_stack_t This should get you as far as entering kernelmain. At this point you really need to start using a debugger.push eax push ebx ; Check for CPUID support call check_cpuid call check_long_mode call set_up_page_tables call enable_paging pop ebx pop eax lgdt [gdt64.pointer] jmp gdt64.code:long_mode_start Note: In 64-bit code if the destination of an instruction is a 32-bit register then the CPU automatically sets the upper 32-bits of the corresponding 64-bit register to zero. |
Page 1 of 2 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |