Add QEMU interrupt handling (incomplete)

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3339 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2011-03-05 16:04:24 +00:00
parent 1135ccf5a0
commit 758b944c8a
8 changed files with 648 additions and 25 deletions

View File

@ -81,6 +81,42 @@
#define X86_EFLAGS_VIP (1 << 20) /* Bit 20: Virtual Interrupt Pending (Pentium+) */
#define X86_EFLAGS_ID (1 << 21) /* Bit 21: CPUID detection flag (Pentium+) */
/****************************************************************************
* Public Types
****************************************************************************/
/* GDT data structures
*
* The Global Descriptor Table or GDT is a data structure used by Intel x86-
* family processors starting with the 80286 in order to define the
* characteristics of the various memory areas used during program execution,
* for example the base address, the size and access privileges like
* executability and writability. These memory areas are called segments in
* Intel terminology.
*/
/* This structure defines one segment */
struct gdt_entry_s
{
uint16_t lowlimit; /* The lower 16 bits of the limit */
uint16_t lowbase; /* The lower 16 bits of the base */
uint8_t midbase; /* The next 8 bits of the base */
uint8_t access; /* Access flags, determine ring segment can be used in */
uint8_t granularity;
uint8_t hibase; /* The last 8 bits of the base */
} __attribute__((packed));
/* This structure refers to the array of GDT entries, and is in the format
* required by the lgdt instruction.
*/
struct gdt_ptr_s
{
uint16_t limit; /* The upper 16 bits of all selector limits */
uint32_t base; /* The address of the first gdt_entry_t struct */
} __attribute__((packed));
/****************************************************************************
* Inline functions
****************************************************************************/

View File

