From 8a818deb60c19ee90d47b99cf12ed079f36d51b3 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 5 Mar 2011 16:04:24 +0000 Subject: Add QEMU interrupt handling (incomplete) git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3339 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/x86/src/qemu/Make.defs | 2 +- nuttx/arch/x86/src/qemu/qemu_head.S | 125 ++++++++-- nuttx/arch/x86/src/qemu/qemu_lowputc.c | 8 + nuttx/arch/x86/src/qemu/qemu_lowsetup.c | 51 +++++ nuttx/arch/x86/src/qemu/qemu_vectors.S | 395 ++++++++++++++++++++++++++++++++ 5 files changed, 559 insertions(+), 22 deletions(-) create mode 100755 nuttx/arch/x86/src/qemu/qemu_vectors.S (limited to 'nuttx/arch/x86/src/qemu') diff --git a/nuttx/arch/x86/src/qemu/Make.defs b/nuttx/arch/x86/src/qemu/Make.defs index 5793ecd09..0010f3f3e 100755 --- a/nuttx/arch/x86/src/qemu/Make.defs +++ b/nuttx/arch/x86/src/qemu/Make.defs @@ -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 diff --git a/nuttx/arch/x86/src/qemu/qemu_head.S b/nuttx/arch/x86/src/qemu/qemu_head.S index 9d436b4fd..cf754408f 100755 --- a/nuttx/arch/x86/src/qemu/qemu_head.S +++ b/nuttx/arch/x86/src/qemu/qemu_head.S @@ -40,20 +40,37 @@ #include /**************************************************************************** - * .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 */ +__start: + /* Set up the stack */ - .set STACKSIZE, 0x4000 /* That is, 16k */ - .comm stack, STACKSIZE, 32 /* Reserve 16k stack on a quadword boundary */ + mov $(idle_stack + CONFIG_IDLETHREAD_STACKSIZE), %esp + + /* Multiboot setup */ -__start: - mov $(stack + STACKSIZE), %esp /* Set up the stack */ 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 */ \ No newline at end of file diff --git a/nuttx/arch/x86/src/qemu/qemu_lowputc.c b/nuttx/arch/x86/src/qemu/qemu_lowputc.c index 16d4425cc..4e3471e60 100644 --- a/nuttx/arch/x86/src/qemu/qemu_lowputc.c +++ b/nuttx/arch/x86/src/qemu/qemu_lowputc.c @@ -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); } diff --git a/nuttx/arch/x86/src/qemu/qemu_lowsetup.c b/nuttx/arch/x86/src/qemu/qemu_lowsetup.c index ef48f0e7b..aac19042d 100644 --- a/nuttx/arch/x86/src/qemu/qemu_lowsetup.c +++ b/nuttx/arch/x86/src/qemu/qemu_lowsetup.c @@ -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 ****************************************************************************/ diff --git a/nuttx/arch/x86/src/qemu/qemu_vectors.S b/nuttx/arch/x86/src/qemu/qemu_vectors.S new file mode 100755 index 000000000..93cac4b3d --- /dev/null +++ b/nuttx/arch/x86/src/qemu/qemu_vectors.S @@ -0,0 +1,395 @@ +/**************************************************************************** + * arch/x86/src/qemu/qemu_head.S + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 +#include + +/**************************************************************************** + * 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 */ -- cgit v1.2.3