160 lines
4.7 KiB
C
160 lines
4.7 KiB
C
/* Copyright (C) 2004 David Decotigny
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
USA.
|
|
*/
|
|
#include "segment.h"
|
|
|
|
#include "idt.h"
|
|
|
|
/**
|
|
* An entry in the IDT, or "IDTE" in the following, ie a reference to
|
|
* a interrupt/trap routine or a task gate to handle the sw/hw
|
|
* interrupts and exceptions.
|
|
*
|
|
* @see figure 5-2, intel x86 doc, vol 3
|
|
*/
|
|
struct x86_idt_entry
|
|
{
|
|
/* Low dword */
|
|
sos_ui16_t offset_low; /* 15..0, offset of the routine in the segment */
|
|
sos_ui16_t seg_sel; /* 31..16, the ID of the segment */
|
|
|
|
/* High dword */
|
|
sos_ui8_t reserved:5; /* 4..0 */
|
|
sos_ui8_t flags:3; /* 7..5 */
|
|
sos_ui8_t type:3; /* 10..8 (interrupt gate, trap gate...) */
|
|
sos_ui8_t op_size:1; /* 11 (0=16bits instructions, 1=32bits instr.) */
|
|
sos_ui8_t zero:1; /* 12 */
|
|
sos_ui8_t dpl:2; /* 14..13 */
|
|
sos_ui8_t present:1; /* 15 */
|
|
sos_ui16_t offset_high; /* 31..16 */
|
|
} __attribute__((packed));
|
|
|
|
|
|
/**
|
|
* The IDT register, which stores the address and size of the
|
|
* IDT.
|
|
*
|
|
* @see Intel x86 doc vol 3, section 2.4, figure 2-4
|
|
*/
|
|
struct x86_idt_register
|
|
{
|
|
/* The maximum GDT offset allowed to access an entry in the GDT */
|
|
sos_ui16_t limit;
|
|
|
|
/* This is not exactly a "virtual" address, ie an adddress such as
|
|
those of instructions and data; this is a "linear" address, ie an
|
|
address in the paged memory. However, in SOS we configure the
|
|
segmented memory as a "flat" space: the 0-4GB segment-based (ie
|
|
"virtual") addresses directly map to the 0-4GB paged memory (ie
|
|
"linear"), so that the "linear" addresses are numerically equal
|
|
to the "virtual" addresses: this base_addr will thus be the same
|
|
as the address of the gdt array */
|
|
sos_ui32_t base_addr;
|
|
} __attribute__((packed, aligned (8)));
|
|
|
|
|
|
static struct x86_idt_entry idt[SOS_IDTE_NUM];
|
|
|
|
sos_ret_t sos_idt_subsystem_setup()
|
|
{
|
|
struct x86_idt_register idtr;
|
|
int i;
|
|
|
|
for (i = 0 ;
|
|
i < SOS_IDTE_NUM ;
|
|
i++)
|
|
{
|
|
struct x86_idt_entry *idte = idt + i;
|
|
|
|
/* Setup an empty IDTE interrupt gate, see figure 5-2 in Intel
|
|
x86 doc, vol 3 */
|
|
idte->seg_sel = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE);
|
|
idte->reserved = 0;
|
|
idte->flags = 0;
|
|
idte->type = 0x6; /* Interrupt gate (110b) */
|
|
idte->op_size = 1; /* 32bits instructions */
|
|
idte->zero = 0;
|
|
|
|
/* Disable this IDT entry for the moment */
|
|
sos_idt_set_handler(i, (sos_vaddr_t)NULL, 0/* Don't care */);
|
|
}
|
|
|
|
/*
|
|
* Setup the IDT register, see Intel x86 doc vol 3, section 5.8.
|
|
*/
|
|
|
|
/* Address of the IDT */
|
|
idtr.base_addr = (sos_ui32_t) idt;
|
|
|
|
/* The limit is the maximum offset in bytes from the base address of
|
|
the IDT */
|
|
idtr.limit = sizeof(idt) - 1;
|
|
|
|
/* Commit the IDT into the CPU */
|
|
asm volatile ("lidt %0\n"::"m"(idtr):"memory");
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_idt_set_handler(int index,
|
|
sos_vaddr_t handler_address,
|
|
int lowest_priviledge /* 0..3 */)
|
|
{
|
|
struct x86_idt_entry *idte;
|
|
|
|
if ((index < 0) || (index >= SOS_IDTE_NUM))
|
|
return -SOS_EINVAL;
|
|
if ((lowest_priviledge < 0) || (lowest_priviledge > 3))
|
|
return -SOS_EINVAL;
|
|
|
|
idte = idt + index;
|
|
if (handler_address != (sos_vaddr_t)NULL)
|
|
{
|
|
idte->offset_low = handler_address & 0xffff;
|
|
idte->offset_high = (handler_address >> 16) & 0xffff;
|
|
idte->dpl = lowest_priviledge;
|
|
idte->present = 1; /* Yes, there is a handler */
|
|
}
|
|
else /* Disable this IDT entry */
|
|
{
|
|
idte->offset_low = 0;
|
|
idte->offset_high = 0;
|
|
idte->dpl = 0;
|
|
idte->present = 0; /* No, there is no handler */
|
|
}
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
sos_ret_t sos_idt_get_handler(int index,
|
|
sos_vaddr_t *handler_address,
|
|
int *lowest_priviledge)
|
|
{
|
|
if ((index < 0) || (index >= SOS_IDTE_NUM))
|
|
return -SOS_EINVAL;
|
|
|
|
if (handler_address != NULL)
|
|
*handler_address = idt[index].offset_low
|
|
| (idt[index].offset_high << 16);
|
|
if (lowest_priviledge != NULL)
|
|
*lowest_priviledge = idt[index].dpl;
|
|
|
|
return SOS_OK;
|
|
}
|