Every branch instruction cause exception after sti

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.
Post Reply
torshie
Member
Member
Posts: 89
Joined: Sun Jan 11, 2009 7:41 pm

Every branch instruction cause exception after sti

Post by torshie »

After entering 64-bit mode, loading GDT, IDT, TSS, TR, I enable interrupt, the sti instruction seems to work. But after the sti instruction, every branch instruction (call, jmp, ret, iretq ...) causes a #GP, even the last instruction "iretq" in interrupt handler, so bochs goes into an endless loop. bochs keeps giving an error message: "00233082544e[CPU0 ] check_cs(0x0216): conforming code seg descriptor dpl > cpl, dpl=3, cpl=2". I checked all the descriptors, every DPL is set to 0, C bit of Code Segment Descriptor is not enabled.
The following is the main part of the code, the attached is the binary file. Please help me find out what goes wrong.

Thanks.

Code: Select all

/* Multiboot constants */
.set MBOOT_ALIGN, 1 << 0
.set MBOOT_MEMINFO, 1 << 1
.set MBOOT_AOUT_KLUDGE, 1 << 16
.set MBOOT_FLAGS, MBOOT_ALIGN | MBOOT_MEMINFO | MBOOT_AOUT_KLUDGE
.set MBOOT_MAGIC, 0x1BADB002
.set MBOOT_CHECKSUM, -(MBOOT_MAGIC + MBOOT_FLAGS)

.set STACK_SIZE, 0x100000 /* 1M kernel stack! Nowadays main memory is very
						   * cheap, right? :-)
						   */
.set PAGE_SIZE, 0x1000

/** FIXME
 * We have to pack neary everything into .text section, because it seems that
 * grub doesn't load content in .data section
 */
.section .text
.global start
start:
	jmp bootstrap

.align 4
mbootheader:
	.long MBOOT_MAGIC
	.long MBOOT_FLAGS
	.long MBOOT_CHECKSUM
	.long mbootheader

	/* Defined in ld script */
	.long _ld_codeStart
	.long _ld_bssStart
	.long _ld_end

	.long start

.align 8
taskStateSegment: /* 64-bit Task State Segment */
	.long 0 /* Reserved, IGN */
	.quad 0 /* RSP0 */
	.quad 0 /* RSP1 */
	.quad 0 /* RSP2 */
	.quad 0 /* Reserved, IGN */
	.quad 0 /* IST1 */
	.quad 0 /* IST2 */
	.quad 0 /* IST3 */
	.quad 0 /* IST4 */
	.quad 0 /* IST5 */
	.quad 0 /* IST6 */
	.quad 0 /* IST7 */
	.quad 0 /* Reserved, IGN */
	.short 0 /* Reserved, IGN */
	.short ioMapBase - taskStateSegment /* I/O Map Base Address */
ioMapBase:
	.rept 8192
		.byte 0xff
	.endr
.set TSS_LIMIT, . - taskStateSegment - 1 

interruptDescriptorTable:
	.rept 32
		.quad 0
		.quad 0
	.endr
interruptDescriptorTablePointer:
	.short . - interruptDescriptorTable - 1
	.quad interruptDescriptorTable

globalDescriptorTable:
	.quad 0 /* Null segment descriptor, required */
	/* 32-bit Code Segment descriptor */
_32bitCodeSegmentSelector:
	.long 0x0000FFFF
	.long 0x00CF9A00 /* Bit G, D, P, 12, 11 and R are set, DPL is 0 */
	/**
	 * Data Segment descriptor.
	 * This segment descriptor can be used both in 32-bit mode and 64-bit mode
	 */
dataSegmentSelector:
	.long 0x0000FFFF
	.long 0x00CF9200 /* Bit G, D/B, P, 12 and W are set, DPL is 0 */
	/**
	 * 64-bit Code Segment descriptor
	 */
_64bitCodeSegmentSelector:
	.long 0x00000000
	.long 0x00209800 /* Bit L, P, 12 and 11 are set, DPL is 0 */
	/**
	 * Space for 64-bit Task State Segment, we will fill content later
	 */
taskStateSegmentSelector:
	.quad 0
	.quad 0

