forked from Imagelibrary/rtems
bsps: Move interrupt controller support to bsps
This patch is a part of the BSP source reorganization. Update #3285.
This commit is contained in:
170
bsps/i386/shared/irq/elcr.c
Normal file
170
bsps/i386/shared/irq/elcr.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*-
|
||||
* Copyright (c) 2004 John Baldwin <jhb@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef __rtems__
|
||||
__FBSDID("$FreeBSD$");
|
||||
#endif /* __rtems__ */
|
||||
|
||||
/*
|
||||
* The ELCR is a register that controls the trigger mode and polarity of
|
||||
* EISA and ISA interrupts. In FreeBSD 3.x and 4.x, the ELCR was only
|
||||
* consulted for determining the appropriate trigger mode of EISA
|
||||
* interrupts when using an APIC. However, it seems that almost all
|
||||
* systems that include PCI also include an ELCR that manages the ISA
|
||||
* IRQs 0 through 15. Thus, we check for the presence of an ELCR on
|
||||
* every machine by checking to see if the values found at bootup are
|
||||
* sane. Note that the polarity of ISA and EISA IRQs are linked to the
|
||||
* trigger mode. All edge triggered IRQs use active-hi polarity, and
|
||||
* all level triggered interrupts use active-lo polarity.
|
||||
*
|
||||
* The format of the ELCR is simple: it is a 16-bit bitmap where bit 0
|
||||
* controls IRQ 0, bit 1 controls IRQ 1, etc. If the bit is zero, the
|
||||
* associated IRQ is edge triggered. If the bit is one, the IRQ is
|
||||
* level triggered.
|
||||
*/
|
||||
|
||||
#ifndef __rtems__
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/systm.h>
|
||||
#include <machine/intr_machdep.h>
|
||||
#endif /* __rtems__ */
|
||||
|
||||
#ifdef __rtems__
|
||||
#include <bsp.h>
|
||||
#include "i386_io.h"
|
||||
#include <errno.h>
|
||||
#include "elcr.h"
|
||||
#endif /* __rtems__ */
|
||||
|
||||
#define ELCR_PORT 0x4d0
|
||||
#define ELCR_MASK(irq) (1 << (irq))
|
||||
|
||||
static int elcr_status;
|
||||
#ifdef __rtems__
|
||||
static
|
||||
#endif /* __rtems__ */
|
||||
int elcr_found;
|
||||
|
||||
#ifdef __rtems__
|
||||
#undef printf
|
||||
#define printf printk
|
||||
#define bootverbose 1
|
||||
#define KASSERT(...)
|
||||
#endif /* __rtems__ */
|
||||
|
||||
/*
|
||||
* Check to see if we have what looks like a valid ELCR. We do this by
|
||||
* verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
|
||||
*/
|
||||
int
|
||||
elcr_probe(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
elcr_status = inb(ELCR_PORT) | inb(ELCR_PORT + 1) << 8;
|
||||
if ((elcr_status & (ELCR_MASK(0) | ELCR_MASK(1) | ELCR_MASK(2) |
|
||||
ELCR_MASK(8) | ELCR_MASK(13))) != 0)
|
||||
return (ENXIO);
|
||||
if (bootverbose) {
|
||||
printf("ELCR Found. ISA IRQs programmed as:\n");
|
||||
for (i = 0; i < 16; i++)
|
||||
printf(" %2d", i);
|
||||
printf("\n");
|
||||
for (i = 0; i < 16; i++)
|
||||
if (elcr_status & ELCR_MASK(i))
|
||||
printf(" L");
|
||||
else
|
||||
printf(" E");
|
||||
printf("\n");
|
||||
}
|
||||
#ifndef __rtems__
|
||||
if (resource_disabled("elcr", 0))
|
||||
return (ENXIO);
|
||||
#endif /* __rtems__ */
|
||||
elcr_found = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 for level trigger, 0 for edge.
|
||||
*/
|
||||
enum intr_trigger
|
||||
elcr_read_trigger(u_int irq)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
if (!elcr_found)
|
||||
return INTR_TRIGGER_EDGE;
|
||||
#endif /* __rtems__ */
|
||||
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
|
||||
KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
|
||||
if (elcr_status & ELCR_MASK(irq))
|
||||
return (INTR_TRIGGER_LEVEL);
|
||||
else
|
||||
return (INTR_TRIGGER_EDGE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
|
||||
* and a mode of 1 means level triggered.
|
||||
*/
|
||||
void
|
||||
elcr_write_trigger(u_int irq, enum intr_trigger trigger)
|
||||
{
|
||||
int new_status;
|
||||
|
||||
#ifdef __rtems__
|
||||
if (!elcr_found)
|
||||
return;
|
||||
#endif /* __rtems__ */
|
||||
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
|
||||
KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
|
||||
if (trigger == INTR_TRIGGER_LEVEL)
|
||||
new_status = elcr_status | ELCR_MASK(irq);
|
||||
else
|
||||
new_status = elcr_status & ~ELCR_MASK(irq);
|
||||
if (new_status == elcr_status)
|
||||
return;
|
||||
elcr_status = new_status;
|
||||
if (irq >= 8)
|
||||
outb(ELCR_PORT + 1, elcr_status >> 8);
|
||||
else
|
||||
outb(ELCR_PORT, elcr_status & 0xff);
|
||||
}
|
||||
|
||||
void
|
||||
elcr_resume(void)
|
||||
{
|
||||
#ifdef __rtems__
|
||||
if (!elcr_found)
|
||||
return;
|
||||
#endif /* __rtems__ */
|
||||
|
||||
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
|
||||
outb(ELCR_PORT, elcr_status & 0xff);
|
||||
outb(ELCR_PORT + 1, elcr_status >> 8);
|
||||
}
|
||||
37
bsps/i386/shared/irq/elcr.h
Normal file
37
bsps/i386/shared/irq/elcr.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2016 Chris Johns <chrisj@rtems.org>
|
||||
*
|
||||
* Header for the FreeBSD ported elcr.c
|
||||
*/
|
||||
|
||||
#ifndef _IRQ_ELCR_H_
|
||||
#define _IRQ_ELCR_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
enum intr_trigger {
|
||||
INTR_TRIGGER_EDGE,
|
||||
INTR_TRIGGER_LEVEL
|
||||
};
|
||||
|
||||
/*
|
||||
* Check to see if we have what looks like a valid ELCR. We do this by
|
||||
* verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
|
||||
*/
|
||||
int elcr_probe(void);
|
||||
|
||||
/*
|
||||
* Returns 1 for level trigger, 0 for edge.
|
||||
*/
|
||||
enum intr_trigger elcr_read_trigger(u_int irq);
|
||||
|
||||
/*
|
||||
* Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
|
||||
* and a mode of 1 means level triggered.
|
||||
*/
|
||||
void elcr_write_trigger(u_int irq, enum intr_trigger trigger);
|
||||
|
||||
void elcr_resume(void);
|
||||
|
||||
|
||||
#endif
|
||||
381
bsps/i386/shared/irq/idt.c
Normal file
381
bsps/i386/shared/irq/idt.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* cpu.c - This file contains implementation of C function to
|
||||
* instantiate IDT entries. More detailled information can be found
|
||||
* on Intel site and more precisely in the following book :
|
||||
*
|
||||
* Pentium Processor family
|
||||
* Developper's Manual
|
||||
*
|
||||
* Volume 3 : Architecture and Programming Manual
|
||||
*
|
||||
* Copyright (C) 1998 Eric Valette (valette@crf.canon.fr)
|
||||
* Canon Centre Recherche France.
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems/score/cpu.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <bsp/tblsizes.h>
|
||||
|
||||
/*
|
||||
* This locking is not enough if IDT is changed at runtime
|
||||
* and entry can be changed for vector which is enabled
|
||||
* at change time. But such use is broken anyway.
|
||||
* Protect code only against concurrent changes.
|
||||
* Even that is probably unnecessary if different
|
||||
* entries are changed concurrently.
|
||||
*/
|
||||
RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_idt_access_lock, "rtems_idt_access_lock" );
|
||||
|
||||
static rtems_raw_irq_connect_data* raw_irq_table;
|
||||
static rtems_raw_irq_connect_data default_raw_irq_entry;
|
||||
static interrupt_gate_descriptor default_idt_entry;
|
||||
static rtems_raw_irq_global_settings* local_settings;
|
||||
|
||||
void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
|
||||
rtems_raw_irq_hdl hdl)
|
||||
{
|
||||
idtEntry->low_offsets_bits = (((unsigned) hdl) & 0xffff);
|
||||
idtEntry->segment_selector = i386_get_cs();
|
||||
idtEntry->fixed_value_bits = 0;
|
||||
idtEntry->gate_type = 0xe;
|
||||
idtEntry->privilege = 0;
|
||||
idtEntry->present = 1;
|
||||
idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
|
||||
}
|
||||
|
||||
rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
|
||||
{
|
||||
uint32_t hdl;
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned limit;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if(index >= limit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdl = (idt_entry_tbl[index].low_offsets_bits |
|
||||
(idt_entry_tbl[index].high_offsets_bits << 16));
|
||||
return (rtems_raw_irq_hdl) hdl;
|
||||
}
|
||||
|
||||
int i386_set_idt_entry (const rtems_raw_irq_connect_data* irq)
|
||||
{
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned limit;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if (irq->idtIndex >= limit) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if default handler is actually connected. If not issue an error.
|
||||
* You must first get the current handler via i386_get_current_idt_entry
|
||||
* and then disconnect it using i386_delete_idt_entry.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
|
||||
|
||||
raw_irq_table [irq->idtIndex] = *irq;
|
||||
create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
|
||||
if (irq->on)
|
||||
irq->on(irq);
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void _CPU_ISR_install_vector (uint32_t vector,
|
||||
proc_ptr hdl,
|
||||
proc_ptr * oldHdl)
|
||||
{
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned limit;
|
||||
interrupt_gate_descriptor new;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if (vector >= limit) {
|
||||
return;
|
||||
}
|
||||
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
|
||||
* ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
|
||||
(idt_entry_tbl[vector].high_offsets_bits << 16);
|
||||
|
||||
create_interrupt_gate_descriptor(&new, hdl);
|
||||
idt_entry_tbl[vector] = new;
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
|
||||
}
|
||||
|
||||
int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
|
||||
{
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned limit;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if (irq->idtIndex >= limit) {
|
||||
return 0;
|
||||
}
|
||||
raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
|
||||
|
||||
*irq = raw_irq_table [irq->idtIndex];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
|
||||
{
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned limit;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if (irq->idtIndex >= limit) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check if handler passed is actually connected. If not issue an error.
|
||||
* You must first get the current handler via i386_get_current_idt_entry
|
||||
* and then disconnect it using i386_delete_idt_entry.
|
||||
* RATIONALE : to always have the same transition by forcing the user
|
||||
* to get the previous handler before accepting to disconnect.
|
||||
*/
|
||||
if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
|
||||
return 0;
|
||||
}
|
||||
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
|
||||
|
||||
idt_entry_tbl[irq->idtIndex] = default_idt_entry;
|
||||
|
||||
if (irq->off)
|
||||
irq->off(irq);
|
||||
|
||||
raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
|
||||
raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Caution this function assumes the IDTR has been already set.
|
||||
*/
|
||||
int i386_init_idt (rtems_raw_irq_global_settings* config)
|
||||
{
|
||||
unsigned limit;
|
||||
unsigned i;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
|
||||
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert limit into number of entries */
|
||||
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if (config->idtSize != limit) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* store various accelarators
|
||||
*/
|
||||
raw_irq_table = config->rawIrqHdlTbl;
|
||||
local_settings = config;
|
||||
default_raw_irq_entry = config->defaultRawEntry;
|
||||
|
||||
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
|
||||
|
||||
create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
|
||||
|
||||
for (i=0; i < limit; i++) {
|
||||
interrupt_gate_descriptor new;
|
||||
create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
|
||||
idt_entry_tbl[i] = new;
|
||||
if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
|
||||
raw_irq_table[i].on(&raw_irq_table[i]);
|
||||
}
|
||||
else {
|
||||
raw_irq_table[i].off(&raw_irq_table[i]);
|
||||
}
|
||||
}
|
||||
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i386_get_idt_config (rtems_raw_irq_global_settings** config)
|
||||
{
|
||||
*config = local_settings;
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
|
||||
segment_descriptors* sd)
|
||||
{
|
||||
uint16_t gdt_limit;
|
||||
uint16_t tmp_segment = 0;
|
||||
segment_descriptors* gdt_entry_tbl;
|
||||
uint8_t present;
|
||||
|
||||
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
|
||||
|
||||
if (segment_selector_index >= (gdt_limit+1)/8) {
|
||||
/* index to GDT table out of bounds */
|
||||
return 0;
|
||||
}
|
||||
if (segment_selector_index == 0) {
|
||||
/* index 0 is not usable */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* put prepared descriptor into the GDT */
|
||||
present = sd->present;
|
||||
sd->present = 0;
|
||||
gdt_entry_tbl[segment_selector_index].present = 0;
|
||||
RTEMS_COMPILER_MEMORY_BARRIER();
|
||||
gdt_entry_tbl[segment_selector_index] = *sd;
|
||||
RTEMS_COMPILER_MEMORY_BARRIER();
|
||||
gdt_entry_tbl[segment_selector_index].present = present;
|
||||
sd->present = present;
|
||||
/*
|
||||
* Now, reload all segment registers so that the possible changes takes effect.
|
||||
*/
|
||||
__asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
|
||||
"movw %%es,%0 ; movw %0,%%es\n\t"
|
||||
"movw %%fs,%0 ; movw %0,%%fs\n\t"
|
||||
"movw %%gs,%0 ; movw %0,%%gs\n\t"
|
||||
"movw %%ss,%0 ; movw %0,%%ss"
|
||||
: "=r" (tmp_segment)
|
||||
: "0" (tmp_segment)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void i386_fill_segment_desc_base(uint32_t base,
|
||||
segment_descriptors* sd)
|
||||
{
|
||||
sd->base_address_15_0 = base & 0xffff;
|
||||
sd->base_address_23_16 = (base >> 16) & 0xff;
|
||||
sd->base_address_31_24 = (base >> 24) & 0xff;
|
||||
}
|
||||
|
||||
void i386_fill_segment_desc_limit(uint32_t limit,
|
||||
segment_descriptors* sd)
|
||||
{
|
||||
sd->granularity = 0;
|
||||
if (limit > 65535) {
|
||||
sd->granularity = 1;
|
||||
limit /= 4096;
|
||||
}
|
||||
sd->limit_15_0 = limit & 0xffff;
|
||||
sd->limit_19_16 = (limit >> 16) & 0xf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Caution this function assumes the GDTR has been already set.
|
||||
*/
|
||||
uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
|
||||
uint32_t limit)
|
||||
{
|
||||
segment_descriptors gdt_entry;
|
||||
memset(&gdt_entry, 0, sizeof(gdt_entry));
|
||||
|
||||
i386_fill_segment_desc_limit(limit, &gdt_entry);
|
||||
i386_fill_segment_desc_base(base, &gdt_entry);
|
||||
/*
|
||||
* set up descriptor type (this may well becomes a parameter if needed)
|
||||
*/
|
||||
gdt_entry.type = 2; /* Data R/W */
|
||||
gdt_entry.descriptor_type = 1; /* Code or Data */
|
||||
gdt_entry.privilege = 0; /* ring 0 */
|
||||
gdt_entry.present = 1; /* not present */
|
||||
|
||||
/*
|
||||
* Now, reload all segment registers so the limit takes effect.
|
||||
*/
|
||||
return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
|
||||
}
|
||||
|
||||
uint16_t i386_next_empty_gdt_entry ()
|
||||
{
|
||||
uint16_t gdt_limit;
|
||||
segment_descriptors* gdt_entry_tbl;
|
||||
/* initial amount of filled descriptors */
|
||||
static uint16_t segment_selector_index = NUM_SYSTEM_GDT_DESCRIPTORS - 1;
|
||||
|
||||
segment_selector_index += 1;
|
||||
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
|
||||
if (segment_selector_index >= (gdt_limit+1)/8) {
|
||||
return 0;
|
||||
}
|
||||
return segment_selector_index;
|
||||
}
|
||||
|
||||
uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
|
||||
segment_descriptors* struct_to_fill)
|
||||
{
|
||||
uint16_t gdt_limit;
|
||||
segment_descriptors* gdt_entry_tbl;
|
||||
|
||||
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
|
||||
|
||||
if (segment_selector_index >= (gdt_limit+1)/8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*struct_to_fill = gdt_entry_tbl[segment_selector_index];
|
||||
return segment_selector_index;
|
||||
}
|
||||
|
||||
segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
|
||||
{
|
||||
uint16_t gdt_limit;
|
||||
segment_descriptors* gdt_entry_tbl;
|
||||
|
||||
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
|
||||
|
||||
if (segment_selector_index >= (gdt_limit+1)/8) {
|
||||
return 0;
|
||||
}
|
||||
return &gdt_entry_tbl[segment_selector_index];
|
||||
}
|
||||
|
||||
uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
|
||||
{
|
||||
uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
|
||||
if (gdt_entry->granularity) {
|
||||
return lim*4096+4095;
|
||||
}
|
||||
return lim;
|
||||
}
|
||||
405
bsps/i386/shared/irq/irq.c
Normal file
405
bsps/i386/shared/irq/irq.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* This file contains the implementation of the function described in irq.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 embedded brains GmbH
|
||||
* Copyright (C) 1998 valette@crf.canon.fr
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <bsp/irq-generic.h>
|
||||
#include <rtems/score/cpu.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#include "elcr.h"
|
||||
|
||||
RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_i8259_access_lock, "rtems_i8259_access_lock" );
|
||||
|
||||
/*
|
||||
* pointer to the mask representing the additionnal irq vectors
|
||||
* that must be disabled when a particular entry is activated.
|
||||
* They will be dynamically computed from teh prioruty table given
|
||||
* in BSP_rtems_irq_mngt_set();
|
||||
* CAUTION : this table is accessed directly by interrupt routine
|
||||
* prologue.
|
||||
*/
|
||||
static rtems_i8259_masks irq_mask_or_tbl[BSP_IRQ_LINES_NUMBER];
|
||||
|
||||
/*
|
||||
* Stats of interrupts dispatched.
|
||||
*/
|
||||
static uint32_t irq_count[BSP_IRQ_VECTOR_NUMBER] = {0};
|
||||
static uint32_t spurious_count;
|
||||
|
||||
/*
|
||||
* Edge or level trigger interrupts.
|
||||
*/
|
||||
static enum intr_trigger irq_trigger[BSP_IRQ_LINES_NUMBER];
|
||||
|
||||
/*-------------------------------------------------------------------------+
|
||||
| Cache for 1st and 2nd PIC IRQ line's mssk (enabled or disabled) register.
|
||||
+--------------------------------------------------------------------------*/
|
||||
/*
|
||||
* lower byte is interrupt mask on the master PIC.
|
||||
* while upper bits are interrupt on the slave PIC.
|
||||
* This cache is initialized in ldseg.s
|
||||
*/
|
||||
static rtems_i8259_masks i8259a_imr_cache = 0xFFFB;
|
||||
static rtems_i8259_masks i8259a_in_progress = 0;
|
||||
|
||||
static inline
|
||||
void BSP_i8259a_irq_update_master_imr( void )
|
||||
{
|
||||
rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
|
||||
outport_byte( PIC_MASTER_IMR_IO_PORT, mask & 0xff );
|
||||
}
|
||||
|
||||
static inline
|
||||
void BSP_i8259a_irq_update_slave_imr( void )
|
||||
{
|
||||
rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
|
||||
outport_byte( PIC_SLAVE_IMR_IO_PORT, ( mask >> 8 ) & 0xff );
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the stats.
|
||||
*/
|
||||
uint32_t BSP_irq_count_dump(FILE *f)
|
||||
{
|
||||
uint32_t tot = 0;
|
||||
int i;
|
||||
if ( !f )
|
||||
f = stdout;
|
||||
fprintf(f,"SPURIOUS: %9"PRIu32"\n", spurious_count);
|
||||
for ( i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++ ) {
|
||||
char type = '-';
|
||||
if (i < BSP_IRQ_LINES_NUMBER)
|
||||
type = irq_trigger[i] == INTR_TRIGGER_EDGE ? 'E' : 'L';
|
||||
tot += irq_count[i];
|
||||
fprintf(f,"IRQ %2u: %c %9"PRIu32"\n", i, type, irq_count[i]);
|
||||
}
|
||||
return tot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the IRQ valid?
|
||||
*/
|
||||
static inline bool BSP_i8259a_irq_valid(const rtems_irq_number irqLine)
|
||||
{
|
||||
return ((int)irqLine >= BSP_IRQ_VECTOR_LOWEST_OFFSET) &&
|
||||
((int)irqLine <= BSP_IRQ_MAX_ON_i8259A);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the IRR register. The default.
|
||||
*/
|
||||
static inline uint8_t BSP_i8259a_irq_int_request_reg(uint32_t ioport)
|
||||
{
|
||||
uint8_t isr;
|
||||
inport_byte(ioport, isr);
|
||||
return isr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the ISR register. Keep the default of the IRR.
|
||||
*/
|
||||
static inline uint8_t BSP_i8259a_irq_in_service_reg(uint32_t ioport)
|
||||
{
|
||||
uint8_t isr;
|
||||
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS);
|
||||
inport_byte(ioport, isr);
|
||||
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR);
|
||||
return isr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------+
|
||||
| Function: BSP_irq_disable_at_i8259a
|
||||
| Description: Mask IRQ line in appropriate PIC chip.
|
||||
| Global Variables: i8259a_imr_cache, i8259a_in_progress
|
||||
| Arguments: vector_offset - number of IRQ line to mask.
|
||||
| Returns: 0 is OK.
|
||||
+--------------------------------------------------------------------------*/
|
||||
static int BSP_irq_disable_at_i8259a(const rtems_irq_number irqLine)
|
||||
{
|
||||
unsigned short mask;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
|
||||
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
mask = 1 << irqLine;
|
||||
i8259a_imr_cache |= mask;
|
||||
|
||||
if (irqLine < 8)
|
||||
{
|
||||
BSP_i8259a_irq_update_master_imr();
|
||||
}
|
||||
else
|
||||
{
|
||||
BSP_i8259a_irq_update_slave_imr();
|
||||
}
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------+
|
||||
| Function: BSP_irq_enable_at_i8259a
|
||||
| Description: Unmask IRQ line in appropriate PIC chip.
|
||||
| Global Variables: i8259a_imr_cache, i8259a_in_progress
|
||||
| Arguments: irqLine - number of IRQ line to mask.
|
||||
| Returns: Nothing.
|
||||
+--------------------------------------------------------------------------*/
|
||||
static int BSP_irq_enable_at_i8259a(const rtems_irq_number irqLine)
|
||||
{
|
||||
unsigned short mask;
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
uint8_t isr;
|
||||
uint8_t irr;
|
||||
|
||||
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
mask = 1 << irqLine;
|
||||
i8259a_imr_cache &= ~mask;
|
||||
|
||||
if (irqLine < 8)
|
||||
{
|
||||
isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
|
||||
irr = BSP_i8259a_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT);
|
||||
BSP_i8259a_irq_update_master_imr();
|
||||
}
|
||||
else
|
||||
{
|
||||
isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
|
||||
irr = BSP_i8259a_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT);
|
||||
BSP_i8259a_irq_update_slave_imr();
|
||||
}
|
||||
|
||||
if (((isr ^ irr) & mask) != 0)
|
||||
printk("i386: isr=%x irr=%x\n", isr, irr);
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
return 0;
|
||||
} /* mask_irq */
|
||||
|
||||
/*-------------------------------------------------------------------------+
|
||||
| Function: BSP_irq_ack_at_i8259a
|
||||
| Description: Signal generic End Of Interrupt (EOI) to appropriate PIC.
|
||||
| Global Variables: None.
|
||||
| Arguments: irqLine - number of IRQ line to acknowledge.
|
||||
| Returns: Nothing.
|
||||
+--------------------------------------------------------------------------*/
|
||||
static int BSP_irq_ack_at_i8259a(const rtems_irq_number irqLine)
|
||||
{
|
||||
uint8_t slave_isr = 0;
|
||||
|
||||
if (irqLine >= 8) {
|
||||
outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI);
|
||||
slave_isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only issue the EOI to the master if there are no more interrupts in
|
||||
* service for the slave. i8259a data sheet page 18, The Special Fully Nested
|
||||
* Mode, b.
|
||||
*/
|
||||
if (slave_isr == 0)
|
||||
outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI);
|
||||
|
||||
return 0;
|
||||
|
||||
} /* ackIRQ */
|
||||
|
||||
/*
|
||||
* ------------------------ RTEMS Irq helper functions ----------------
|
||||
*/
|
||||
|
||||
static rtems_irq_prio irqPrioTable[BSP_IRQ_LINES_NUMBER]={
|
||||
/*
|
||||
* actual priorities for each interrupt source:
|
||||
* 0 means that only current interrupt is masked
|
||||
* 255 means all other interrupts are masked
|
||||
* The second entry has a priority of 255 because
|
||||
* it is the slave pic entry and is should always remain
|
||||
* unmasked.
|
||||
*/
|
||||
0,0,
|
||||
255,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
static void compute_i8259_masks_from_prio (void)
|
||||
{
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
|
||||
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
/*
|
||||
* Always mask at least current interrupt to prevent re-entrance
|
||||
*/
|
||||
for (i=0; i < BSP_IRQ_LINES_NUMBER; i++) {
|
||||
* ((unsigned short*) &irq_mask_or_tbl[i]) = (1 << i);
|
||||
for (j = 0; j < BSP_IRQ_LINES_NUMBER; j++) {
|
||||
/*
|
||||
* Mask interrupts at i8259 level that have a lower priority
|
||||
*/
|
||||
if (irqPrioTable [i] > irqPrioTable [j]) {
|
||||
* ((unsigned short*) &irq_mask_or_tbl[i]) |= (1 << j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
|
||||
}
|
||||
|
||||
static inline bool bsp_interrupt_vector_is_valid(rtems_vector_number vector)
|
||||
{
|
||||
return BSP_i8259a_irq_valid((const rtems_irq_number) vector);
|
||||
}
|
||||
|
||||
void bsp_interrupt_vector_enable(rtems_vector_number vector)
|
||||
{
|
||||
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
|
||||
BSP_irq_enable_at_i8259a(vector);
|
||||
}
|
||||
|
||||
void bsp_interrupt_vector_disable(rtems_vector_number vector)
|
||||
{
|
||||
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
|
||||
BSP_irq_disable_at_i8259a(vector);
|
||||
}
|
||||
|
||||
rtems_status_code bsp_interrupt_facility_initialize(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* set up internal tables used by rtems interrupt prologue
|
||||
*/
|
||||
compute_i8259_masks_from_prio();
|
||||
|
||||
/*
|
||||
* must enable slave pic anyway
|
||||
*/
|
||||
BSP_irq_enable_at_i8259a(2);
|
||||
|
||||
/*
|
||||
* Probe the ELCR.
|
||||
*/
|
||||
elcr_probe();
|
||||
|
||||
for (i = 0; i < BSP_IRQ_LINES_NUMBER; i++)
|
||||
irq_trigger[i] = elcr_read_trigger(i);
|
||||
|
||||
return RTEMS_SUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global so the asm handler can call it.
|
||||
*/
|
||||
void BSP_dispatch_isr(int vector);
|
||||
|
||||
void BSP_dispatch_isr(int vector)
|
||||
{
|
||||
rtems_interrupt_lock_context lock_context;
|
||||
rtems_i8259_masks in_progress_save = 0;
|
||||
|
||||
if (vector < BSP_IRQ_VECTOR_NUMBER) {
|
||||
/*
|
||||
* Hardware?
|
||||
*/
|
||||
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
|
||||
|
||||
rtems_interrupt_lock_acquire_isr(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
/*
|
||||
* See if this is a spurious interrupt.
|
||||
*/
|
||||
if ((vector == 7 || vector == 15)) {
|
||||
/*
|
||||
* Only check it there no handler for 7 or 15.
|
||||
*/
|
||||
if (bsp_interrupt_handler_is_empty(vector)) {
|
||||
/*
|
||||
* Read the ISR register to see if IRQ 7/15 is really pending.
|
||||
*/
|
||||
uint8_t isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
|
||||
if ((isr & (1 << 7)) == 0) {
|
||||
++spurious_count;
|
||||
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the current cached value for the IMR. It will have the bit for this
|
||||
* vector clear.
|
||||
*/
|
||||
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
|
||||
in_progress_save = i8259a_in_progress;
|
||||
i8259a_in_progress |= irq_mask_or_tbl[vector];
|
||||
BSP_i8259a_irq_update_master_imr();
|
||||
BSP_i8259a_irq_update_slave_imr();
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not use auto-EOI as some slave PIC do not work correctly.
|
||||
*/
|
||||
BSP_irq_ack_at_i8259a(vector);
|
||||
|
||||
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the interrupt.
|
||||
*/
|
||||
irq_count[vector]++;
|
||||
|
||||
RTEMS_COMPILER_MEMORY_BARRIER();
|
||||
/*
|
||||
* Allow nesting.
|
||||
*/
|
||||
__asm__ __volatile__("sti");
|
||||
|
||||
bsp_interrupt_handler_dispatch(vector);
|
||||
|
||||
/*
|
||||
* Disallow nesting.
|
||||
*/
|
||||
__asm__ __volatile__("cli");
|
||||
|
||||
RTEMS_COMPILER_MEMORY_BARRIER();
|
||||
|
||||
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
|
||||
|
||||
rtems_interrupt_lock_acquire_isr(&rtems_i8259_access_lock, &lock_context);
|
||||
|
||||
/*
|
||||
* Put the mask back but keep this vector masked if the trigger type is
|
||||
* level. The driver or a thread level interrupt server needs to enable it
|
||||
* again.
|
||||
*/
|
||||
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
|
||||
i8259a_in_progress = in_progress_save;
|
||||
BSP_i8259a_irq_update_master_imr();
|
||||
BSP_i8259a_irq_update_slave_imr();
|
||||
}
|
||||
|
||||
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
268
bsps/i386/shared/irq/irq_asm.S
Normal file
268
bsps/i386/shared/irq/irq_asm.S
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* This file contains the implementation of the function described in irq.h
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 1998 valette@crf.canon.fr
|
||||
*
|
||||
* COPYRIGHT (c) 1989-2011.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems/asm.h>
|
||||
#include <rtems/system.h>
|
||||
#include <bspopts.h>
|
||||
#include <rtems/score/cpu.h>
|
||||
#include <rtems/score/percpu.h>
|
||||
|
||||
#include <bsp.h> /* to establish dependency on prototype */
|
||||
|
||||
#ifndef CPU_STACK_ALIGNMENT
|
||||
#error "Missing header? CPU_STACK_ALIGNMENT is not defined here"
|
||||
#endif
|
||||
|
||||
/* Stack frame we use for intermediate storage */
|
||||
#define ARG_OFF 0
|
||||
#define MSK_OFF 4 /* not used any more */
|
||||
#define EBX_OFF 8 /* ebx */
|
||||
#define EBP_OFF 12 /* code restoring ebp/esp relies on */
|
||||
#define ESP_OFF 16 /* esp being on top of ebp! */
|
||||
#ifdef __SSE__
|
||||
/* need to be on 16 byte boundary for SSE, add 12 to do that */
|
||||
#define FRM_SIZ (20+12+512)
|
||||
#define SSE_OFF 32
|
||||
#else
|
||||
#define FRM_SIZ 20
|
||||
#endif
|
||||
|
||||
BEGIN_CODE
|
||||
|
||||
SYM (_ISR_Handler):
|
||||
/*
|
||||
* Before this was point is reached the vectors unique
|
||||
* entry point did the following:
|
||||
*
|
||||
* 1. saved scratch registers registers eax edx ecx"
|
||||
* 2. put the vector number in ecx.
|
||||
*
|
||||
* BEGINNING OF ESTABLISH SEGMENTS
|
||||
*
|
||||
* WARNING: If an interrupt can occur when the segments are
|
||||
* not correct, then this is where we should establish
|
||||
* the segments. In addition to establishing the
|
||||
* segments, it may be necessary to establish a stack
|
||||
* in the current data area on the outermost interrupt.
|
||||
*
|
||||
* NOTE: If the previous values of the segment registers are
|
||||
* pushed, do not forget to adjust SAVED_REGS.
|
||||
*
|
||||
* NOTE: Make sure the exit code which restores these
|
||||
* when this type of code is needed.
|
||||
*/
|
||||
|
||||
/***** ESTABLISH SEGMENTS CODE GOES HERE ******/
|
||||
|
||||
/*
|
||||
* END OF ESTABLISH SEGMENTS
|
||||
*/
|
||||
|
||||
/*
|
||||
* Establish an aligned stack frame
|
||||
* original sp
|
||||
* saved ebx
|
||||
* saved ebp
|
||||
* saved irq mask
|
||||
* vector arg to BSP_dispatch_isr <- aligned SP
|
||||
*/
|
||||
movl esp, eax
|
||||
subl $FRM_SIZ, esp
|
||||
andl $ - CPU_STACK_ALIGNMENT, esp
|
||||
movl ebx, EBX_OFF(esp)
|
||||
movl eax, ESP_OFF(esp)
|
||||
movl ebp, EBP_OFF(esp)
|
||||
|
||||
/*
|
||||
* GCC versions starting with 4.3 no longer place the cld
|
||||
* instruction before string operations. We need to ensure
|
||||
* it is set correctly for ISR handlers.
|
||||
*/
|
||||
cld
|
||||
|
||||
#ifdef __SSE__
|
||||
/* NOTE: SSE only is supported if the BSP enables fxsave/fxrstor
|
||||
* to save/restore SSE context! This is so far only implemented
|
||||
* for pc386!.
|
||||
*/
|
||||
|
||||
/* We save SSE here (on the task stack) because we possibly
|
||||
* call other C-code (besides the ISR, namely _Thread_Dispatch())
|
||||
*/
|
||||
/* don't wait here; a possible exception condition will eventually be
|
||||
* detected when the task resumes control and executes a FP instruction
|
||||
fwait
|
||||
*/
|
||||
fxsave SSE_OFF(esp)
|
||||
fninit /* clean-slate FPU */
|
||||
movl $0x1f80, ARG_OFF(esp) /* use ARG_OFF as scratch space */
|
||||
ldmxcsr ARG_OFF(esp) /* clean-slate MXCSR */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Now switch stacks if necessary
|
||||
*/
|
||||
|
||||
PUBLIC (ISR_STOP)
|
||||
ISR_STOP:
|
||||
.check_stack_switch:
|
||||
movl esp, ebp /* ebp = previous stack pointer */
|
||||
|
||||
#ifdef RTEMS_SMP
|
||||
call SYM(_CPU_SMP_Get_current_processor)
|
||||
sall $PER_CPU_CONTROL_SIZE_LOG2, eax
|
||||
addl $SYM(_Per_CPU_Information), eax
|
||||
movl eax, ebx
|
||||
#else
|
||||
movl $SYM(_Per_CPU_Information), ebx
|
||||
#endif
|
||||
|
||||
/* is this the outermost interrupt? */
|
||||
cmpl $0, PER_CPU_ISR_NEST_LEVEL(ebx)
|
||||
jne nested /* No, then continue */
|
||||
movl PER_CPU_INTERRUPT_STACK_HIGH(ebx), esp
|
||||
|
||||
/*
|
||||
* We want to insure that the old stack pointer is in ebp
|
||||
* By saving it on every interrupt, all we have to do is
|
||||
* movl ebp->esp near the end of every interrupt.
|
||||
*/
|
||||
|
||||
nested:
|
||||
incl PER_CPU_ISR_NEST_LEVEL(ebx) /* one nest level deeper */
|
||||
incl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx) /* disable
|
||||
multitasking */
|
||||
/*
|
||||
* ECX is preloaded with the vector number; store as arg
|
||||
* on top of stack. Note that _CPU_Interrupt_stack_high
|
||||
* was adjusted in _CPU_Interrupt_stack_setup() (score/rtems/cpu.h)
|
||||
* to make sure there is space.
|
||||
*/
|
||||
|
||||
movl ecx, ARG_OFF(esp) /* store vector arg in stack */
|
||||
call BSP_dispatch_isr
|
||||
|
||||
movl ARG_OFF(esp), ecx /* grab vector arg from stack */
|
||||
|
||||
/*
|
||||
* Restore stack. This moves back to the task stack
|
||||
* when all interrupts are unnested.
|
||||
*/
|
||||
movl ebp, esp
|
||||
|
||||
decl PER_CPU_ISR_NEST_LEVEL(ebx) /* one less ISR nest level */
|
||||
/* If interrupts are nested, */
|
||||
/* then dispatching is disabled */
|
||||
|
||||
decl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx)
|
||||
/* unnest multitasking */
|
||||
/* Is dispatch disabled */
|
||||
jne .exit /* Yes, then exit */
|
||||
|
||||
cmpb $0, PER_CPU_DISPATCH_NEEDED(ebx)
|
||||
/* Is task switch necessary? */
|
||||
jne .schedule /* Yes, then call the scheduler */
|
||||
jmp .exit /* No, exit */
|
||||
|
||||
.schedule:
|
||||
/*
|
||||
* the scratch registers have already been saved and we are already
|
||||
* back on the thread system stack. So we can call _Thread_Dispatch
|
||||
* directly
|
||||
*/
|
||||
call _Thread_Dispatch
|
||||
/*
|
||||
* fall through exit to restore complete contex (scratch registers
|
||||
* eip, CS, Flags).
|
||||
*/
|
||||
.exit:
|
||||
|
||||
#ifdef __SSE__
|
||||
fwait
|
||||
fxrstor SSE_OFF(esp)
|
||||
#endif
|
||||
|
||||
/* restore ebx, ebp and original esp */
|
||||
addl $EBX_OFF, esp
|
||||
popl ebx
|
||||
popl ebp
|
||||
popl esp
|
||||
|
||||
/*
|
||||
* BEGINNING OF DE-ESTABLISH SEGMENTS
|
||||
*
|
||||
* NOTE: Make sure there is code here if code is added to
|
||||
* load the segment registers.
|
||||
*
|
||||
*/
|
||||
|
||||
/******* DE-ESTABLISH SEGMENTS CODE GOES HERE ********/
|
||||
|
||||
/*
|
||||
* END OF DE-ESTABLISH SEGMENTS
|
||||
*/
|
||||
popl edx
|
||||
popl ecx
|
||||
popl eax
|
||||
iret
|
||||
|
||||
#define DISTINCT_INTERRUPT_ENTRY(_vector) \
|
||||
.p2align 4 ; \
|
||||
PUBLIC (rtems_irq_prologue_ ## _vector ) ; \
|
||||
SYM (rtems_irq_prologue_ ## _vector ): \
|
||||
pushl eax ; \
|
||||
pushl ecx ; \
|
||||
pushl edx ; \
|
||||
movl $ _vector, ecx ; \
|
||||
jmp SYM (_ISR_Handler) ;
|
||||
|
||||
DISTINCT_INTERRUPT_ENTRY(0)
|
||||
DISTINCT_INTERRUPT_ENTRY(1)
|
||||
DISTINCT_INTERRUPT_ENTRY(2)
|
||||
DISTINCT_INTERRUPT_ENTRY(3)
|
||||
DISTINCT_INTERRUPT_ENTRY(4)
|
||||
DISTINCT_INTERRUPT_ENTRY(5)
|
||||
DISTINCT_INTERRUPT_ENTRY(6)
|
||||
DISTINCT_INTERRUPT_ENTRY(7)
|
||||
DISTINCT_INTERRUPT_ENTRY(8)
|
||||
DISTINCT_INTERRUPT_ENTRY(9)
|
||||
DISTINCT_INTERRUPT_ENTRY(10)
|
||||
DISTINCT_INTERRUPT_ENTRY(11)
|
||||
DISTINCT_INTERRUPT_ENTRY(12)
|
||||
DISTINCT_INTERRUPT_ENTRY(13)
|
||||
DISTINCT_INTERRUPT_ENTRY(14)
|
||||
DISTINCT_INTERRUPT_ENTRY(15)
|
||||
DISTINCT_INTERRUPT_ENTRY(16)
|
||||
|
||||
/*
|
||||
* routine used to initialize the IDT by default
|
||||
*/
|
||||
|
||||
PUBLIC (default_raw_idt_handler)
|
||||
PUBLIC (raw_idt_notify)
|
||||
|
||||
SYM (default_raw_idt_handler):
|
||||
pusha
|
||||
cld
|
||||
mov esp, ebp
|
||||
andl $ - CPU_STACK_ALIGNMENT, esp
|
||||
call raw_idt_notify
|
||||
mov ebp, esp
|
||||
popa
|
||||
iret
|
||||
|
||||
END_CODE
|
||||
|
||||
END
|
||||
191
bsps/i386/shared/irq/irq_init.c
Normal file
191
bsps/i386/shared/irq/irq_init.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/* irq_init.c
|
||||
*
|
||||
* This file contains the implementation of rtems initialization
|
||||
* related to interrupt handling.
|
||||
*
|
||||
* Copyright (c) 2009 embedded brains GmbH
|
||||
* CopyRight (C) 1998 valette@crf.canon.fr
|
||||
*
|
||||
* COPYRIGHT (c) 2011.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* The license and distribution terms for this file may be
|
||||
* found in the file LICENSE in this distribution or at
|
||||
* http://www.rtems.org/license/LICENSE.
|
||||
*/
|
||||
|
||||
#include <rtems/bspIo.h>
|
||||
|
||||
#include <rtems/score/cpu.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <bsp/irq.h>
|
||||
#include <bsp/irq-generic.h>
|
||||
|
||||
/*
|
||||
* rtems prologue generated in irq_asm.S
|
||||
*/
|
||||
extern void rtems_irq_prologue_0(void);
|
||||
extern void rtems_irq_prologue_1(void);
|
||||
extern void rtems_irq_prologue_2(void);
|
||||
extern void rtems_irq_prologue_3(void);
|
||||
extern void rtems_irq_prologue_4(void);
|
||||
extern void rtems_irq_prologue_5(void);
|
||||
extern void rtems_irq_prologue_6(void);
|
||||
extern void rtems_irq_prologue_7(void);
|
||||
extern void rtems_irq_prologue_8(void);
|
||||
extern void rtems_irq_prologue_9(void);
|
||||
extern void rtems_irq_prologue_10(void);
|
||||
extern void rtems_irq_prologue_11(void);
|
||||
extern void rtems_irq_prologue_12(void);
|
||||
extern void rtems_irq_prologue_13(void);
|
||||
extern void rtems_irq_prologue_14(void);
|
||||
extern void rtems_irq_prologue_15(void);
|
||||
extern void rtems_irq_prologue_16(void);
|
||||
/*
|
||||
* default vectors
|
||||
*/
|
||||
extern void default_raw_idt_handler(void);
|
||||
|
||||
/*
|
||||
* default raw on/off function
|
||||
*/
|
||||
static void raw_nop_func(const struct __rtems_raw_irq_connect_data__ *unused)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* default raw isOn function
|
||||
*/
|
||||
static int raw_not_connected(
|
||||
const struct __rtems_raw_irq_connect_data__ *unused
|
||||
)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static rtems_raw_irq_connect_data idtHdl[IDT_SIZE];
|
||||
|
||||
static rtems_raw_irq_hdl rtemsIrq[BSP_IRQ_VECTOR_NUMBER] = {
|
||||
rtems_irq_prologue_0,
|
||||
rtems_irq_prologue_1,
|
||||
rtems_irq_prologue_2,
|
||||
rtems_irq_prologue_3,
|
||||
rtems_irq_prologue_4,
|
||||
rtems_irq_prologue_5,
|
||||
rtems_irq_prologue_6,
|
||||
rtems_irq_prologue_7,
|
||||
rtems_irq_prologue_8,
|
||||
rtems_irq_prologue_9,
|
||||
rtems_irq_prologue_10,
|
||||
rtems_irq_prologue_11,
|
||||
rtems_irq_prologue_12,
|
||||
rtems_irq_prologue_13,
|
||||
rtems_irq_prologue_14,
|
||||
rtems_irq_prologue_15,
|
||||
rtems_irq_prologue_16,
|
||||
};
|
||||
|
||||
static rtems_raw_irq_connect_data defaultRawIrq = {
|
||||
0, /* vectorIdex */
|
||||
default_raw_idt_handler, /* hdl */
|
||||
raw_nop_func, /* on */
|
||||
raw_nop_func, /* off */
|
||||
raw_not_connected /* isOn */
|
||||
};
|
||||
|
||||
static interrupt_gate_descriptor idtEntry;
|
||||
|
||||
static rtems_raw_irq_global_settings raw_initial_config;
|
||||
|
||||
|
||||
/*
|
||||
* This method is called from irq_asm.S and cannot be static.
|
||||
*/
|
||||
void raw_idt_notify(void)
|
||||
{
|
||||
printk("raw_idt_notify has been called \n");
|
||||
}
|
||||
|
||||
void rtems_irq_mngt_init(void)
|
||||
{
|
||||
int i;
|
||||
interrupt_gate_descriptor* idt_entry_tbl;
|
||||
unsigned int limit;
|
||||
rtems_interrupt_level level;
|
||||
|
||||
i386_get_info_from_IDTR(&idt_entry_tbl, &limit);
|
||||
|
||||
/* Convert into number of entries */
|
||||
limit = (limit + 1)/sizeof(interrupt_gate_descriptor);
|
||||
|
||||
if(limit != IDT_SIZE) {
|
||||
printk("IDT table size mismatch !!! System locked\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The interrupt management can be initialized only once
|
||||
* during system bootup and that should happen on boot
|
||||
* CPU so there is no need to synchronize with others CPUs.
|
||||
*/
|
||||
rtems_interrupt_local_disable(level);
|
||||
|
||||
/*
|
||||
* Init the complete IDT vector table with defaultRawIrq value
|
||||
*/
|
||||
for (i = 0; i < IDT_SIZE ; i++) {
|
||||
idtHdl[i] = defaultRawIrq;
|
||||
idtHdl[i].idtIndex = i;
|
||||
}
|
||||
|
||||
raw_initial_config.idtSize = IDT_SIZE;
|
||||
raw_initial_config.defaultRawEntry = defaultRawIrq;
|
||||
raw_initial_config.rawIrqHdlTbl = idtHdl;
|
||||
|
||||
if (!i386_init_idt (&raw_initial_config)) {
|
||||
/*
|
||||
* put something here that will show the failure...
|
||||
*/
|
||||
printk("Unable to initialize IDT!!! System locked\n");
|
||||
while (1);
|
||||
}
|
||||
/*
|
||||
* Patch the entry that will be used by RTEMS for interrupt management
|
||||
* with RTEMS prologue.
|
||||
*/
|
||||
for (i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++) {
|
||||
create_interrupt_gate_descriptor(&idtEntry, rtemsIrq[i]);
|
||||
idt_entry_tbl[i + BSP_ASM_IRQ_VECTOR_BASE] = idtEntry;
|
||||
}
|
||||
/*
|
||||
* At this point we have completed the initialization of IDT
|
||||
* with raw handlers. We must now initialize the higher level
|
||||
* interrupt management.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Init initial Interrupt management config
|
||||
*/
|
||||
bsp_interrupt_initialize();
|
||||
|
||||
/*
|
||||
* #define DEBUG
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
{
|
||||
/*
|
||||
* following adresses should be the same
|
||||
*/
|
||||
unsigned tmp;
|
||||
|
||||
printk("idt_entry_tbl = %x Interrupt_descriptor_table addr = %x\n",
|
||||
idt_entry_tbl, &Interrupt_descriptor_table);
|
||||
tmp = (unsigned) get_hdl_from_vector (BSP_ASM_IRQ_VECTOR_BASE + BSP_PERIODIC_TIMER);
|
||||
printk("clock isr address from idt = %x should be %x\n",
|
||||
tmp, (unsigned) rtems_irq_prologue_0);
|
||||
}
|
||||
printk("i8259s_cache = %x\n", * (unsigned short*) &i8259s_cache);
|
||||
BSP_wait_polled_input();
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user