@ -48,7 +48,57 @@
* Definitions
****************************************************************************/
#define NR_IRQS 0
#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 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 NR_IRQS 48
/****************************************************************************
* Public Types

View File

@ -1,7 +1,7 @@
/****************************************************************************
* arch/x86/src/i486/up_assert.c
*
* Copyright (C) 2010 Gregory Nutt. All rights reserved.
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without

View File

@ -50,7 +50,7 @@ CMN_CSRCS = up_allocateheap.c up_assert.c up_blocktask.c up_copystate.c \
# Required QEMU files
CHIP_ASRCS = qemu_saveusercontext.S qemu_fullcontextrestore.S
CHIP_ASRCS = qemu_saveusercontext.S qemu_fullcontextrestore.S qemu_vectors.S
CHIP_CSRCS = qemu_idle.c qemu_irq.c qemu_lowputc.c qemu_lowsetup.c \
qemu_timerisr.c

View File

@ -40,20 +40,37 @@
#include <nuttx/config.h>
/****************************************************************************
* .text
* Pre-processor definitions
****************************************************************************/
/* Memory Map: _sbss is the start of the BSS region (see ld.script) _ebss is
* the end of the BSS regsion (see ld.script). The idle task stack starts at
* the end of BSS and is of size CONFIG_IDLETHREAD_STACKSIZE. The IDLE thread
* is the thread that the system boots on and, eventually, becomes the idle,
* do nothing task that runs only when there is nothing else to run. The
* heap continues from there until the end of memory. See g_heapbase below.
*/
#define STACKBASE ((_ebss + 0x1f) & 0xffffffe0)
#define IDLE_STACK (STACKBASE+CONFIG_IDLETHREAD_STACKSIZE)
#define HEAP_BASE (STACKBASE+CONFIG_IDLETHREAD_STACKSIZE)
/****************************************************************************
* Nasm .text
****************************************************************************/
#ifdef CONFIG_X86_NASM
global __start /* Making entry point visible to linker */
extern os_start /* os_start is defined elsewhere */
extern up_lowsetup /* up_lowsetup is defined elsewhere */
global _g_heapbase /* The start of the heap */
extern _os_start /* os_start is defined elsewhere */
extern _up_lowsetup /* up_lowsetup is defined elsewhere */
/* Setting up the Multiboot header - see GRUB docs for details */
MODULEALIGN equ 1<<0 /* Align loaded modules on page boundaries */
MEMINFO equ 1<<1 /* Provide memory map */
FLAGS equ MODULEALIGN | MEMINFO /* This is the Multiboot 'flag' field */
MAGIC equ 0x1BADB002 /* 'magic number' lets bootloader find the header */
MAGIC equ 0x1badb002 /* 'magic number' lets bootloader find the header */
CHECKSUM equ -(MAGIC + FLAGS) /* Checksum required */
section .text
@ -63,63 +80,129 @@ MultiBootHeader:
dd FLAGS
dd CHECKSUM
/* Reserve initial kernel stack space */
STACKSIZE equ 0x4000 /* That's 16k */
__start:
mov esp, stack+STACKSIZE /* Set up the stack */
/* Set up the stack */
mov esp, idle_stack + CONFIG_IDLETHREAD_STACKSIZE
/* Multiboot setup */
push eax /* Pass Multiboot magic number */
push ebx /* Pass Multiboot info structure */
/* Initialize and start NuttX */
call _up_lowsetup /* Low-level, pre-OS initialization */
call _os_start /* Start NuttX */
call up_lowsetup /* Low-level, pre-OS initialization */
call os_start /* Start NuttX */
/* NuttX will not return */
cli
hang:
hlt /* Halt machine should NuttX return */
jmp hang
/****************************************************************************
* .bss
****************************************************************************/
/* The stack for the IDLE task thread is declared in .bss. NuttX boots and
* initializes on the IDLE thread, then at the completion of OS startup, this
* thread becomes the thread that executes when there is nothing else to
* do in the system (see up_idle()).
*/
section .bss
align 4
stack:
resb STACKSIZE /* Reserve 16k stack on a doubleword boundary */
idle_stack:
resb CONFIG_IDLETHREAD_STACKSIZE
/****************************************************************************
* .rodata
****************************************************************************/
section .rodata
/* HEAP BASE: _sbss is the start of the BSS region (see ld.script) _ebss is
* the end of the BSS region (see ld.script). The heap continues from there
* until the end of memory.
*/
align 4
g_heapbase:
dd _ebss
#else /* !CONFIG_X86_NASM (GAS) */
/****************************************************************************
* GAS .text
****************************************************************************/
.global __start /* Making entry point visible to linker */
.global _os_start /* os_start is defined elsewhere */
.global _up_lowsetup /* up_lowsetup is defined elsewhere */
.global _g_heapbase /* The start of the heap */
/* Setting up the Multiboot header - see GRUB docs for details */
/* Setting up the Multiboot header - see GRUB docs for details */
.set ALIGN, 1<<0 /* Align loaded modules on page boundaries */
.set MEMINFO, 1<<1 /* Provide memory map */
.set FLAGS, ALIGN | MEMINFO /* This is the Multiboot 'flag' field */
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
.set MAGIC, 0x1badb002 /* 'magic number' lets bootloader find the header */
.set CHECKSUM, -(MAGIC + FLAGS) /* Checksum required */
.text
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
/* Reserve initial kernel stack space */
.set STACKSIZE, 0x4000 /* That is, 16k */
.comm stack, STACKSIZE, 32 /* Reserve 16k stack on a quadword boundary */
__start:
mov $(stack + STACKSIZE), %esp /* Set up the stack */
/* Set up the stack */
mov $(idle_stack + CONFIG_IDLETHREAD_STACKSIZE), %esp
/* Multiboot setup */
push %eax /* Multiboot magic number */
push %ebx /* Multiboot data structure */
/* Initialize and start NuttX */
call _up_lowsetup /* Low-level, pre-OS initialization */
call _os_start /* Start NuttX */
/* NuttX will not return */
cli
hang:
hlt /* Halt machine should NuttX return */
jmp hang
/****************************************************************************
* .bss
****************************************************************************/
/* The stack for the IDLE task thread is declared in .bss. NuttX boots and
* initializes on the IDLE thread, then at the completion of OS startup, this
* thread becomes the thread that executes when there is nothing else to
* do in the system (see up_idle()).
*/
.comm idle_stack, CONFIG_IDLETHREAD_STACKSIZE, 32
/****************************************************************************
* .rodata
****************************************************************************/
.section .rodata, "a"
/* HEAP BASE: _sbss is the start of the BSS region (see ld.script) _ebss is
* the end of the BSS region (see ld.script). The heap continues from there
* until the end of memory.
*/
_g_heapbase:
.word _ebss
.end
#endif /* CONFIG_X86_NASM */

