after some time I started again on working on some kernel experiments. Currently I am trying to implement a loader that sets up long mode and then jumps to an 64 elf module loaded by GRUB (just like it is described here: http://wiki.osdev.org/Creating_a_64-bit ... ate_loader)
After my loader was put into memory by Grub I setup a temporary GDT and start loading the elf. After that I check if long mode is supported (cpuid...).
Then I setup paging for long mode (not enabling it yet), enable PAE, switch to compatibility mode and then enable paging. Then I call my enter64BitKernel method (which sets a new Long Mode GDT and then jumps to the kernel).
Everything works fine (no faults). When I put only 32bit code in my 64bit kernel module it executes just fine (so I assume my elf loading is working probably). But as soon as I put any 64bit instructions in my 64 bit kernel it doesn't work and eventually triple faults.
So here is some code:
loader.c:
Code: Select all
disablePaging();
cout<<"Setup 64 bit paging\n";
setupPaging64();
cout<<"Paging setup completed - not enabled\n";
enablePAE();
cout<<"PAE enabled\n";
switchIA32e();
cout<<"IA32 compatibltiy mode enabled\n";
enablePaging64();
cout<<"Paging enabled\n";
gdt64[0].set(0x0);
gdt64[1].set(0x0, 0xFFFFFFFF, 0x9A);
gdt64[2].set(0x0, 0xFFFFFFFF, 0x92);
if(gdt64[1].isLongMode() && gdt64[2].isLongMode()) {
cout<<"long mode set\n";
}
GDTDescriptor gdtDescriptor64(GDT_ENTRIES_NUM, &gdt64[0]);
cout<<"Loader finished, entering 64 bit kernel\n";
enter64BitKernel(&gdtDescriptor64, kernelEntry);
cout<<"We never should have reached this\n";
Code: Select all
.global setupPaging64
.type setupPaging64, @function
setupPaging64:
movl $0x1000, %edi
movl %edi, %cr3
xorl %eax, %eax
movl $4096, %ecx
rep stosl
movl %cr3, %edi
movl $0x2003, (%edi)
addl $0x1000, %edi
movl $0x3003, (%edi)
addl $0x1000, %edi
movl $0x4003, (%edi)
addl $0x1000, %edi
movl $0x00000003, %ebx
movl $512, %ecx
.SetEntry:
movl %ebx, (%edi)
addl $0x1000, %ebx
addl $8, %edi
loop .SetEntry
ret
.global enablePAE
.type enablePAE, @function
enablePAE:
movl %cr4, %eax
orl $0x20, %eax # 1 << 5
movl %eax, %cr4
ret
.global switchIA32e
.type switchIA32e, @function
switchIA32e:
movl $0xC0000080, %ecx
rdmsr
orl $0x100, %eax # 1 << 8
wrmsr
ret
.global enablePaging64
.type enablePaging64, @function
enablePaging64:
movl %cr0, %eax
orl $0x80000000, %eax # 1<<31
movl %eax, %cr0
ret
.global enter64BitKernel
.type enter64BitKernel, @function
enter64BitKernel:
movl 8(%esp), %eax
movl %eax, (kernel_entry)
movl 4(%esp), %eax
lgdt (%eax)
reloadSegmentsLong: # jump to code segement
jmp $0x08, $.reloadCSLong
.reloadCSLong:
mov $0x10, %ax # load data segment
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
movl (kernel_entry), %eax
#movl $stack_top, %esp
#.long 0
jmp *%eax # jump to new kernel
ret
Code: Select all
.section .text
.global _start64
.type _start64, @function
_start64:
movl $0x00b8000, %edi
movq $500, %rcx
movq $0x1F201F201F201F20, %rax
rep stosq
movl $0x00b8000, %edi
# if I uncomment those lines it tripple faults
#movq $0x1F6C1F6C1F651F48, %rax
#movq %rax, (%edi)
#movq $0x1F6F1F571F201F6F, %rax
#movq %rax, 8(%edi)
#movq $0x1F211F641F6C1F72, %rax
#movq %rax, 16(%edi)
call halt
.global halt
.type halt, @function
halt:
cli
1:
hlt
jmp 1b
What makes me curious is the following: When I look at the memory with qemu it shows the following (0x509 is the entry point of the elf):
it doesn't recognize it as 64Bit instructions. Is this normal behaviour of qemu or does this only mean that I'am not in long mode?!(qemu) xp /10i 0x509
0x0000000000000509: mov $0xb8000,%edi
0x000000000000050e: dec %eax
0x000000000000050f: mov $0x1f4,%ecx
0x0000000000000515: dec %eax
0x0000000000000516: mov $0x1f201f20,%eax
0x000000000000051b: and %bl,(%edi)
0x000000000000051d: and %bl,(%edi)
0x000000000000051f: repz dec %eax
0x0000000000000521: stos %eax,%es:(%edi)
0x0000000000000522: mov $0xb8000,%edi
(qemu)
Any ideas are appreciated
Greets
Nils