globalDescriptorTablePointer:
	.short . - globalDescriptorTable - 1
	.quad globalDescriptorTable

.code32
bootstrap:
	cli

xchg %bx, %bx

	mov $kernelStack, %esp

	/**
	 * Call mangled function kernel::fillPageTable(unsigned long long*,
	 * 			unsigned long long*, unsigned long long*, unsigned long long*)
	 *
	 * Because now the CPU is still in 32-bit mode, we use 32-bit calling
	 * convention to call the function, the source code is also compiled
	 * with -m32 flag
	 */
	pushl $pageMapLevel4
	pushl $pageDirectoryPointer
	pushl $pageDirectory
	pushl $pageTable
	call _ZN6kernel13fillPageTableEPyS0_S0_S0_
	add $0x10, %esp /* Remove function parameters from stack */
	
	/**
	 * Call mangled kernel::fillTaskStateSegmentSelector(unsigned long,
	 * 				unsigned long, unsigned short*)
	 */
	pushl $taskStateSegmentSelector
	pushl $taskStateSegment
	pushl $TSS_LIMIT
	call _ZN6kernel28fillTaskStateSegmentSelectorEmmPt
	add $0xc, %esp /* Remove function parameters from stack */

	/**
	 * Call mangled kernel::fillInterruptDescriptorTable
	 */
	pushl $(_64bitCodeSegmentSelector - globalDescriptorTable)
	pushl $isrAddressTable
	pushl $interruptDescriptorTable
	call _ZN6kernel28fillInterruptDescriptorTableEPNS_19InterruptDescriptorEPml
	add $0xc, %esp /* Remove function parameters from stack */
	
	mov $0x1, %eax
	mov %eax, %cr0

	mov %cr4, %eax
	bts $5, %eax /* Set PAE */
	mov %eax, %cr4

	mov $pageMapLevel4, %eax /* Point cr3 at pageMapLevel4 */
	mov %eax, %cr3

	mov $0x00C0000080, %ecx /* EFER MSR */
	rdmsr
	bts $8, %eax /* LME */
	wrmsr

	lgdt globalDescriptorTablePointer
	
	mov %cr0, %eax
	bts $31, %eax /* Enable paging */
	mov %eax, %cr0

	ljmp $(_64bitCodeSegmentSelector - globalDescriptorTable), $_64bitMode

.code64
_64bitMode:
	cli
	mov $kernelStack, %rsp
	
	mov $(dataSegmentSelector - globalDescriptorTable), %ax
	mov %ax, %ds

	lidt interruptDescriptorTablePointer

	mov $(taskStateSegmentSelector - globalDescriptorTable), %ax
	ltr %ax

	sti

	/* Call mangled kernel::startKernel(void) */
	call _ZN6kernel11startKernelEv   /* <<============== Endless loop starts here =============== */

	mov $0xB8000, %edi
	mov $0x0f620f2d0f340f36, %rax
	mov %rax, (%edi)

	mov $0x0f6d0f200f640f69, %rax
	mov %rax, 8(%edi)

	mov $0x0f200f650f640f6f, %rax
	mov %rax, 16(%edi)

	jmp .

.macro ISR_WITHOUT_CODE isrNumber
isr\isrNumber:
	push $0
	push $\isrNumber
	ISR_BODY
.endm

.macro ISR_WITH_CODE isrNumber
isr\isrNumber:
	push $\isrNumber
	ISR_BODY
.endm

.macro ISR_BODY
	add $0x10, %rsp
	iretq
.endm