View File

@ -67,6 +67,14 @@
void up_lowputc(char ch)
{
/* Wait until the BIOS can accept another character (so that the OS will
* continue to run.
*/
while ((inb(0x3f8+5) & (1 << 5)) == 0);
/* Then output the character */
outb(ch, 0x3f8);
}

View File

@ -56,6 +56,57 @@
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: up_gdtentry
*
* Description:
* Set the value of one GDT entry.
*
****************************************************************************/
static void up_gdtentry(struct gdt_entry_s *entry, uint32_t base,
uint32_t limit, uint8_t access, uint8_t gran)
{
entry->lowbase = (base & 0xffff);
entry->midbase = (base >> 16) & 0xff;
entry->hibase = (base >> 24) & 0xff;
entry->lowlimit = (limit & 0xffff);
entry->granularity = (limit >> 16) & 0x0f;
entry->granularity |= gran & 0xf0;
entry->access = access;
}
/****************************************************************************
* Name: up_gdtinit
*
* Description:
* Initialize the GDT. The Global Descriptor Table or GDT is a data
* structure used by Intel x86-family processors starting with the 80286
* in order to define the characteristics of the various memory areas used
* during program execution, for example the base address, the size and
* access privileges like executability and writability. These memory areas
* are called segments in Intel terminology.
*
****************************************************************************/
static void up_gdtinit(void)
{
struct gdt_entry_s gdt_entries[5];
struct gdt_ptr_s gdt_ptr;
up_gdtentry(0, 0, 0, 0, 0); /* Null segment */
up_gdtentry(1, 0, 0xffffffff, 0x9a, 0xcf); /* Code segment */
up_gdtentry(2, 0, 0xffffffff, 0x92, 0xcf); /* Data segment */
up_gdtentry(3, 0, 0xffffffff, 0xfa, 0xcf); /* User mode code segment */
up_gdtentry(4, 0, 0xffffffff, 0xf2, 0xcf); /* User mode data segment */
gdt_ptr.limit = (sizeof(struct gdt_entry_s) * 5) - 1;
gdt_ptr.base = (uint32_t)gdt_entries;
gdt_flush((uint32_t )&gdt_ptr);
}
/****************************************************************************
* Public Functions
****************************************************************************/

395
arch/x86/src/qemu/qemu_vectors.S Executable file
View File

