Page 1 of 1

x86_64 stack swap

Posted: Sun May 16, 2010 10:28 pm
by proxy
Time for yet another x86_64 question! I am trying get threads going and have come across a wierd issue. Basically, after a few swaps, somehow interrupts get disabled leaving one task running indefinitely.

Here's my context structure:

Code: Select all

#pragma pack (push, 1)
struct context64 {
	std::uint64_t gs; 
	std::uint64_t fs;
	std::uint64_t r15; 
	std::uint64_t r14;
	std::uint64_t r13;
	std::uint64_t r12;
	std::uint64_t r11;
	std::uint64_t r10;
	std::uint64_t r9;
	std::uint64_t r8;
	std::uint64_t rbp;
	std::uint64_t rsi;
	std::uint64_t rdi;
	std::uint64_t rdx;
	std::uint64_t rcx;
	std::uint64_t rbx;
	std::uint64_t rax;
	std::uint64_t int_no;
	std::uint64_t error;
	std::uint64_t rip;
	std::uint64_t cs;
	std::uint64_t rflags;
	std::uint64_t rsp;
	std::uint64_t ss;
};
#pragma pack(pop)
and here's my interrupt handler code:

Code: Select all

#define PUSHA  \
	push %rax; \
	push %rbx; \
	push %rcx; \
	push %rdx; \
	push %rdi; \
	push %rsi; \
	push %rbp; \
	push %r8;  \
	push %r9;  \
	push %r10; \
	push %r11; \
	push %r12; \
	push %r13; \
	push %r14; \
	push %r15
	
#define POPA  \
	pop	%r15; \
	pop	%r14; \
	pop	%r13; \
	pop	%r12; \
	pop	%r11; \
	pop	%r10; \
	pop	%r9;  \
	pop	%r8;  \
	pop	%rbp; \
	pop	%rsi; \
	pop	%rdi; \
	pop	%rdx; \
	pop	%rcx; \
	pop	%rbx; \
	pop	%rax

#define irq(n)					\
	.align 16;					\
	.globl	_irq##n;			\
_irq##n: 						\
	push $-1;					\
	push $0x##n;				\
	jmp _interrupt_stub;

_interrupt_stub:
	PUSHA
	push %fs
	push %gs
	
        /* make sure we have a sane kernel segment setup */
	xor %ax, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	
	movq %rsp, %rdi
	call _interrupt_handler
	movq %rax, %rsp
	
	pop	%gs
	pop	%fs
	POPA
	
	addq $16, %rsp
	iretq

irq(00)
As you can see, I push the entire context onto the stack and pass that (via the rdi register) to the handler function. This function is expected to return a pointer to an offset to the next task's kernel stack where the kernel context is found. Obviously if it just returns the value passed to it, no task switch will happen.

Finally, my handler looks something like this (assume runqueue has same functionality as std::list in c++)

Code: Select all

void* _interrupt_handler(void *p) {
        // get a context to look at..
	context64 *ctx = reinterpret_cast<context64 *>(p);	

        // if it's hardware IRQ, send an EOI
	if(ctx->int_no < 16) {	
		pic::send_eoi(ctx->int_no);
	}

	// timer interrupt, possible task swap?
	if(ctx->int_no == 0) {
                // if this isn't the very first task swap, store the current stack pointer into the current running task
		if(current) {
			current->m_KernelStackAddress = p;
			runqueue.push_back(current);
		}
		
		current = runqueue.front();
		runqueue.pop_front();
		return current->m_KernelStackAddress;
	}

	return p;
}
Are there any obvious issues with this code? I am having a hard time finding examples of 64-bit task swap code out there. Most example 64-bit OS's that I've found are pretty basic in functionality so I'm having a hard time comparing my code to what's out there. Am I missing something simple? Do I need to disable interrupts explicitly when in an ISR? Should I manually disable/enable the hardware interrupt 0 while handling the interrupt 0 event?

Thanks

Re: x86_64 stack swap

Posted: Sun May 16, 2010 11:07 pm
by proxy
I believe that I have tracked it down to the face that my global system lock was not re-entrant. It's surprising that this never effected my 32-bit code in any visible way, but oh well :-).

Obviously if anyone can see any obviously flaws with my posted code, I'll still appreciate it.

Thanks

Re: x86_64 stack swap

Posted: Sun May 16, 2010 11:24 pm
by gerryg400
/* make sure we have a sane kernel segment setup */
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
Are you sure it's okay to load 0 to DS and ES ?

- gerryg400

Re: x86_64 stack swap

Posted: Sun May 16, 2010 11:25 pm
by proxy
Yea, in long mode segments are pretty much ignored for most purposes.

Re: x86_64 stack swap

Posted: Sun May 16, 2010 11:45 pm
by gerryg400
When do you put the ring 0 rsp for the current/new task in the TSS ? Or does everything run at ring0 ?

BTW, I'm moving my 32bit code to 64bit as well. Having all these issues (and others!) as well. Your interrupt entry code looks similar to mine although mine is multicore.

- gerryg400

Re: x86_64 stack swap

Posted: Sun May 16, 2010 11:48 pm
by proxy
my port to 64-bit is being done through a complete re-write, so currently all tasks are ring0. Good point though that the tss will need to have rsp0 set during each task swap.

Re: x86_64 stack swap

Posted: Mon May 17, 2010 9:48 am
by lemonyii
where does it got problems?
first, it seems there is no PUSHA/POPA in x86-64 (i ever tried,but this will not pass compiling in yasm0.8.0).
second, make sure the ERROR CODE is pushed when and only when neccessary.
third, do you have a correct stack switch? if you are always in ring0, the cpu will not switch stack on interruption.
last, use bochsdbg to track the fault.(i often use it for such purposes),or use handler within 32 to print some information(like memory context of the stack)to show where or how it go wrong(#GP?#DF? i often use this to judge what's wrong with it).
cheers!