From 663ea6d478d52917601d71ceb10089bb7b82f150 Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 17 Mar 2011 22:33:49 +0000 Subject: [PATCH] Fix QEMU timer interrupt handler git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3389 42af7a65-404d-4744-a932-0658087f49c3 --- arch/x86/include/i486/irq.h | 115 ++++++++++++-------- arch/x86/src/common/up_internal.h | 2 +- arch/x86/src/i486/up_initialstate.c | 5 +- arch/x86/src/i486/up_savestate.c | 113 +++++++++++++++++++ arch/x86/src/qemu/Make.defs | 2 +- arch/x86/src/qemu/qemu_fullcontextrestore.S | 34 ++++-- arch/x86/src/qemu/qemu_saveusercontext.S | 21 +++- arch/x86/src/qemu/qemu_vectors.S | 3 +- 8 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 arch/x86/src/i486/up_savestate.c diff --git a/arch/x86/include/i486/irq.h b/arch/x86/include/i486/irq.h index 9b1162671b..4373521993 100755 --- a/arch/x86/include/i486/irq.h +++ b/arch/x86/include/i486/irq.h @@ -56,55 +56,55 @@ /* ISR and IRQ numbers */ -#define ISR0 0 -#define ISR1 1 -#define ISR2 2 -#define ISR3 3 -#define ISR4 4 -#define ISR5 5 -#define ISR6 6 -#define ISR7 7 -#define ISR8 8 -#define ISR9 9 -#define ISR10 10 -#define ISR11 11 -#define ISR12 12 -#define ISR13 13 -#define ISR14 14 -#define ISR15 15 -#define ISR16 16 -#define ISR17 17 -#define ISR18 18 -#define ISR19 19 -#define ISR20 20 -#define ISR21 21 -#define ISR22 22 -#define ISR23 23 -#define ISR24 24 -#define ISR25 25 -#define ISR26 26 -#define ISR27 27 -#define ISR28 28 -#define ISR29 29 -#define ISR30 30 -#define ISR31 31 +#define ISR0 0 /* Division by zero exception */ +#define ISR1 1 /* Debug exception */ +#define ISR2 2 /* Non maskable interrupt */ +#define ISR3 3 /* Breakpoint exception */ +#define ISR4 4 /* 'Into detected overflow' */ +#define ISR5 5 /* Out of bounds exception */ +#define ISR6 6 /* Invalid opcode exception */ +#define ISR7 7 /* No coprocessor exception */ +#define ISR8 8 /* Double fault (pushes an error code) */ +#define ISR9 9 /* Coprocessor segment overrun */ +#define ISR10 10 /* Bad TSS (pushes an error code) */ +#define ISR11 11 /* Segment not present (pushes an error code) */ +#define ISR12 12 /* Stack fault (pushes an error code) */ +#define ISR13 13 /* General protection fault (pushes an error code) */ +#define ISR14 14 /* Page fault (pushes an error code) */ +#define ISR15 15 /* Unknown interrupt exception */ +#define ISR16 16 /* Coprocessor fault */ +#define ISR17 17 /* Alignment check exception */ +#define ISR18 18 /* Machine check exception */ +#define ISR19 19 /* Reserved */ +#define ISR20 20 /* Reserved */ +#define ISR21 21 /* Reserved */ +#define ISR22 22 /* Reserved */ +#define ISR23 23 /* Reserved */ +#define ISR24 24 /* Reserved */ +#define ISR25 25 /* Reserved */ +#define ISR26 26 /* Reserved */ +#define ISR27 27 /* Reserved */ +#define ISR28 28 /* Reserved */ +#define ISR29 29 /* Reserved */ +#define ISR30 30 /* Reserved */ +#define ISR31 31 /* Reserved */ -#define IRQ0 32 -#define IRQ1 33 -#define IRQ2 34 -#define IRQ3 35 -#define IRQ4 36 -#define IRQ5 37 -#define IRQ6 38 -#define IRQ7 39 -#define IRQ8 40 -#define IRQ9 41 -#define IRQ10 42 -#define IRQ11 43 -#define IRQ12 44 -#define IRQ13 45 -#define IRQ14 46 -#define IRQ15 47 +#define IRQ0 32 /* System timer (cannot be changed) */ +#define IRQ1 33 /* Keyboard controller (cannot be changed) */ +#define IRQ2 34 /* Cascaded signals from IRQs 8–15 */ +#define IRQ3 35 /* Serial port controller for COM2/4 */ +#define IRQ4 36 /* serial port controller for COM1/3 */ +#define IRQ5 37 /* LPT port 2 or sound card */ +#define IRQ6 38 /* Floppy disk controller */ +#define IRQ7 39 /* LPT port 1 or sound card */ +#define IRQ8 40 /* Real time clock (RTC) */ +#define IRQ9 41 /* Open interrupt/available or SCSI host adapter */ +#define IRQ10 42 /* Open interrupt/available or SCSI or NIC */ +#define IRQ11 43 /* Open interrupt/available or SCSI or NIC */ +#define IRQ12 44 /* Mouse on PS/2 connector */ +#define IRQ13 45 /* Math coprocessor */ +#define IRQ14 46 /* Primary ATA channel */ +#define IRQ15 47 /* Secondary ATA channel */ #define NR_IRQS 48 @@ -138,6 +138,25 @@ #define XCPTCONTEXT_REGS (16) #define XCPTCONTEXT_SIZE (4 * XCPTCONTEXT_REGS) +/* Some special landmarks in the stack frame: + * + * TOP_PUSHA - The offset (in 32-bit words) from the beginning of the + * save area on the stack to the value that should be in REG_ESP. + * BOTTOM_PUSHA - The offset (in 32-bit words) from the stack position before + * the interrupt occurred to the value that should be in REG_ESP. + * save area on the stack to the value that should be in REG_ESP. + * OFFSET_PRIO - The offset from the value of REG_ESP to the value of the + * stack pointer before the interrupt occurred (assuming that a priority + * change occurred. + * OFFSET_PRIO - The offset from the value of REG_ESP to the value of the + * stack pointer before the interrupt occurred (assuming that NO priority + * change occurred. + */ + +#define TOP_PUSHA REG_IRQNO +#define BOTTOM_PRIO (XCPTCONTEXT_REGS-REG_IRQNO) +#define BOTTOM_NOPRIO (REG_SP-REG_IRQNO) + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/arch/x86/src/common/up_internal.h b/arch/x86/src/common/up_internal.h index d8e25329e9..51f79beb35 100644 --- a/arch/x86/src/common/up_internal.h +++ b/arch/x86/src/common/up_internal.h @@ -80,7 +80,6 @@ * referenced is passed to get the state from the TCB. */ -#define up_savestate(regs) up_copystate(regs, current_regs) #define up_restorestate(regs) (current_regs = regs) /**************************************************************************** @@ -150,6 +149,7 @@ extern uint32_t _ebss; /* End+1 of .bss */ extern void up_boot(void); extern void up_copystate(uint32_t *dest, uint32_t *src); +extern void up_savestate(uint32_t *regs); extern void up_decodeirq(uint32_t *regs); extern void up_irqinitialize(void); #ifdef CONFIG_ARCH_DMA diff --git a/arch/x86/src/i486/up_initialstate.c b/arch/x86/src/i486/up_initialstate.c index 1a13b89915..9ca40feb3c 100644 --- a/arch/x86/src/i486/up_initialstate.c +++ b/arch/x86/src/i486/up_initialstate.c @@ -85,7 +85,10 @@ void up_initial_state(_TCB *tcb) memset(xcp, 0, sizeof(struct xcptcontext)); - /* Save the initial stack pointer */ + /* Save the initial stack pointer... the value of the stackpointer before + * the "interrupt occurs." We don't know the value of REG_ESP yet.. + * that depends on if a priority change is required or not. + */ xcp->regs[REG_SP] = (uint32_t)tcb->adj_stack_ptr; diff --git a/arch/x86/src/i486/up_savestate.c b/arch/x86/src/i486/up_savestate.c new file mode 100644 index 0000000000..d5f97fa857 --- /dev/null +++ b/arch/x86/src/i486/up_savestate.c @@ -0,0 +1,113 @@ +/**************************************************************************** + * arch/x86/src/i486/up_savestate.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "up_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: up_savestate + * + * Description: + * This function saves the interrupt level context information in the + * TCB. This would just be a up_copystate but we have to handle one + * special case. In the case where the privilige level changes, the + * value of sp and ss will not be saved on stack by the interrupt handler. + * So, in that case, we will have to fudge those values here. + * + ****************************************************************************/ + +void up_savestate(uint32_t *regs) +{ + uint8_t cpl; + uint8_t rpl; + + /* First, just copy all of the registers */ + + up_copystate(regs, current_regs); + + /* The RES_SP and REG_SS values will not be saved by the interrupt handling + * logic if there is no change in privilege level. In that case, we will + * have to "fudge" those values here. For now, just overwrite the REG_SP + * and REG_SS values with what we believe to be correct. Obviously, this + * will have to change in the future to support multi-segment operation. + * + * Check for a change in privilege level. + */ + + rpl = regs[REG_CS] & 3; + cpl = up_getcs() & 3; + DEBUGASSERT(rpl >= cpl); + + if (rpl == cpl) + { + /* No priority change, SP and SS are not present in the stack frame. + * + * The value saved in the REG_ESP will be the stackpointer value prior to + * the execution of the PUSHA. It will point at REG_IRQNO. + */ + + regs[REG_SP] = current_regs[REG_ESP] + 4*BOTTOM_NOPRIO; + regs[REG_SS] = up_getss(); + } + else + { + DEBUGASSERT(regs[REG_SP] == current_regs[REG_ESP] + 4*BOTTOM_PRIO); + } +} diff --git a/arch/x86/src/qemu/Make.defs b/arch/x86/src/qemu/Make.defs index 3177f3db4d..ee239910bd 100755 --- a/arch/x86/src/qemu/Make.defs +++ b/arch/x86/src/qemu/Make.defs @@ -45,7 +45,7 @@ CMN_CSRCS = up_allocateheap.c up_assert.c up_blocktask.c up_copystate.c \ up_initialize.c up_initialstate.c up_interruptcontext.c up_irq.c \ up_modifyreg8.c up_modifyreg16.c up_modifyreg32.c up_regdump.c \ up_releasepending.c up_releasestack.c up_reprioritizertr.c \ - up_sigdeliver.c up_schedulesigaction.c up_unblocktask.c \ + up_savestate.c up_sigdeliver.c up_schedulesigaction.c up_unblocktask.c \ up_usestack.c # Required QEMU files diff --git a/arch/x86/src/qemu/qemu_fullcontextrestore.S b/arch/x86/src/qemu/qemu_fullcontextrestore.S index 9a1e1e64e4..ded55a1470 100644 --- a/arch/x86/src/qemu/qemu_fullcontextrestore.S +++ b/arch/x86/src/qemu/qemu_fullcontextrestore.S @@ -106,26 +106,43 @@ up_fullcontextrestore: cli - /* We now have everything we need from the old stack. Now get the new - * stack pointer. + /* Get the value of the stack pointer as it was when the pusha was + * executed the interrupt handler. */ movl (4*REG_SP)(%eax), %esp /* Create an interrupt stack frame for the final iret. * - * SP Before -> - * SS - * ESP - * EFLAGS - * CS - * SP After -> EIP + * + * IRET STACK + * PRIO CHANGE No PRIO CHANGE + * --------------- ----------------- + * SP Before -> + * SS EFLAGS + * ESP CS + * EFLAGS -> EIP + * CS ... + * SP After -> EIP + * + * So, first check for a priority change. */ + movl (4*REG_CS)(%eax), %edx + andl $3, %edx + mov %cs, %ebx + andl $3, %ebx + cmpb %bl, %dl + je .Lnopriochange + + /* The priority will change... put SS and ESP on the stack */ + mov (4*REG_SS)(%eax), %ebx push %ebx movl (4*REG_SP)(%eax), %ebx push %ebx + +.Lnopriochange: movl (4*REG_EFLAGS)(%eax), %ebx push %ebx mov (4*REG_CS)(%eax), %ebx @@ -162,4 +179,3 @@ up_fullcontextrestore: iret .size up_fullcontextrestore, . - up_fullcontextrestore .end - diff --git a/arch/x86/src/qemu/qemu_saveusercontext.S b/arch/x86/src/qemu/qemu_saveusercontext.S index e9a98b3315..427c3bd5d4 100644 --- a/arch/x86/src/qemu/qemu_saveusercontext.S +++ b/arch/x86/src/qemu/qemu_saveusercontext.S @@ -126,11 +126,26 @@ up_saveusercontext: mov %cs, (4*REG_CS)(%eax) mov %ds, (4*REG_DS)(%eax) - /* Save the value of SP as will be after we return (don't bother to save - * REG_ESP). + /* Save the value of SP as will be at the time of the IRET that will + * appear to be the return from this function. + * + * + * CURRENT STACK IRET STACK + * PRIO CHANGE No PRIO CHANGE + * --------------- --------------- ----------------- + * EIP + * CS ... + * EFLAGS EIP + * -> ESP CS + * ESP->Return address SS EFLAGS + * Argument Argument Argument + * + * NOTE: We don't yet know the value for REG_ESP! That depends upon + * if a priority change occurs or not. */ - leal 4(%esp), %ecx + + leal -4(%esp), %ecx movl %ecx, (4*REG_SP)(%eax) /* Fetch the PC from the stack and save it in the save block */ diff --git a/arch/x86/src/qemu/qemu_vectors.S b/arch/x86/src/qemu/qemu_vectors.S index 87412c21b1..d6e17e57bc 100755 --- a/arch/x86/src/qemu/qemu_vectors.S +++ b/arch/x86/src/qemu/qemu_vectors.S @@ -266,7 +266,6 @@ irq_common: popa /* Pops edi,esi,ebp... */ add $8, %esp /* Cleans up the pushed error code and pushed ISR number */ - sti - iret /* Pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP */ + iret /* Pops 3-5 things at once: CS, EIP, EFLAGS (and maybe SS and ESP) */ .size irq_common, . - irq_common .end