@ -0,0 +1,395 @@
/****************************************************************************
* arch/x86/src/qemu/qemu_head.S
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Based on Bran's kernel development tutorials. Rewritten for JamesM's
* kernel development tutorials.
*
* 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 <nuttx/config.h>
#include <arch/irq.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define KSEG 0x10
/****************************************************************************
* Nasm .text
****************************************************************************/
#ifdef CONFIG_X86_NASM
extern _irq_handler
extern _isr_handler
/* Trace macros, use like trace 'i' to print char to serial port. */
%macro io_outb 2
mov dx, %1 /* param1 = address, param2 = data. */
mov al, %2
out dx, al
%endmacro
%macro trace 1
io_outb 0x3f8, %1 /* diagnostic character */
%endmacro
/* This macro creates a stub for an ISR which does NOT pass it's own
* error code (adds a dummy errcode byte).
*/
%macro ISR_NOERRCODE 1
global vector_isr%1
vector_isr%1:
cli /* Disable interrupts firstly. */
push byte 0 /* Push a dummy error code. */
push byte %1 /* Push the interrupt number. */
jmp isr_common /* Go to our common handler code. */
%endmacro
/* This macro creates a stub for an ISR which passes it's own
* error code.
*/
%macro ISR_ERRCODE 1
global vector_isr%1
vector_isr%1:
cli /* Disable interrupts. */
push byte %1 /* Push the interrupt number */
jmp isr_common
%endmacro
/* This macro creates a stub for an IRQ - the first parameter is
* the IRQ number, the second is the ISR number it is remapped to.
*/
%macro IRQ 2
global vector_irq%1
vector_irq%1:
cli
push byte 0
push byte %2
jmp irq_common
%endmacro
/* The following will be the vector address programmed into the IDT */
ISR_NOERRCODE ISR0
ISR_NOERRCODE ISR1
ISR_NOERRCODE ISR2
ISR_NOERRCODE ISR3
ISR_NOERRCODE ISR4
ISR_NOERRCODE ISR5
ISR_NOERRCODE ISR6
ISR_NOERRCODE ISR7
ISR_ERRCODE ISR8
ISR_NOERRCODE ISR9
ISR_ERRCODE ISR10
ISR_ERRCODE ISR11
ISR_ERRCODE ISR12
ISR_ERRCODE ISR13
ISR_ERRCODE ISR14
ISR_NOERRCODE ISR15
ISR_NOERRCODE ISR16
ISR_NOERRCODE ISR17
ISR_NOERRCODE ISR18
ISR_NOERRCODE ISR19
ISR_NOERRCODE ISR20
ISR_NOERRCODE ISR21
ISR_NOERRCODE ISR22
ISR_NOERRCODE ISR23
ISR_NOERRCODE ISR24
ISR_NOERRCODE ISR25
ISR_NOERRCODE ISR26
ISR_NOERRCODE ISR27
ISR_NOERRCODE ISR28
ISR_NOERRCODE ISR29
ISR_NOERRCODE ISR30
ISR_NOERRCODE ISR31
IRQ 0, IRQ0
IRQ 1, IRQ1
IRQ 2, IRQ2
IRQ 3, IRQ3
IRQ 4, IRQ4
IRQ 5, IRQ5
IRQ 6, IRQ6
IRQ 7, IRQ7
IRQ 8, IRQ8
IRQ 9, IRQ9
IRQ 10, IRQ10
IRQ 11, IRQ11
IRQ 12, IRQ12
IRQ 13, IRQ13
IRQ 14, IRQ14
IRQ 15, IRQ15
/* This is our common ISR stub. It saves the processor state, sets up for
* kernel mode segments, calls the C-level fault handler, and finally restores
* the stack frame.
*/
isr_common:
/* trace 'S' */
pusha /* Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax */
mov ax, ds /* Lower 16-bits of eax = ds. */
push eax /* Save the data segment descriptor */
mov ax, KSEG /* Load the kernel data segment descriptor */
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call _isr_handler
pop ebx /* Reload the original data segment descriptor */
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa /* Pops edi,esi,ebp... */
add esp, 8 /* Cleans up the pushed error code and pushed ISR number */
sti
iret /* Pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP */
/* This is our common IRQ stub. It saves the processor state, sets up for
* kernel mode segments, calls the C-level fault handler, and finally restores
* the stack frame.
*/
irq_common:
/* trace 'R' */
pusha /* Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax */
mov ax, ds /* Lower 16-bits of eax = ds. */
push eax /* Save the data segment descriptor */
mov ax, KSEG /* Load the kernel data segment descriptor */
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call _irq_handler
pop ebx /* Reload the original data segment descriptor */
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa /* Pops edi,esi,ebp... */
add esp, 8 /* Cleans up the pushed error code and pushed ISR number */
sti
iret /* Pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP */
#else /* !CONFIG_X86_NASM (GAS) */
/****************************************************************************
* GAS .text
****************************************************************************/
.globl _irq_handler
.globl _isr_handler
/* Trace macros, use like trace 'i' to print char to serial port. */
.macro io_outb, addr, data
mov dx, $\addr
mov al, $\data
out dx, al
.endm
.macro trace, ch
io_outb 0x3f8, \ch
.endm
/* This macro creates a stub for an ISR which does NOT pass it's own
* error code (adds a dummy errcode byte).
*/
.macro ISR_NOERRCODE, intno
.globl vector_isr\intno
vector_isr\intno:
cli /* Disable interrupts firstly. */
push $0 /* Push a dummy error code. */
push $\intno /* Push the interrupt number. */
jmp isr_common /* Go to the common handler code. */
.endm
/* This macro creates a stub for an ISR which passes it's own
* error code.
*/
.macro ISR_ERRCODE, intno
.globl vector_isr\intno
vector_isr\intno:
cli /* Disable interrupts firstly. */
push $\intno /* Push the interrupt number. */
jmp isr_common /* Go to the common handler code. */
.endm
/* This macro creates a stub for an IRQ - the first parameter is
* the IRQ number, the second is the ISR number it is remapped to.
*/
.macro IRQ, irqno, intno
.globl vector_irq\irqno
vector_irq\irqno:
cli /* Disable interrupts firstly. */
push $0 /* Push a dummy error code. */
push $\intno /* Push the interrupt number. */
jmp isr_common /* Go to the common handler code. */
.endm
/* The following will be the vector address programmed into the IDT */
ISR_NOERRCODE ISR0
ISR_NOERRCODE ISR1
ISR_NOERRCODE ISR2
ISR_NOERRCODE ISR3
ISR_NOERRCODE ISR4
ISR_NOERRCODE ISR5
ISR_NOERRCODE ISR6
ISR_NOERRCODE ISR7
ISR_ERRCODE ISR8
ISR_NOERRCODE ISR9
ISR_ERRCODE ISR10
ISR_ERRCODE ISR11
ISR_ERRCODE ISR12
ISR_ERRCODE ISR13
ISR_ERRCODE ISR14
ISR_NOERRCODE ISR15
ISR_NOERRCODE ISR16
ISR_NOERRCODE ISR17
ISR_NOERRCODE ISR18
ISR_NOERRCODE ISR19
ISR_NOERRCODE ISR20
ISR_NOERRCODE ISR21
ISR_NOERRCODE ISR22
ISR_NOERRCODE ISR23
ISR_NOERRCODE ISR24
ISR_NOERRCODE ISR25
ISR_NOERRCODE ISR26
ISR_NOERRCODE ISR27
ISR_NOERRCODE ISR28
ISR_NOERRCODE ISR29
ISR_NOERRCODE ISR30
ISR_NOERRCODE ISR31
IRQ 0, IRQ0
IRQ 1, IRQ1
IRQ 2, IRQ2
IRQ 3, IRQ3
IRQ 4, IRQ4
IRQ 5, IRQ5
IRQ 6, IRQ6
IRQ 7, IRQ7
IRQ 8, IRQ8
IRQ 9, IRQ9
IRQ 10, IRQ10
IRQ 11, IRQ11
IRQ 12, IRQ12
IRQ 13, IRQ13
IRQ 14, IRQ14
IRQ 15, IRQ15
/* This is our common ISR stub. It saves the processor state, sets up for
* kernel mode segments, calls the C-level fault handler, and finally restores
* the stack frame.
*/
isr_common:
/* trace 'S' */
pusha /* Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax */
mov %ax, ds /* Lower 16-bits of eax = ds. */
pushl %eax /* Save the data segment descriptor */
mov %ax, KSEG /* Load the kernel data segment descriptor */
mov ds, %ax
mov es, %ax
mov fs, %ax
mov gs, %ax
call _isr_handler
pop ebx /* Reload the original data segment descriptor */
mov ds, %bx
mov es, %bx
mov fs, %bx
mov gs, %bx
popa /* Pops edi,esi,ebp... */
add %esp, 8 /* Cleans up the pushed error code and pushed ISR number */
sti
iret /* Pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP */
/* This is our common IRQ stub. It saves the processor state, sets up for
* kernel mode segments, calls the C-level fault handler, and finally restores
* the stack frame.
*/
irq_common:
/* trace 'R' */
pusha /* Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax */
mov %ax, ds /* Lower 16-bits of eax = ds. */
push %eax /* Save the data segment descriptor */
mov %ax, KSEG /* Load the kernel data segment descriptor */
mov ds, %ax
mov es, %ax
mov fs, %ax
mov gs, %ax
call _irq_handler
pop %ebx /* Reload the original data segment descriptor */
mov ds, %bx
mov es, %bx
mov fs, %bx
mov gs, %bx
popa /* Pops edi,esi,ebp... */
add %esp, 8 /* Cleans up the pushed error code and pushed ISR number */
sti
iret /* Pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP */
.end
#endif /* CONFIG_X86_NASM */