x86_64 stack swap
Posted: Sun May 16, 2010 10:28 pm
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:
and here's my interrupt handler code:
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++)
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
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)
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)
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;
}
Thanks