ISR_WITHOUT_CODE 0
ISR_WITHOUT_CODE 1
ISR_WITHOUT_CODE 2
ISR_WITHOUT_CODE 3
ISR_WITHOUT_CODE 4
ISR_WITHOUT_CODE 5
ISR_WITHOUT_CODE 6
ISR_WITHOUT_CODE 7
ISR_WITH_CODE 8
ISR_WITHOUT_CODE 9
ISR_WITH_CODE 10
ISR_WITH_CODE 11
ISR_WITH_CODE 12
ISR_WITH_CODE 13
ISR_WITH_CODE 14
ISR_WITHOUT_CODE 15
ISR_WITHOUT_CODE 16
ISR_WITH_CODE 17
ISR_WITHOUT_CODE 18
ISR_WITHOUT_CODE 19
ISR_WITHOUT_CODE 20
ISR_WITHOUT_CODE 21
ISR_WITHOUT_CODE 22
ISR_WITHOUT_CODE 23
ISR_WITHOUT_CODE 24
ISR_WITHOUT_CODE 25
ISR_WITHOUT_CODE 26
ISR_WITHOUT_CODE 27
ISR_WITHOUT_CODE 28
ISR_WITHOUT_CODE 29
ISR_WITHOUT_CODE 30
ISR_WITHOUT_CODE 31

isrAddressTable:
	.long isr0
	.long isr1
	.long isr2
	.long isr3
	.long isr4
	.long isr5
	.long isr6
	.long isr7
	.long isr8
	.long isr9
	.long isr10
	.long isr11
	.long isr12
	.long isr13
	.long isr14
	.long isr15
	.long isr16
	.long isr17
	.long isr18
	.long isr19
	.long isr20
	.long isr21
	.long isr22
	.long isr23
	.long isr24
	.long isr25
	.long isr26
	.long isr27
	.long isr28
	.long isr29
	.long isr30
	.long isr31

.section .bss
.comm stackTop, STACK_SIZE, PAGE_SIZE
kernelStack: /* We just define a lable kernelStack, because stack grows
			  * downside
			  */
.comm pageMapLevel4, PAGE_SIZE, PAGE_SIZE
.comm pageDirectoryPointer, PAGE_SIZE, PAGE_SIZE
.comm pageDirectory, 0x4000, PAGE_SIZE
.comm pageTable, 0x800000, PAGE_SIZE
Attachments
kernel.elf.gz
Use gzip to decompress, then load it with grub.
(5.19 KiB) Downloaded 39 times
torshie
Member
Member
Posts: 89
Joined: Sun Jan 11, 2009 7:41 pm

Re: Every branch instruction cause exception after sti

Post by torshie »

I find out the bug :mrgreen: !
In fact, I was wrong. The infinite loop is caused by another reason: iretq in isr8 caused a #GP, iretq in isr13 caused a double fault. Then the CPU ended up in handling #GP and double fault.

After sti, an instruction 'push %rbp' generated by G++ in function startKernel will get a double fault(don't know why), and control will be transferred to isr8. Both Intel and AMD's manual says CPU will push a zero on the stack before transferring control to isr8, but I double checked the stack, there wasn't a zero on the stack!!!, then iretq cannot find required data on the stack. So, the solution to the bug is very simple
change line
ISR_WITH_CODE 8
to
ISR_WITHOUT_CODE 8
which means I push the zero on the stack myself

This solution works on both bochs and qemu, I haven't tested on a physical machine.

The attached is the complete source code. I work on a mac, use x86_64-elf toolchain to compile and link. Type command "make kernel.elf" to generate the kernel binary kernel.elf. In fact, the kernel image is not an elf binary :lol:
Attachments
mouse.tar.gz
(17.32 KiB) Downloaded 49 times
cyr1x
Member
Member
Posts: 207
Joined: Tue Aug 21, 2007 1:41 am
Location: Germany

Re: Every branch instruction cause exception after sti

Post by cyr1x »

8259 FTW :roll:
torshie
Member
Member
Posts: 89
Joined: Sun Jan 11, 2009 7:41 pm

Re: Every branch instruction cause exception after sti

Post by torshie »

cyr1x wrote:8259 FTW :roll:
What does this mean? :?:
English isn't my mother tongue :roll:
User avatar
neon
Member
Member
Posts: 1567
Joined: Sun Feb 18, 2007 7:28 pm
Contact:

Re: Every branch instruction cause exception after sti

Post by neon »

torshie wrote:
cyr1x wrote:8259 FTW :roll:
What does this mean? :?:
English isn't my mother tongue :roll:
8259 = Intels family of PIC microcontrollers
FTW = For the Win
The point of the post: I have no idea

...Glad to hear you got it working! :D
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Post Reply