bsps/i386: Move shared files to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-04-24 07:36:46 +02:00
parent c7410f1783
commit 7a8e71bd70
11 changed files with 9 additions and 9 deletions

View File

@@ -77,9 +77,9 @@ endif
# console (non-graphics support)
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/serial_mouse_config.c
librtemsbsp_a_SOURCES += ../shared/comm/uart.c
librtemsbsp_a_SOURCES += ../shared/comm/tty_drv.c
librtemsbsp_a_SOURCES += ../shared/realmode_int/realmode_int.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/uart.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/tty_drv.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/realmode_int.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/legacy-console.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/console_select.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/console_control.c
@@ -91,9 +91,9 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/uart_bus_pci.
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/gdb_select.c
# gdb
librtemsbsp_a_SOURCES += ../shared/comm/i386-stub.c
librtemsbsp_a_SOURCES += ../shared/comm/i386-stub-glue.c
librtemsbsp_a_SOURCES += ../shared/comm/gdb_glue.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/i386-stub.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/i386-stub-glue.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/console/gdb_glue.c
# gnat
@@ -132,8 +132,8 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/ata/idecfg.c
endif
if HAS_SMP
librtemsbsp_a_SOURCES += ../shared/smp/getcpuid.c
librtemsbsp_a_SOURCES += ../shared/smp/smp-imps.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/getcpuid.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/i386/pc386/start/smp-imps.c
project_lib_DATA += appstart.$(OBJEXT)
appcpustart.$(OBJEXT): ../../../../../../bsps/i386/pc386/start/start16.S

View File

@@ -1,84 +0,0 @@
i386/pc386 GDB Stub
The i386 GDB stub has been updated to use the libchip drivers for the
NS16550. Make sure you have detect the device and you have added a console
entry. On the PC the legacy and PCI boards are supported.
This GDB stub glue code is specific to the UART protocol defined in libbchip.
The pc386 BSP has boot command line options to manage GDB support.
a) Find the minor number of the console device:
#include <console_private.h>
rtems_device_minor_number minor = 0;
if (console_find_console_entry("/dev/com1",
strlen("/dev/com1") - 1, &minor) == NULL)
error("driver not found\n");
Note, this call is part of the private console API and may change.
b) To start GDB stub, run this:
#include <bsp.h>
/* Init GDB glue */
i386_stub_glue_init(minor);
/* Init GDB stub itself */
set_debug_traps();
/*
* Init GDB break in capability,
* has to be called after
* set_debug_traps
*/
i386_stub_glue_init_breakin();
/* Put breakpoint in */
breakpoint();
c) To run use GDB:
$ i386-rtems4.12-gdb hello.exe
GNU gdb (GDB) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-freebsd10.1 --target=i386-rtems4.12".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello.exe...done.
(gdb) target remote /dev/cuaU5
Remote debugging using /dev/cuaU5
0x00103fda in breakpoint () at i386-stub.c:1004
1004 BREAKPOINT ();
(gdb) b Init
Breakpoint 1 at 0x1001e0: file init.c, line 29.
(gdb) c
Continuing.
Breakpoint 1, Init (ignored=1269800) at init.c:29
29 {
(gdb)
Pressing ^C works and if running the board should halt when GDB connects.
e) Use ser2net to provide reomve access over a network to a board. Install the
ser2net package and add a configuration for the port GDB connects to. For
example:
0005:raw:0:/dev/cuaU5:115200
Start ser2net running then connect GDB using:
(gdb) target remote myhost:30005

View File

@@ -1,61 +0,0 @@
/* gdb_glue
*
* Interface to initialize the GDB.
*
* COPYRIGHT (c) 1989-1998.
* 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 <bsp.h>
#include <stdio.h>
#include <uart.h>
#define BREAKPOINT() __asm__ (" int $3");
extern int BSPConsolePort;
void i386_stub_glue_init(int);
void i386_stub_glue_init_breakin(void);
void set_debug_traps(void);
/* Init GDB glue */
void init_remote_gdb( void )
{
if(BSPConsolePort != BSP_UART_COM2)
{
/*
* If com2 is not used as console use it for
* debugging
*/
i386_stub_glue_init(BSP_UART_COM2);
printf( "Remote GDB using COM2...\n" );
}
else
{
/* Otherwise use com1 */
i386_stub_glue_init(BSP_UART_COM1);
printf( "Remote GDB using COM1...\n" );
}
printf( "Remote GDB: setting traps...\n" );
/* Init GDB stub itself */
set_debug_traps();
printf( "Remote GDB: waiting remote connection....\n" );
/*
* Init GDB break in capability,
* has to be called after
* set_debug_traps
*/
i386_stub_glue_init_breakin();
/* Put breakpoint in */
/* breakpoint(); */
/* BREAKPOINT(); */
}

View File

@@ -1,273 +0,0 @@
/*
* Copyright (c) 2016.
* Chris Johns <chrisj@rtems.org>
*
* This software is Copyright (C) 1998 by T.sqware - all rights limited
* It is provided in to the public domain "as is", can be freely modified
* as far as this copyight notice is kept unchanged, but does not imply
* an endorsement by T.sqware of the product in which it is included.
*/
#include <bsp.h>
#include <bsp/irq-generic.h>
#include <libchip/serial.h>
#include "../../../../../../../bsps/shared/dev/serial/legacy-console.h"
int putDebugChar(int ch); /* write a single character */
int getDebugChar(void); /* read and return a single char */
/* Check is any characters received are a ^C */
int i386_gdb_uart_ctrl_c_check(void);
/* Raw interrupt handler. */
void i386_gdb_uart_isr(void);
/* assign an exception handler */
void exceptionHandler(int, void (*handler)(void));
/* User supplied remote debug option. */
extern int remote_debug;
/* Current uart and port used by the gdb stub */
static int uart_current;
static int uart_vector;
static console_tbl* port_current;
/*
* Interrupt service routine for all, it does it check whether ^C is received
* if yes it will flip TF bit before returning.
*
* Note: it should be installed as raw interrupt handler.
*
* Warning: I do not like the use of the global data, I am not
* sure if this is SMP safe.
*/
int i386_gdb_uart_isr_regsav[4] RTEMS_UNUSED;
__asm__ (".p2align 4");
__asm__ (".text");
__asm__ (".globl i386_gdb_uart_isr");
__asm__ ("i386_gdb_uart_isr:");
__asm__ (" pusha"); /* Push all */
__asm__ (" call i386_gdb_uart_ctrl_c_check"); /* Look for ^C */
__asm__ (" movl %eax, i386_gdb_uart_isr_regsav"); /* Save eax */
__asm__ (" popa"); /* Pop all */
__asm__ (" xchgl %eax, i386_gdb_uart_isr_regsav"); /* Exchange eax */
__asm__ (" cmpl $0, %eax"); /* 1 == ^C */
__asm__ (" je i386_gdb_uart_isr_1"); /* branch if 0 */
__asm__ (" movl %ebx, i386_gdb_uart_isr_regsav + 4"); /* Save ebx */
__asm__ (" movl %edx, i386_gdb_uart_isr_regsav + 8"); /* Save edx */
__asm__ (" popl %ebx"); /* Pop eip */
__asm__ (" popl %edx"); /* Pop cs */
__asm__ (" popl %eax"); /* Pop flags */
__asm__ (" orl $0x100, %eax"); /* Modify it */
__asm__ (" pushl %eax"); /* Push it back */
__asm__ (" pushl %edx"); /* Push cs */
__asm__ (" pushl %ebx"); /* Push eip */
__asm__ (" movl i386_gdb_uart_isr_regsav + 4, %ebx"); /* Restore ebx */
__asm__ (" movl i386_gdb_uart_isr_regsav + 8, %edx"); /* Restore edx */
__asm__ ("i386_gdb_uart_isr_1:");
__asm__ (" movl i386_gdb_uart_isr_regsav, %eax"); /* Restore eax */
__asm__ (" iret"); /* Done */
static int gdb_hello_index;
static const char *const gdb_hello = "+";
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;
}
static inline void BSP_irq_ack_at_i8259a(const int 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);
}
int i386_gdb_uart_ctrl_c_check(void)
{
BSP_irq_ack_at_i8259a(uart_vector);
if (port_current) {
int c = 0;
while (c >= 0) {
c = port_current->pDeviceFns->deviceRead(uart_current);
if (c == 3) {
gdb_hello_index = 0;
return 1;
} else if (gdb_hello[gdb_hello_index] == (char) c) {
++gdb_hello_index;
if (gdb_hello[gdb_hello_index] == '\0') {
gdb_hello_index = 0;
return 1;
}
} else {
gdb_hello_index = 0;
}
}
}
return 0;
}
static void
nop(const rtems_raw_irq_connect_data* notused)
{
}
static int
isOn(const rtems_raw_irq_connect_data* notused)
{
return 1;
}
int i386_stub_glue_uart(void)
{
if (port_current == NULL)
return -1;
return uart_current;
}
/*
* Initialize glue code linking i386-stub with the rest of
* the system
*/
void
i386_stub_glue_init(int uart)
{
rtems_device_minor_number minor = (rtems_device_minor_number) uart;
port_current = console_find_console_entry(NULL, 0, &minor);
if (port_current == NULL) {
printk("GDB: invalid minor number for UART\n");
return;
}
uart_current = uart;
/* Intialise the UART, assuming polled drivers */
port_current->pDeviceFns->deviceInitialize(uart);
}
static void BSP_uart_on(const rtems_raw_irq_connect_data* used)
{
bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
}
static void BSP_uart_off(const rtems_raw_irq_connect_data* used)
{
bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
}
/*
* In order to have a possibility to break into
* running program, one has to call this function
*/
void i386_stub_glue_init_breakin(void)
{
rtems_raw_irq_connect_data uart_raw_irq_data;
if (port_current == NULL) {
printk("GDB: no port initialised\n");
return;
}
if ((port_current->ulIntVector == 0) || (port_current->ulIntVector > 16)) {
printk("GDB: no UART interrupt support\n");
}
else {
uart_vector = port_current->ulIntVector;
uart_raw_irq_data.idtIndex = port_current->ulIntVector + BSP_IRQ_VECTOR_BASE;
if (!i386_get_current_idt_entry(&uart_raw_irq_data)) {
printk("GBD: cannot get idt entry\n");
rtems_fatal_error_occurred(1);
}
if (!i386_delete_idt_entry(&uart_raw_irq_data)) {
printk("GDB: cannot delete idt entry\n");
rtems_fatal_error_occurred(1);
}
uart_raw_irq_data.on = BSP_uart_on;
uart_raw_irq_data.off = BSP_uart_off;
/* Install ISR */
uart_raw_irq_data.idtIndex = port_current->ulIntVector + BSP_IRQ_VECTOR_BASE;
uart_raw_irq_data.hdl = i386_gdb_uart_isr;
if (!i386_set_idt_entry (&uart_raw_irq_data)) {
printk("GDB: raw exception handler connection failed\n");
rtems_fatal_error_occurred(1);
}
/* Enable interrupts, this is a bit of a hack because we
* have to know the device but there is no other call. */
(*port_current->setRegister)(port_current->ulCtrlPort1, 1, 0x01);
}
}
int
putDebugChar(int ch)
{
if (port_current != NULL) {
port_current->pDeviceFns->deviceWritePolled(uart_current, ch);
}
return 1;
}
int getDebugChar(void)
{
int c = -1;
if (port_current != NULL) {
while (c < 0)
c = port_current->pDeviceFns->deviceRead(uart_current);
}
return c;
}
void exceptionHandler(int vector, void (*handler)(void))
{
rtems_raw_irq_connect_data excep_raw_irq_data;
excep_raw_irq_data.idtIndex = vector;
if(!i386_get_current_idt_entry(&excep_raw_irq_data))
{
printk("GDB: cannot get idt entry\n");
rtems_fatal_error_occurred(1);
}
if(!i386_delete_idt_entry(&excep_raw_irq_data))
{
printk("GDB: cannot delete idt entry\n");
rtems_fatal_error_occurred(1);
}
excep_raw_irq_data.on = nop;
excep_raw_irq_data.off = nop;
excep_raw_irq_data.isOn = isOn;
excep_raw_irq_data.hdl = handler;
if (!i386_set_idt_entry (&excep_raw_irq_data)) {
printk("GDB: raw exception handler connection failed\n");
rtems_fatal_error_occurred(1);
}
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,413 +0,0 @@
/***************************************************************************
*
* MODULE DESCRIPTION:
* This module implements the RTEMS drivers for the PC serial ports
* as /dev/ttyS1 for COM1 and /dev/ttyS2 as COM2. If one of the ports
* is used as the console, this driver would fail to initialize.
*
* This code was based on the console driver. It is based on the
* current termios framework. This is just a shell around the
* termios support.
*
* by: Rosimildo da Silva:
* rdasilva@connecttel.com
* http://www.connecttel.com
*
****************************************************************************/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <rtems/bspIo.h>
#include <rtems/libio.h>
#include <rtems/termiostypes.h>
#include <termios.h>
#include <uart.h>
#include <libcpu/cpuModel.h>
#include <bsp/tty_drv.h>
int BSP_poll_read(int);
/* Internal routines */
static int tty1_conSetAttr( int minor, const struct termios *t);
static int tty2_conSetAttr( int minor, const struct termios *t);
extern BSP_polling_getchar_function_type BSP_poll_char;
extern int BSPConsolePort;
extern void rtems_set_waiting_id_comx( int port, rtems_id id, rtems_event_set event );
/*
* TTYS1 - device driver INITIALIZE entry point.
*/
rtems_device_driver
tty1_initialize(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
rtems_status_code status;
/* Check if this port is not been used as console */
if( BSPConsolePort == BSP_UART_COM1 )
{
status = -1;
printk("TTYS1: port selected as console.\n");
rtems_fatal_error_occurred( status );
}
/*
* Set up TERMIOS
*/
rtems_termios_initialize();
/*
* Do device-specific initialization
*/
/* 9600-8-N-1, without hardware flow control */
BSP_uart_init( BSP_UART_COM1, 9600, CHR_8_BITS, 0, 0, 0 );
status = rtems_interrupt_handler_install(
BSP_UART_COM1_IRQ,
"tty_drv",
RTEMS_INTERRUPT_UNIQUE,
BSP_uart_termios_isr_com1,
NULL
);
assert(status == RTEMS_SUCCESSFUL);
/*
* Register the device
*/
status = rtems_io_register_name ("/dev/ttyS1", major, 0);
if (status != RTEMS_SUCCESSFUL)
{
printk("Error registering ttyS1 device!\n");
rtems_fatal_error_occurred (status);
}
printk("Device: /dev/ttyS1 initialized.\n");
return RTEMS_SUCCESSFUL;
} /* tty_initialize */
static int tty1_last_close(int major, int minor, void *arg)
{
rtems_status_code status;
status = rtems_interrupt_handler_remove(
BSP_UART_COM1_IRQ,
BSP_uart_termios_isr_com1,
NULL
);
assert(status == RTEMS_SUCCESSFUL);
return 0;
}
/*
* TTY1 - device driver OPEN entry point
*/
rtems_device_driver
tty1_open(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
rtems_status_code status;
#ifndef USE_TASK_DRIVEN
static rtems_termios_callbacks cb =
{
NULL, /* firstOpen */
tty1_last_close, /* lastClose */
NULL, /* poll read */
BSP_uart_termios_write_com1, /* write */
tty1_conSetAttr, /* setAttributes */
NULL, /* stopRemoteTx */
NULL, /* startRemoteTx */
TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
};
#else
static rtems_termios_callbacks cb =
{
NULL, /* firstOpen */
NULL, /* lastClose */
BSP_uart_termios_read_com1, /* poll read */
BSP_uart_termios_write_com1, /* write */
tty1_conSetAttr, /* setAttributes */
NULL, /* stopRemoteTx */
NULL, /* startRemoteTx */
TERMIOS_TASK_DRIVEN /* outputUsesInterrupts */
};
#endif
status = rtems_termios_open( major, minor, arg, &cb );
if(status != RTEMS_SUCCESSFUL)
{
printk("Error openning tty1 device\n");
return status;
}
/*
* Pass data area info down to driver
*/
BSP_uart_termios_set( BSP_UART_COM1,
((rtems_libio_open_close_args_t *)arg)->iop->data1 );
/* Enable interrupts on channel */
BSP_uart_intr_ctrl( BSP_UART_COM1, BSP_UART_INTR_CTRL_TERMIOS);
return RTEMS_SUCCESSFUL;
}
/*
* TTY - device driver CLOSE entry point
*/
rtems_device_driver
tty_close(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
return (rtems_termios_close (arg));
} /* tty_close */
/*
* TTY device driver READ entry point.
* Read characters from the tty device.
*/
rtems_device_driver
tty_read(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
return rtems_termios_read (arg);
} /* tty_read */
/*
* TTY device driver WRITE entry point.
* Write characters to the tty device.
*/
rtems_device_driver
tty_write(rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg)
{
return rtems_termios_write (arg);
} /* tty_write */
/*
* Handle ioctl request. This is a generic internal
* routine to handle both devices.
*/
static rtems_device_driver tty_control( int port, void *arg )
{
rtems_libio_ioctl_args_t *args = arg;
switch( args->command )
{
default:
return rtems_termios_ioctl (arg);
break;
}
args->ioctl_return = 0;
return RTEMS_SUCCESSFUL;
}
/*
* Handle ioctl request for ttyS1.
*/
rtems_device_driver
tty1_control(rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return tty_control( BSP_UART_COM1, arg );
}
static int
conSetAttr(int port, int minor, const struct termios *t)
{
unsigned long baud, databits, parity, stopbits;
baud = rtems_termios_baud_to_number(t->c_ospeed);
if ( baud > 115200 )
rtems_fatal_error_occurred (RTEMS_INTERNAL_ERROR);
if (t->c_cflag & PARENB) {
/* Parity is enabled */
if (t->c_cflag & PARODD) {
/* Parity is odd */
parity = PEN;
}
else {
/* Parity is even */
parity = PEN | EPS;
}
}
else {
/* No parity */
parity = 0;
}
switch (t->c_cflag & CSIZE) {
case CS5: databits = CHR_5_BITS; break;
case CS6: databits = CHR_6_BITS; break;
case CS7: databits = CHR_7_BITS; break;
default: /* just to avoid warnings -- all cases are covered */
case CS8: databits = CHR_8_BITS; break;
}
if (t->c_cflag & CSTOPB) {
/* 2 stop bits */
stopbits = STB;
}
else {
/* 1 stop bit */
stopbits = 0;
}
printk("Setting attributes, port=%X, baud=%" PRId32 ", linemode = 0x%02" PRIx32 "\n",
port, baud, databits | parity | stopbits );
BSP_uart_set_attributes(port, baud, databits, parity, stopbits);
return 0;
}
/*
* Handle ioctl request for ttyS2.
*/
static int
tty1_conSetAttr( int minor, const struct termios *t)
{
return conSetAttr( BSP_UART_COM1, minor, t );
}
/*
* TTY2 device driver INITIALIZE entry point.
*/
rtems_device_driver
tty2_initialize(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
rtems_status_code status;
/* Check if this port is not been used as console */
if( BSPConsolePort == BSP_UART_COM2 )
{
status = -1;
printk("TTY2: port selected as console.\n");
rtems_fatal_error_occurred( status );
}
/*
* Set up TERMIOS
*/
rtems_termios_initialize();
/*
* Do device-specific initialization
*/
/* 9600-8-N-1, without hardware flow control */
BSP_uart_init( BSP_UART_COM2, 9600, CHR_8_BITS, 0, 0, 0);
status = rtems_interrupt_handler_install(
BSP_UART_COM2_IRQ,
"tty_drv",
RTEMS_INTERRUPT_UNIQUE,
BSP_uart_termios_isr_com2,
NULL
);
assert(status == RTEMS_SUCCESSFUL);
/*
* Register the device
*/
status = rtems_io_register_name ("/dev/ttyS2", major, 0);
if (status != RTEMS_SUCCESSFUL)
{
printk("Error registering tty2 device!\n");
rtems_fatal_error_occurred (status);
}
printk("Device: /dev/ttyS2 initialized.\n");
return RTEMS_SUCCESSFUL;
} /* tty_initialize */
static int tty2_last_close(int major, int minor, void *arg)
{
rtems_status_code status;
status = rtems_interrupt_handler_remove(
BSP_UART_COM2_IRQ,
BSP_uart_termios_isr_com2,
NULL
);
assert(status == RTEMS_SUCCESSFUL);
return 0;
}
/*
* TTY2 device driver OPEN entry point
*/
rtems_device_driver
tty2_open(rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg)
{
rtems_status_code status;
#ifndef USE_TASK_DRIVEN
static rtems_termios_callbacks cb =
{
NULL, /* firstOpen */
tty2_last_close, /* lastClose */
NULL, /* poll read */
BSP_uart_termios_write_com2, /* write */
tty2_conSetAttr, /* setAttributes */
NULL, /* stopRemoteTx */
NULL, /* startRemoteTx */
TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
};
#else
static rtems_termios_callbacks cb =
{
NULL, /* firstOpen */
NULL, /* lastClose */
BSP_uart_termios_read_com2, /* poll read */
BSP_uart_termios_write_com2, /* write */
tty2_conSetAttr, /* setAttributes */
NULL, /* stopRemoteTx */
NULL, /* startRemoteTx */
TERMIOS_TASK_DRIVEN /* outputUsesInterrupts */
};
#endif
status = rtems_termios_open (major, minor, arg, &cb);
if(status != RTEMS_SUCCESSFUL)
{
printk("Error openning tty1 device\n");
return status;
}
/*
* Pass data area info down to driver
*/
BSP_uart_termios_set( BSP_UART_COM2,
((rtems_libio_open_close_args_t *)arg)->iop->data1 );
/* Enable interrupts on channel */
BSP_uart_intr_ctrl( BSP_UART_COM2, BSP_UART_INTR_CTRL_TERMIOS);
return RTEMS_SUCCESSFUL;
}
/*
* Handle ioctl request for TTY2
*/
rtems_device_driver
tty2_control(rtems_device_major_number major,
rtems_device_minor_number minor,
void * arg
)
{
return tty_control( BSP_UART_COM2, arg );
}
static int
tty2_conSetAttr( int minor, const struct termios *t)
{
return conSetAttr( BSP_UART_COM2, minor, t );
}

View File

@@ -1,959 +0,0 @@
/*
* This software is Copyright (C) 1998 by T.sqware - all rights limited
* It is provided in to the public domain "as is", can be freely modified
* as far as this copyight notice is kept unchanged, but does not imply
* an endorsement by T.sqware of the product in which it is included.
*/
#include <stdio.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <uart.h>
#include <rtems/libio.h>
#include <rtems/termiostypes.h>
#include <termios.h>
#include <assert.h>
/*
* Basic 16552 driver
*/
struct uart_data
{
int ioMode;
int hwFlow;
unsigned int ier;
unsigned long baud;
unsigned long databits;
unsigned long parity;
unsigned long stopbits;
};
static struct uart_data uart_data[2];
/*
* Macros to read/write register of uart, if configuration is
* different just rewrite these macros
*/
static inline unsigned char
uread(int uart, unsigned int reg)
{
register unsigned char val;
if (uart == 0) {
inport_byte(COM1_BASE_IO+reg, val);
} else {
inport_byte(COM2_BASE_IO+reg, val);
}
return val;
}
static inline void
uwrite(int uart, int reg, unsigned int val)
{
if (uart == 0) {
outport_byte(COM1_BASE_IO+reg, val);
} else {
outport_byte(COM2_BASE_IO+reg, val);
}
}
static void
uartError(int uart)
{
unsigned char uartStatus, dummy;
uartStatus = uread(uart, LSR);
(void) uartStatus; /* avoid set but not used warning */
dummy = uread(uart, RBR);
(void) dummy; /* avoid set but not used warning */
#ifdef UARTDEBUG
if (uartStatus & OE)
printk("********* Over run Error **********\n");
if (uartStatus & PE)
printk("********* Parity Error **********\n");
if (uartStatus & FE)
printk("********* Framing Error **********\n");
if (uartStatus & BI)
printk("********* Parity Error **********\n");
if (uartStatus & ERFIFO)
printk("********* Error receive Fifo **********\n");
#endif
}
/*
* Uart initialization, it is hardcoded to 8 bit, no parity,
* one stop bit, FIFO, things to be changed
* are baud rate and nad hw flow control,
* and longest rx fifo setting
*/
void
BSP_uart_init
(
int uart,
unsigned long baud,
unsigned long databits,
unsigned long parity,
unsigned long stopbits,
int hwFlow
)
{
/* Sanity check */
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
switch(baud)
{
case 50:
case 75:
case 110:
case 134:
case 300:
case 600:
case 1200:
case 2400:
case 9600:
case 19200:
case 38400:
case 57600:
case 115200:
break;
default:
assert(0);
return;
}
/* Set DLAB bit to 1 */
uwrite(uart, LCR, DLAB);
/* Set baud rate */
uwrite(uart, DLL, (BSPBaseBaud/baud) & 0xff);
uwrite(uart, DLM, ((BSPBaseBaud/baud) >> 8) & 0xff);
/* 8-bit, no parity , 1 stop */
uwrite(uart, LCR, databits | parity | stopbits);
/* Set DTR, RTS and OUT2 high */
uwrite(uart, MCR, DTR | RTS | OUT_2);
/* Enable FIFO */
uwrite(uart, FCR, FIFO_EN | XMIT_RESET | RCV_RESET | RECEIVE_FIFO_TRIGGER12);
/* Disable Interrupts */
uwrite(uart, IER, 0);
/* Read status to clear them */
uread(uart, LSR);
uread(uart, RBR);
uread(uart, MSR);
/* Remember state */
uart_data[uart].baud = baud;
uart_data[uart].databits = databits;
uart_data[uart].parity = parity;
uart_data[uart].stopbits = stopbits;
uart_data[uart].hwFlow = hwFlow;
return;
}
/*
* Set baud
*/
void
BSP_uart_set_baud(
int uart,
unsigned long baud
)
{
/* Sanity check */
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
BSP_uart_set_attributes( uart, baud, uart_data[uart].databits,
uart_data[uart].parity, uart_data[uart].stopbits );
}
/*
* Set all attributes
*/
void
BSP_uart_set_attributes
(
int uart,
unsigned long baud,
unsigned long databits,
unsigned long parity,
unsigned long stopbits
)
{
unsigned char mcr, ier;
/* Sanity check */
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
/*
* This function may be called whenever TERMIOS parameters
* are changed, so we have to make sure that baud change is
* indeed required
*/
if( (baud == uart_data[uart].baud) &&
(databits == uart_data[uart].databits) &&
(parity == uart_data[uart].parity) &&
(stopbits == uart_data[uart].stopbits) )
{
return;
}
mcr = uread(uart, MCR);
ier = uread(uart, IER);
BSP_uart_init(uart, baud, databits, parity, stopbits, uart_data[uart].hwFlow);
uwrite(uart, MCR, mcr);
uwrite(uart, IER, ier);
return;
}
/*
* Enable/disable interrupts
*/
void
BSP_uart_intr_ctrl(int uart, int cmd)
{
int iStatus = (int)INTERRUPT_DISABLE;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
switch(cmd)
{
case BSP_UART_INTR_CTRL_ENABLE:
iStatus |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE | TRANSMIT_ENABLE);
if ( uart_data[uart].hwFlow ) {
iStatus |= MODEM_ENABLE;
}
break;
case BSP_UART_INTR_CTRL_TERMIOS:
iStatus |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
if ( uart_data[uart].hwFlow ) {
iStatus |= MODEM_ENABLE;
}
break;
case BSP_UART_INTR_CTRL_GDB:
iStatus |= RECEIVE_ENABLE;
break;
}
uart_data[uart].ier = iStatus;
uwrite(uart, IER, iStatus);
return;
}
void
BSP_uart_throttle(int uart)
{
unsigned int mcr;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
if(!uart_data[uart].hwFlow)
{
/* Should not happen */
assert(0);
return;
}
mcr = uread (uart, MCR);
/* RTS down */
mcr &= ~RTS;
uwrite(uart, MCR, mcr);
return;
}
void
BSP_uart_unthrottle(int uart)
{
unsigned int mcr;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
if(!uart_data[uart].hwFlow)
{
/* Should not happen */
assert(0);
return;
}
mcr = uread (uart, MCR);
/* RTS up */
mcr |= RTS;
uwrite(uart, MCR, mcr);
return;
}
/*
* Status function, -1 if error
* detected, 0 if no received chars available,
* 1 if received char available, 2 if break
* is detected, it will eat break and error
* chars. It ignores overruns - we cannot do
* anything about - it execpt count statistics
* and we are not counting it.
*/
int
BSP_uart_polled_status(int uart)
{
unsigned char val;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
val = uread(uart, LSR);
if(val & BI)
{
/* BREAK found, eat character */
uread(uart, RBR);
return BSP_UART_STATUS_BREAK;
}
if((val & (DR | OE | FE)) == 1)
{
/* No error, character present */
return BSP_UART_STATUS_CHAR;
}
if((val & (DR | OE | FE)) == 0)
{
/* Nothing */
return BSP_UART_STATUS_NOCHAR;
}
/*
* Framing or parity error
* eat character
*/
uread(uart, RBR);
return BSP_UART_STATUS_ERROR;
}
/*
* Polled mode write function
*/
void
BSP_uart_polled_write(int uart, int val)
{
unsigned char val1;
/* Sanity check */
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
for(;;)
{
if((val1=uread(uart, LSR)) & THRE)
{
break;
}
}
if(uart_data[uart].hwFlow)
{
for(;;)
{
if(uread(uart, MSR) & CTS)
{
break;
}
}
}
uwrite(uart, THR, val & 0xff);
/*
* Wait for character to be transmitted.
* This ensures that printk and printf play nicely together
* when using the same serial port.
* Yes, there's a performance hit here, but if we're doing
* polled writes to a serial port we're probably not that
* interested in efficiency anyway.....
*/
for(;;)
{
if((val1=uread(uart, LSR)) & THRE)
{
break;
}
}
return;
}
void
BSP_output_char_via_serial(char val)
{
BSP_uart_polled_write(BSPConsolePort, val);
}
/*
* Polled mode read function
*/
int
BSP_uart_polled_read(int uart)
{
unsigned char val;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
for(;;)
{
if(uread(uart, LSR) & DR)
{
break;
}
}
val = uread(uart, RBR);
return (int)(val & 0xff);
}
int
BSP_poll_char_via_serial(void)
{
return BSP_uart_polled_read(BSPConsolePort);
}
/* ================ Termios support =================*/
static volatile int termios_stopped_com1 = 0;
static volatile int termios_tx_active_com1 = 0;
static void* termios_ttyp_com1 = NULL;
static char termios_tx_hold_com1 = 0;
static volatile char termios_tx_hold_valid_com1 = 0;
static volatile int termios_stopped_com2 = 0;
static volatile int termios_tx_active_com2 = 0;
static void* termios_ttyp_com2 = NULL;
static char termios_tx_hold_com2 = 0;
static volatile char termios_tx_hold_valid_com2 = 0;
static void ( *driver_input_handler_com1 )( void *, char *, int ) = 0;
static void ( *driver_input_handler_com2 )( void *, char *, int ) = 0;
/*
* Set channel parameters
*/
void
BSP_uart_termios_set(int uart, void *ttyp)
{
struct rtems_termios_tty *p = (struct rtems_termios_tty *)ttyp;
unsigned char val;
assert(uart == BSP_UART_COM1 || uart == BSP_UART_COM2);
if(uart == BSP_UART_COM1)
{
uart_data[uart].ioMode = p->device.outputUsesInterrupts;
if(uart_data[uart].hwFlow)
{
val = uread(uart, MSR);
termios_stopped_com1 = (val & CTS) ? 0 : 1;
}
else
{
termios_stopped_com1 = 0;
}
termios_tx_active_com1 = 0;
termios_ttyp_com1 = ttyp;
termios_tx_hold_com1 = 0;
termios_tx_hold_valid_com1 = 0;
}
else
{
uart_data[uart].ioMode = p->device.outputUsesInterrupts;
if(uart_data[uart].hwFlow)
{
val = uread(uart, MSR);
termios_stopped_com2 = (val & CTS) ? 0 : 1;
}
else
{
termios_stopped_com2 = 0;
}
termios_tx_active_com2 = 0;
termios_ttyp_com2 = ttyp;
termios_tx_hold_com2 = 0;
termios_tx_hold_valid_com2 = 0;
}
return;
}
int
BSP_uart_termios_read_com1(int uart)
{
int off = (int)0;
char buf[40];
/* read bytes */
while (( off < sizeof(buf) ) && ( uread(BSP_UART_COM1, LSR) & DR )) {
buf[off++] = uread(BSP_UART_COM1, RBR);
}
/* write out data */
if ( off > 0 ) {
rtems_termios_enqueue_raw_characters(termios_ttyp_com1, buf, off);
}
/* enable receive interrupts */
uart_data[BSP_UART_COM1].ier |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
return ( EOF );
}
int
BSP_uart_termios_read_com2(int uart)
{
int off = (int)0;
char buf[40];
/* read current byte */
while (( off < sizeof(buf) ) && ( uread(BSP_UART_COM2, LSR) & DR )) {
buf[off++] = uread(BSP_UART_COM2, RBR);
}
/* write out data */
if ( off > 0 ) {
rtems_termios_enqueue_raw_characters(termios_ttyp_com2, buf, off);
}
/* enable receive interrupts */
uart_data[BSP_UART_COM2].ier |= (RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
return ( EOF );
}
ssize_t
BSP_uart_termios_write_com1(int minor, const char *buf, size_t len)
{
if(len <= 0)
{
return 0;
}
assert(buf != NULL);
/* If there TX buffer is busy - something is royally screwed up */
assert((uread(BSP_UART_COM1, LSR) & THRE) != 0);
if(termios_stopped_com1)
{
/* CTS low */
termios_tx_hold_com1 = *buf;
termios_tx_hold_valid_com1 = 1;
return 0;
}
/* Write character */
uwrite(BSP_UART_COM1, THR, *buf & 0xff);
/* Enable interrupts if necessary */
if ( !termios_tx_active_com1 ) {
termios_tx_active_com1 = 1;
uart_data[BSP_UART_COM1].ier |= TRANSMIT_ENABLE;
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
}
return 1;
}
ssize_t
BSP_uart_termios_write_com2(int minor, const char *buf, size_t len)
{
if(len <= 0)
{
return 0;
}
assert(buf != NULL);
/* If there TX buffer is busy - something is royally screwed up */
assert((uread(BSP_UART_COM2, LSR) & THRE) != 0);
if(termios_stopped_com2)
{
/* CTS low */
termios_tx_hold_com2 = *buf;
termios_tx_hold_valid_com2 = 1;
return 0;
}
/* Write character */
uwrite(BSP_UART_COM2, THR, *buf & 0xff);
/* Enable interrupts if necessary */
if ( !termios_tx_active_com2 ) {
termios_tx_active_com2 = 1;
uart_data[BSP_UART_COM2].ier |= TRANSMIT_ENABLE;
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
}
return 1;
}
void
BSP_uart_termios_isr_com1(void *ignored)
{
unsigned char buf[40];
unsigned char val;
int off, ret, vect;
off = 0;
for(;;)
{
vect = uread(BSP_UART_COM1, IIR) & 0xf;
switch(vect)
{
case MODEM_STATUS :
val = uread(BSP_UART_COM1, MSR);
if(uart_data[BSP_UART_COM1].hwFlow)
{
if(val & CTS)
{
/* CTS high */
termios_stopped_com1 = 0;
if(termios_tx_hold_valid_com1)
{
termios_tx_hold_valid_com1 = 0;
BSP_uart_termios_write_com1(0, &termios_tx_hold_com1,
1);
}
}
else
{
/* CTS low */
termios_stopped_com1 = 1;
}
}
break;
case NO_MORE_INTR :
/* No more interrupts */
if(off != 0)
{
/* Update rx buffer */
if( driver_input_handler_com1 )
{
driver_input_handler_com1( termios_ttyp_com1, (char *)buf, off );
}
else
{
/* Update rx buffer */
rtems_termios_enqueue_raw_characters(termios_ttyp_com1, (char *)buf, off );
}
}
return;
case TRANSMITTER_HODING_REGISTER_EMPTY :
/*
* TX holding empty: we have to disable these interrupts
* if there is nothing more to send.
*/
/* If nothing else to send disable interrupts */
ret = rtems_termios_dequeue_characters(termios_ttyp_com1, 1);
if ( ret == 0 ) {
termios_tx_active_com1 = 0;
uart_data[BSP_UART_COM1].ier &= ~(TRANSMIT_ENABLE);
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
}
break;
case RECEIVER_DATA_AVAIL :
case CHARACTER_TIMEOUT_INDICATION:
if ( uart_data[BSP_UART_COM1].ioMode == TERMIOS_TASK_DRIVEN ) {
/* ensure interrupts are enabled */
if ( uart_data[BSP_UART_COM1].ier & RECEIVE_ENABLE ) {
/* disable interrupts and notify termios */
uart_data[BSP_UART_COM1].ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
uwrite(BSP_UART_COM1, IER, uart_data[BSP_UART_COM1].ier);
rtems_termios_rxirq_occured(termios_ttyp_com1);
}
}
else {
/* RX data ready */
assert(off < sizeof(buf));
buf[off++] = uread(BSP_UART_COM1, RBR);
}
break;
case RECEIVER_ERROR:
/* RX error: eat character */
uartError(BSP_UART_COM1);
break;
default:
/* Should not happen */
assert(0);
return;
}
}
}
void
BSP_uart_termios_isr_com2(void *ignored)
{
unsigned char buf[40];
unsigned char val;
int off, ret, vect;
off = 0;
for(;;)
{
vect = uread(BSP_UART_COM2, IIR) & 0xf;
switch(vect)
{
case MODEM_STATUS :
val = uread(BSP_UART_COM2, MSR);
if(uart_data[BSP_UART_COM2].hwFlow)
{
if(val & CTS)
{
/* CTS high */
termios_stopped_com2 = 0;
if(termios_tx_hold_valid_com2)
{
termios_tx_hold_valid_com2 = 0;
BSP_uart_termios_write_com2(0, &termios_tx_hold_com2,
1);
}
}
else
{
/* CTS low */
termios_stopped_com2 = 1;
}
}
break;
case NO_MORE_INTR :
/* No more interrupts */
if(off != 0)
{
/* Update rx buffer */
if( driver_input_handler_com2 )
{
driver_input_handler_com2( termios_ttyp_com2, (char *)buf, off );
}
else
{
rtems_termios_enqueue_raw_characters(termios_ttyp_com2, (char *)buf, off);
}
}
return;
case TRANSMITTER_HODING_REGISTER_EMPTY :
/*
* TX holding empty: we have to disable these interrupts
* if there is nothing more to send.
*/
/* If nothing else to send disable interrupts */
ret = rtems_termios_dequeue_characters(termios_ttyp_com2, 1);
if ( ret == 0 ) {
termios_tx_active_com2 = 0;
uart_data[BSP_UART_COM2].ier &= ~(TRANSMIT_ENABLE);
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
}
break;
case RECEIVER_DATA_AVAIL :
case CHARACTER_TIMEOUT_INDICATION:
if ( uart_data[BSP_UART_COM2].ioMode == TERMIOS_TASK_DRIVEN ) {
/* ensure interrupts are enabled */
if ( uart_data[BSP_UART_COM2].ier & RECEIVE_ENABLE ) {
/* disable interrupts and notify termios */
uart_data[BSP_UART_COM2].ier &= ~(RECEIVE_ENABLE | RECEIVER_LINE_ST_ENABLE);
uwrite(BSP_UART_COM2, IER, uart_data[BSP_UART_COM2].ier);
rtems_termios_rxirq_occured(termios_ttyp_com2);
}
}
else {
/* RX data ready */
assert(off < sizeof(buf));
buf[off++] = uread(BSP_UART_COM2, RBR);
}
break;
case RECEIVER_ERROR:
/* RX error: eat character */
uartError(BSP_UART_COM2);
break;
default:
/* Should not happen */
assert(0);
return;
}
}
}
/* ================= GDB support ===================*/
int BSP_uart_dbgisr_com_regsav[4] RTEMS_UNUSED;
/*
* Interrupt service routine for COM1 - all,
* it does it check whether ^C is received
* if yes it will flip TF bit before returning
* Note: it should be installed as raw interrupt
* handler
*/
__asm__ (".p2align 4");
__asm__ (".text");
__asm__ (".globl BSP_uart_dbgisr_com1");
__asm__ ("BSP_uart_dbgisr_com1:");
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav"); /* Save eax */
__asm__ (" movl %ebx, BSP_uart_dbgisr_com_regsav + 4"); /* Save ebx */
__asm__ (" movl %edx, BSP_uart_dbgisr_com_regsav + 8"); /* Save edx */
__asm__ (" movl $0, %ebx"); /* Clear flag */
/*
* We know that only receive related interrupts
* are available, eat chars
*/
__asm__ ("uart_dbgisr_com1_1:");
__asm__ (" movw $0x3FD, %dx");
__asm__ (" inb %dx, %al"); /* Read LSR */
__asm__ (" andb $1, %al");
__asm__ (" cmpb $0, %al");
__asm__ (" je uart_dbgisr_com1_2");
__asm__ (" movw $0x3F8, %dx");
__asm__ (" inb %dx, %al"); /* Get input character */
__asm__ (" cmpb $3, %al");
__asm__ (" jne uart_dbgisr_com1_1");
/* ^C received, set flag */
__asm__ (" movl $1, %ebx");
__asm__ (" jmp uart_dbgisr_com1_1");
/* All chars read */
__asm__ ("uart_dbgisr_com1_2:");
/* If flag is set we have to tweak TF */
__asm__ (" cmpl $0, %ebx");
__asm__ (" je uart_dbgisr_com1_3");
/* Flag is set */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
/* Set TF bit */
__asm__ (" popl %eax"); /* Pop eip */
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 4"); /* Save it */
__asm__ (" popl %eax"); /* Pop cs */
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 8"); /* Save it */
__asm__ (" popl %eax"); /* Pop flags */
__asm__ (" orl $0x100, %eax"); /* Modify it */
__asm__ (" pushl %eax"); /* Push it back */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %eax"); /* Put back cs */
__asm__ (" pushl %eax");
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %eax"); /* Put back eip */
__asm__ (" pushl %eax");
/* Acknowledge IRQ */
__asm__ (" movb $0x20, %al");
__asm__ (" outb %al, $0x20");
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
__asm__ (" iret"); /* Done */
/* Flag is not set */
__asm__ ("uart_dbgisr_com1_3:");
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
/* Acknowledge irq */
__asm__ (" movb $0x20, %al");
__asm__ (" outb %al, $0x20");
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
__asm__ (" iret"); /* Done */
/*
* Interrupt service routine for COM2 - all,
* it does it check whether ^C is received
* if yes it will flip TF bit before returning
* Note: it has to be installed as raw interrupt
* handler
*/
__asm__ (".p2align 4");
__asm__ (".text");
__asm__ (".globl BSP_uart_dbgisr_com2");
__asm__ ("BSP_uart_dbgisr_com2:");
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav"); /* Save eax */
__asm__ (" movl %ebx, BSP_uart_dbgisr_com_regsav + 4"); /* Save ebx */
__asm__ (" movl %edx, BSP_uart_dbgisr_com_regsav + 8"); /* Save edx */
__asm__ (" movl $0, %ebx"); /* Clear flag */
/*
* We know that only receive related interrupts
* are available, eat chars
*/
__asm__ ("uart_dbgisr_com2_1:");
__asm__ (" movw $0x2FD, %dx");
__asm__ (" inb %dx, %al"); /* Read LSR */
__asm__ (" andb $1, %al");
__asm__ (" cmpb $0, %al");
__asm__ (" je uart_dbgisr_com2_2");
__asm__ (" movw $0x2F8, %dx");
__asm__ (" inb %dx, %al"); /* Get input character */
__asm__ (" cmpb $3, %al");
__asm__ (" jne uart_dbgisr_com2_1");
/* ^C received, set flag */
__asm__ (" movl $1, %ebx");
__asm__ (" jmp uart_dbgisr_com2_1");
/* All chars read */
__asm__ ("uart_dbgisr_com2_2:");
/* If flag is set we have to tweak TF */
__asm__ (" cmpl $0, %ebx");
__asm__ (" je uart_dbgisr_com2_3");
/* Flag is set */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
/* Set TF bit */
__asm__ (" popl %eax"); /* Pop eip */
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 4"); /* Save it */
__asm__ (" popl %eax"); /* Pop cs */
__asm__ (" movl %eax, BSP_uart_dbgisr_com_regsav + 8"); /* Save it */
__asm__ (" popl %eax"); /* Pop flags */
__asm__ (" orl $0x100, %eax"); /* Modify it */
__asm__ (" pushl %eax"); /* Push it back */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %eax"); /* Put back cs */
__asm__ (" pushl %eax");
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %eax"); /* Put back eip */
__asm__ (" pushl %eax");
/* Acknowledge IRQ */
__asm__ (" movb $0x20, %al");
__asm__ (" outb %al, $0x20");
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
__asm__ (" iret"); /* Done */
/* Flag is not set */
__asm__ ("uart_dbgisr_com2_3:");
__asm__ (" movl BSP_uart_dbgisr_com_regsav+4, %ebx"); /* Restore ebx */
__asm__ (" movl BSP_uart_dbgisr_com_regsav+8, %edx"); /* Restore edx */
/* Acknowledge irq */
__asm__ (" movb $0x20, %al");
__asm__ (" outb %al, $0x20");
__asm__ (" movl BSP_uart_dbgisr_com_regsav, %eax"); /* Restore eax */
__asm__ (" iret"); /* Done */

View File

@@ -1,454 +0,0 @@
/**
* @file realmode_int.c
*
* @ingroup i386_shared
*
* @brief Real mode interrupt call implementation
*/
/*
* Copyright (c) 2014 - CTU in Prague
* Jan Doležal ( dolezj21@fel.cvut.cz )
*
* 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/realmode_int.h>
#include <string.h>
#include <rtems/score/cpu.h>
/*
* offsets to \a i386_realmode_interrupt_registers declared in realmode_int.h
* used in inline assmbler for better readability
*/
#define IR_EAX_OFF "0x00"
#define IR_EBX_OFF "0x04"
#define IR_ECX_OFF "0x08"
#define IR_EDX_OFF "0x0C"
#define IR_ESI_OFF "0x10"
#define IR_EDI_OFF "0x14"
#define IR_DS_OFF "0x18"
#define IR_ES_OFF "0x1A"
#define IR_FS_OFF "0x1C"
#define IR_GS_OFF "0x1E"
/*
* offsets to \a rm_int_regs_bkp_param
*/
#define BKP_ESP_OFF "0x20"
#define BKP_SS_OFF "0x24"
#define BKP_DS_OFF "0x26"
#define RM_ENTRY "0x28"
#define PM_ENTRY "0x2C"
/**
* @brief parameters, results, backup values accessible in real mode
*
* @note Struct members not necessarily used in C. This serves also as
* layout of memory and it is used within inline assembler.
*/
typedef struct {
i386_realmode_interrupt_registers inoutregs;
/** spot for back up of protected mode stack pointer */
uint32_t pm_esp_bkp;
/** spot for back up of protected mode stack selector */
uint16_t pm_ss_bkp;
/** spot for back up of protected mode data selector */
uint16_t ds_bkp;
/** spot for setting up long indirect jump offset
to real mode from 16bit protected mode */
uint16_t rm_entry;
/** spot for setting up long indirect jump segment
to real mode from 16bit protected mode */
uint16_t rm_code_segment;
/** returning offset for long indirect jump back
to 32bit protected mode */
uint32_t pm_entry;
/** returning selector for long indirect jump back
to 32bit protected mode */
uint16_t pm_code_selector;
/* if this struct is to be modified update offset definitions as well */
} RTEMS_PACKED rm_int_regs_bkp_param;
/* offsets to \a pm_bkp_and_param */
#define BKP_IDTR_LIM "0x00"
#define BKP_IDTR_BASE "0x02"
#define BKP_ES_OFF "0x06"
#define BKP_FS_OFF "0x08"
#define BKP_GS_OFF "0x0A"
#define RML_ENTRY "0x0C"
#define RML_D_SEL "0x12"
#define RM_SS "0x14"
#define RM_SP "0x16"
#define RM_DS "0x18"
/**
* @brief backup values, pointers/parameters accessible in protected mode
*
* @note Struct members not necessarily used in C. This serves also as
* layout of memory and it is used within inline assembler.
*/
typedef struct {
/** spot for backup protected mode interrupt descriptor table register */
uint16_t idtr_lim_bkp;
/** @see idtr_lim_bkp */
uint32_t idtr_base_bkp;
/** spot to backup of ES register value in 32bit protected mode */
uint16_t es_bkp;
/** spot to backup of FS register value in 32bit protected mode */
uint16_t fs_bkp;
/** spot to backup of GS register value in 32bit protected mode */
uint16_t gs_bkp;
/** values for indirect jump to 16bit protected mode */
uint32_t rml_entry;
/** @see rml_entry */
uint16_t rml_code_selector;
/** data selector for 16bit protected mode */
uint16_t rml_data_selector;
/** values determinig location of real mode stack */
uint16_t rm_stack_segment;
/** @see rm_stack_segment */
uint16_t rm_stack_pointer;
/** data segment for real mode */
uint16_t rm_data_segment;
} RTEMS_PACKED pm_bkp_and_param;
/* addresses where we are going to put Interrupt buffer,
* parameter/returned/preserved values, stack and copy code
* for calling BIOS interrupt real mode interface
* The value is chosen arbitrarily in the first 640kB
* to be accessible for real mode. It should be out of range
* used by RTEMS because its base address is above 1MB.
* It has to be above first 4kB (or better 64kB) which could
* be used by BIOS.
*/
#define REAL_MODE_SPOT 0x12000
/* REAL_MODE_SPOT value is also top of real mode stack */
/* buffers positions and lengths */
#define DEFAULT_BUFFER_SIZE 512
static void *default_rm_buffer_spot = (void *)REAL_MODE_SPOT;
static uint16_t default_rm_buffer_size = DEFAULT_BUFFER_SIZE;
/* real mode stack */
#define STACK_SIZE 8192
#define INT_STACK_TOP REAL_MODE_SPOT
/******************************
* STACK * *
****************************** REAL_MODE_SPOT
* INT_BUF * 512 B *
******************************
* INT_REGs * 50 B *
******************************
* INT_FNC *~149 B *
******************************/
#define __DP_TYPE uint8_t
#define __DP_YES ((__DP_TYPE)1)
#define __DP_NO ((__DP_TYPE)-1)
#define __DP_FAIL ((__DP_TYPE)0)
static __DP_TYPE descsPrepared = __DP_NO;
/* rml - real mode alike */
#define rml_limit 0xFFFF
static uint16_t rml_code_dsc_index = 0;
static uint16_t rml_data_dsc_index = 0;
/**
* @brief Prepares real-mode like descriptors to be used for switching
* to real mode.
*
* Descriptors will be placed to the GDT.
*
* @param[in] base32 32-bit physical address to be used as base for 16-bit
* protected mode descriptors
* @retval __DP_YES descriptors are prepared
* @retval __DP_FAIL descriptors allocation failed (GDT too small)
*/
static __DP_TYPE prepareRMDescriptors (void *base32) {
static void *prevBase = (void *)-1;
/* check if descriptors were prepared already */
if (descsPrepared == __DP_YES && prevBase == base32)
return descsPrepared;
if (descsPrepared == __DP_FAIL)
return descsPrepared;
/* create 'real mode like' segment descriptors, for switching to real mode */
rml_code_dsc_index = i386_next_empty_gdt_entry();
if (rml_code_dsc_index == 0)
{
/* not enough space in GDT */
descsPrepared = __DP_FAIL;
return descsPrepared;
}
segment_descriptors flags_desc;
memset(&flags_desc, 0, sizeof(flags_desc));
flags_desc.type = 0xE; /* bits 4 */
flags_desc.descriptor_type = 0x1; /* bits 1 */
flags_desc.privilege = 0x0; /* bits 2 */
flags_desc.present = 0x1; /* bits 1 */
flags_desc.available = 0x0; /* bits 1 */
flags_desc.fixed_value_bits = 0x0; /* bits 1 */
flags_desc.operation_size = 0x0; /* bits 1 */
flags_desc.granularity = 0x0; /* bits 1 */
i386_fill_segment_desc_base((unsigned)base32, &flags_desc);
i386_fill_segment_desc_limit(rml_limit, &flags_desc);
if (i386_raw_gdt_entry(rml_code_dsc_index, &flags_desc) == 0)
{
/* selector to GDT out of range */
descsPrepared = __DP_FAIL;
return descsPrepared;
}
rml_data_dsc_index = i386_next_empty_gdt_entry();
if (rml_data_dsc_index == 0)
{
/* not enough space in GDT for both descriptors */
descsPrepared = __DP_FAIL;
return descsPrepared;
}
flags_desc.type = 0x2; /* bits 4 */
if (i386_raw_gdt_entry(rml_data_dsc_index, &flags_desc) == 0)
{
/* selector to GDT out of range */
descsPrepared = __DP_FAIL;
return descsPrepared;
}
prevBase = base32;
descsPrepared = __DP_YES;
return descsPrepared;
}
void *i386_get_default_rm_buffer(uint16_t *size) {
*size = default_rm_buffer_size;
return default_rm_buffer_spot;
}
int i386_real_interrupt_call(uint8_t interrupt_number,
i386_realmode_interrupt_registers *ir)
{
uint32_t pagingon;
rm_int_regs_bkp_param *int_passed_regs_spot;
/* place where the code switching to realmode and executing
interrupt is coppied */
void *rm_swtch_code_dst;
void *rm_stack_top;
size_t cpLength;
void *cpBeg;
/* values that can be passed from protected mode are stored in this struct
and they are passed later to the inline assembler executing interrupt */
volatile pm_bkp_and_param pm_bkp, *pm_bkp_addr;
unsigned short unused_offset;
__asm__ volatile( "\t"
"movl %%cr0, %%eax\n\t"
"andl %1, %%eax\n"
: "=a"(pagingon)
: "i"(CR0_PAGING)
);
if (pagingon)
return 0;
/* located under 1MB for real mode to be able to get/set values */
int_passed_regs_spot = (rm_int_regs_bkp_param *)
(default_rm_buffer_spot+default_rm_buffer_size);
/* position for real mode code reallocation to the first 1MB of RAM */
rm_swtch_code_dst = (void *)((uint32_t)int_passed_regs_spot +
sizeof(*int_passed_regs_spot));
rm_stack_top = (void *)INT_STACK_TOP;
if (prepareRMDescriptors(int_passed_regs_spot) != __DP_YES)
return 0;
pm_bkp_addr = &pm_bkp;
i386_Physical_to_real(
rm_stack_top - STACK_SIZE,
(unsigned short *)&pm_bkp.rm_stack_segment,
(unsigned short *)&pm_bkp.rm_stack_pointer
);
pm_bkp.rm_stack_pointer += STACK_SIZE;
pm_bkp.rml_code_selector = (rml_code_dsc_index<<3);
pm_bkp.rml_entry = ((uint32_t)rm_swtch_code_dst -
(uint32_t)int_passed_regs_spot);
pm_bkp.rml_data_selector = (rml_data_dsc_index<<3);
i386_Physical_to_real(
int_passed_regs_spot,
(unsigned short *)&pm_bkp.rm_data_segment,
&unused_offset
);
int_passed_regs_spot->inoutregs = *ir;
/* offset from the beginning of coppied code */
uint16_t rm_entry_offset;
__asm__ volatile(
"movw $(rment-cp_beg), %0\n\t"
: "=r"(rm_entry_offset)
);
i386_Physical_to_real(
rm_swtch_code_dst+rm_entry_offset,
(unsigned short *)&int_passed_regs_spot->rm_code_segment,
(unsigned short *)&int_passed_regs_spot->rm_entry
);
__asm__ volatile(
"movl $(cp_end), %0\n\t"
"movw %%cs, %1\n\t"
: "=mr"(int_passed_regs_spot->pm_entry),
"=mr"(int_passed_regs_spot->pm_code_selector)
);
/* copy code for switch to real mode and
executing interrupt to first MB of RAM */
__asm__ volatile( "\t"
"mov $cp_end-cp_beg, %0\n\t"
"mov $cp_beg, %1\n\t"
: "=rm"(cpLength), "=rm"(cpBeg)
);
memcpy(rm_swtch_code_dst, cpBeg, cpLength);
/* write interrupt number to be executed */
uint16_t interrupt_number_off;
uint8_t *interrupt_number_ptr;
__asm__ volatile( "\t"
"movw $intnum-cp_beg, %0\n\t"
: "=rm"(interrupt_number_off)
);
interrupt_number_ptr = (uint8_t *)(rm_swtch_code_dst+interrupt_number_off);
*interrupt_number_ptr = interrupt_number;
/* execute code that jumps to coppied function, which switches to real mode,
loads registers with values passed to interrupt and executes interrupt */
__asm__ volatile( "\t"
/* backup stack */
"movl %[regs_spot], %%ebx\n\t"
"movl %%esp, "BKP_ESP_OFF"(%%ebx)\n\t"
"movw %%ss, "BKP_SS_OFF"(%%ebx)\n\t"
/* backup data selector */
"movw %%ds, "BKP_DS_OFF"(%%ebx)\n\t"
/* backup other selectors */
"movl %[pm_bkp], %%esi\n\t"
"movw %%es, "BKP_ES_OFF"(%%esi)\n\t"
"movw %%fs, "BKP_FS_OFF"(%%esi)\n\t"
"movw %%gs, "BKP_GS_OFF"(%%esi)\n\t"
/* hopefully loader does not damage interrupt table on the beginning of
memory; that means length: 0x3FF, base: 0x0 */
/* preserve idtr */
"movl %%esi, %%eax\n\t"
"addl $"BKP_IDTR_LIM", %%eax\n\t"
"cli\n\t"
"sidt (%%eax)\n\t"
"movl $rmidt, %%eax\n\t"
"lidt (%%eax)\n\t"
/* prepare 'real mode like' data selector */
"movw "RML_D_SEL"(%%esi), %%ax\n\t"
/* prepare real mode data segment value */
"xorl %%edx,%%edx\n\t"
"movw "RM_DS"(%%esi), %%dx\n\t"
/* prepare real mode stack values */
"movw "RM_SS"(%%esi), %%cx\n\t"
"movzwl "RM_SP"(%%esi), %%esp\n\t"
/* jump to copied function and */
/* load 'real mode like' code selector */
"ljmp *"RML_ENTRY"(%%esi)\n"
"rmidt:"/* limit and base for realmode interrupt descriptor table */
".word 0x3FF\n\t"
".long 0\n\t"
/* load 'real mode like' data selectors */
"cp_beg: .code16\n\t"
"movw %%ax, %%ss\n\t"
"movw %%ax, %%ds\n\t"
"movw %%ax, %%es\n\t"
"movw %%ax, %%fs\n\t"
"movw %%ax, %%gs\n\t"
/* disable protected mode */
"movl %%cr0, %%eax\n\t"
"and %[cr0_prot_dis], %%ax\n\t"
"movl %%eax, %%cr0\n\t"
/* base for data selector of 16-bit protected mode is
at beginning of passed regs */
/* flush prefetch queue by far jumping */
"ljmp *"RM_ENTRY"\n\t"
"rment: "
/* establish rm stack - esp was already set in 32-bit protected mode*/
"movw %%cx, %%ss\n\t"
/* set data segment (value prepared in 32-bit prot mode) */
"movw %%dx, %%ds\n\t"
/* count real mode pointer so we don't need to overuse address
prefix (by using 32bit addresses in 16bit context) */
"shll $4,%%edx\n\t"
"subl %%edx,%%ebx\n\t"
/* prepare values to be used after interrupt call */
"pushw %%bx\n\t"
"pushw %%ds\n\t"
/* fill registers with parameters */
"movw " IR_DS_OFF"(%%bx), %%ax\n\t"
"pushw %%ax\n\t"
"movl "IR_EAX_OFF"(%%bx), %%eax\n\t"
"movl "IR_ECX_OFF"(%%bx), %%ecx\n\t"
"movl "IR_EDX_OFF"(%%bx), %%edx\n\t"
"movl "IR_EDI_OFF"(%%bx), %%edi\n\t"
"movl "IR_ESI_OFF"(%%bx), %%esi\n\t"
"movw " IR_ES_OFF"(%%bx), %%es\n\t"
"movw " IR_FS_OFF"(%%bx), %%fs\n\t"
"movw " IR_GS_OFF"(%%bx), %%gs\n\t"
/* prepare ebx register */
"movl "IR_EBX_OFF"(%%bx), %%ebx\n\t"
/* prepare ds */
"popw %%ds\n\t"
/* interrupt instruction */
".byte 0xCD\n\t"
"intnum: .byte 0x0\n\t"
/* fill return structure */
"pushw %%ds\n\t"
"pushl %%ebx\n\t"
"movw 0x6(%%esp), %%ds\n\t"
"movw 0x8(%%esp),%%bx\n\t" /* regs_spot */
"movl %%eax,"IR_EAX_OFF"(%%bx)\n\t"
"popl %%eax\n\t"
"movl %%eax,"IR_EBX_OFF"(%%bx)\n\t"
"movl %%ecx,"IR_ECX_OFF"(%%bx)\n\t"
"movl %%edx,"IR_EDX_OFF"(%%bx)\n\t"
"movl %%esi,"IR_ESI_OFF"(%%bx)\n\t"
"movl %%edi,"IR_EDI_OFF"(%%bx)\n\t"
"popw %%ax\n\t"
"movw %%ax, " IR_DS_OFF"(%%bx)\n\t"
"movw %%es, " IR_ES_OFF"(%%bx)\n\t"
"movw %%fs, " IR_FS_OFF"(%%bx)\n\t"
"movw %%gs, " IR_GS_OFF"(%%bx)\n\t"
/* prepare protected mode data segment */
"movw "BKP_DS_OFF"(%%bx), %%ax\n\t"
/* restore protected mode stack values */
"movl "BKP_ESP_OFF"(%%bx),%%esp\n\t"
"movw "BKP_SS_OFF"(%%bx), %%dx\n\t"
/* return to protected mode */
"movl %%cr0, %%ecx \n\t"
"or %[cr0_prot_ena], %%cx\n\t"
"movl %%ecx, %%cr0 \n\t"
"ljmpl *"PM_ENTRY"(%%bx)\n\t"
".code32\n"
/* reload segmentation registers */
"cp_end:"
"movw %%ax, %%ds\n\t"
/* restore stack segment in protected mode context */
"movw %%dx, %%ss\n\t"
"movl %[pm_bkp], %%esi\n\t"
"movw "BKP_ES_OFF"(%%esi), %%es\n\t"
"movw "BKP_FS_OFF"(%%esi), %%fs\n\t"
"movw "BKP_GS_OFF"(%%esi), %%gs\n\t"
/* restore IDTR */
"addl $"BKP_IDTR_LIM", %%esi\n\t"
"lidt (%%esi)\n\t"
:
: [regs_spot]"m"(int_passed_regs_spot),
[pm_bkp]"m"(pm_bkp_addr),
[cr0_prot_ena]"i"(CR0_PROTECTION_ENABLE),
[cr0_prot_dis]"i"(~CR0_PROTECTION_ENABLE)
: "memory", "ebx", "ecx", "edx", "esi", "edi"
);
*ir = int_passed_regs_spot->inoutregs;
return 1;
}

View File

@@ -1,10 +0,0 @@
The following files were developed by Erich Boleyn <erich@uruk.org>.
The original files were copied from http://www.uruk.org/mps/ and committed
into cvs as is. These files will be modified for RTEMS smp support,
but the original files are being commited in order to track any file
changes that occur. We are attempting to keep these changes as minimal
and conainted as possible.
+ smp-imps.c
+ smp-imps.h
+ ../irq/apic.h

View File

@@ -1,22 +0,0 @@
/*
* 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/score/cpu.h>
#include <bsp/apic.h>
#include <bsp/smp-imps.h>
static int lapic_dummy = 0;
unsigned imps_lapic_addr = ((unsigned)(&lapic_dummy)) - LAPIC_ID;
uint32_t _CPU_SMP_Get_current_processor( void )
{
return APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
}

View File

@@ -1,828 +0,0 @@
/*
* Author: Erich Boleyn <erich@uruk.org>
* http://www.uruk.org/~erich/
*
* Copyright (c) 1997-2011 Erich Boleyn. 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*/
/*
* Source file implementing Intel MultiProcessor Specification (MPS)
* version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,
* with hooks for running correctly on a standard PC without the hardware.
*
* This file was created from information in the Intel MPS version 1.4
* document, order number 242016-004, which can be ordered from the
* Intel literature center.
*
* General limitations of this code:
*
* (1) : This code has never been tested on an MPS-compatible system with
* 486 CPUs, but is expected to work.
* (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and
* that 32-bit pointers and memory addressing is used uniformly.
*/
#define _SMP_IMPS_C
/*
* Includes here
*/
#if 0
#define IMPS_DEBUG
#endif
#include <bsp/apic.h>
#include <bsp/smp-imps.h>
#include <bsp/irq.h>
#include <rtems/score/smpimpl.h>
/*
* XXXXX The following absolutely must be defined!!!
*
* The "KERNEL_PRINT" could be made a null macro with no danger, of
* course, but pretty much nothing would work without the other
* ones defined.
*/
#if 0
#define KERNEL_PRINT(x) /* some kind of print function */
#define CMOS_WRITE_BYTE(x,y) /* write unsigned char "y" at CMOS loc "x" */
#define CMOS_READ_BYTE(x) /* read unsigned char at CMOS loc "x" */
#define PHYS_TO_VIRTUAL(x) /* convert physical address "x" to virtual */
#define VIRTUAL_TO_PHYS(x) /* convert virtual address "x" to physical */
#define UDELAY(x) /* delay roughly at least "x" microsecs */
#define READ_MSR_LO(x) /* Read MSR low function */
#else
#include <string.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/bspIo.h>
#include <rtems/score/cpu.h>
#include <assert.h>
extern void _pc386_delay(void);
/* #define KERNEL_PRINT(_format) printk(_format) */
static void CMOS_WRITE_BYTE(
unsigned int offset,
unsigned char value
)
{
if ( offset < 128 ) {
outport_byte( 0x70, offset );
outport_byte( 0x71, value );
} else {
outport_byte( 0x72, offset );
outport_byte( 0x73, value );
}
}
static unsigned char CMOS_READ_BYTE(
unsigned int offset
)
{
unsigned char value;
if ( offset < 128 ) {
outport_byte( 0x70, offset );
inport_byte( 0x71, value );
} else {
outport_byte( 0x72, offset );
inport_byte( 0x73, value );
}
return value;
}
#define PHYS_TO_VIRTUAL(_x) _x
#define VIRTUAL_TO_PHYS(_x) _x
static void UDELAY(int x)
{ int _i = x;
while ( _i-- )
_pc386_delay();
}
#define READ_MSR_LO(_x) \
(unsigned int)(read_msr(_x) & 0xffffffff)
static inline unsigned long long read_msr(unsigned int msr)
{
unsigned long long value;
asm volatile("rdmsr" : "=A" (value) : "c" (msr));
return value;
}
#endif
/*
* Defines that are here so as not to be in the global header file.
*/
#define EBDA_SEG_ADDR 0x40E
#define BIOS_RESET_VECTOR 0x467
#define LAPIC_ADDR_DEFAULT 0xFEE00000uL
#define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
#define CMOS_RESET_CODE 0xF
#define CMOS_RESET_JUMP 0xa
#define CMOS_BASE_MEMORY 0x15
/*
* Static defines here for SMP use.
*/
#define DEF_ENTRIES 23
static struct {
imps_processor proc[2];
imps_bus bus[2];
imps_ioapic ioapic;
imps_interrupt intin[16];
imps_interrupt lintin[2];
} defconfig = {
{ { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
{ IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
{ { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
{ 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
{ IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
{ { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
{ IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
{ { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
{ IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
};
/*
* Exported globals here.
*/
volatile int imps_release_cpus = 0;
int imps_enabled = 0;
int imps_num_cpus = 1;
unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
/* now defined in getcpuid.c */
extern unsigned imps_lapic_addr;
static void secondary_cpu_initialize(void);
/*
* MPS checksum function
*
* Function finished.
*/
static int
get_checksum(unsigned start, int length)
{
unsigned sum = 0;
while (length-- > 0) {
sum += *((unsigned char *) (start++));
}
return (sum&0xFF);
}
/*
* APIC ICR write and status check function.
*/
static int
send_ipi(unsigned int dst, unsigned int v)
{
int to, send_status;
IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (dst << 24));
IMPS_LAPIC_WRITE(LAPIC_ICR, v);
/* Wait for send to finish */
to = 0;
do {
UDELAY(100);
send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
} while (send_status && (to++ < 1000));
return (to < 1000);
}
/*
* Primary function for booting individual CPUs.
*
* This must be modified to perform whatever OS-specific initialization
* that is required.
*/
static int
boot_cpu(imps_processor *proc)
{
int apicid = proc->apic_id, success = 1;
unsigned bootaddr;
unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);
/*
* Copy boot code for secondary CPUs here. Find it in between
* "patch_code_start" and "patch_code_end" symbols. The other CPUs
* will start there in 16-bit real mode under the 1MB boundary.
* "patch_code_start" should be placed at a 4K-aligned address
* under the 1MB boundary.
*/
uint32_t *reset;
bootaddr = (512-64)*1024;
reset= (uint32_t *)bootaddr;
memcpy(
(char *) bootaddr,
_binary_appstart_bin_start,
(size_t)_binary_appstart_bin_size
);
reset[1] = (uint32_t)secondary_cpu_initialize;
reset[2] = (uint32_t)_Per_CPU_Get_by_index(apicid)->interrupt_stack_high;
/*
* Generic CPU startup sequence starts here.
*/
/* set BIOS reset vector */
CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);
*((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);
/* clear the APIC error register */
IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
IMPS_LAPIC_READ(LAPIC_ESR);
/* assert INIT IPI */
send_ipi(
apicid,
LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT
);
UDELAY(10000);
/* de-assert INIT IPI */
send_ipi(apicid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
UDELAY(10000);
/*
* Send Startup IPIs if not an old pre-integrated APIC.
*/
if (proc->apic_ver >= APIC_VER_NEW) {
int i;
for (i = 1; i <= 2; i++) {
send_ipi(apicid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
UDELAY(1000);
}
}
/*
* Generic CPU startup sequence ends here, the rest is cleanup.
*/
/* clear the APIC error register */
IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
IMPS_LAPIC_READ(LAPIC_ESR);
/* clean up BIOS reset vector */
CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
*((volatile unsigned *) bios_reset_vector) = 0;
printk("\n");
return success;
}
/*
* read bios stuff and fill tables
*/
static void
add_processor(imps_processor *proc)
{
int apicid = proc->apic_id;
printk(" Processor [APIC id %d ver %d]: ", apicid, proc->apic_ver);
if (!(proc->flags & IMPS_FLAG_ENABLED)) {
printk("DISABLED\n");
return;
}
if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
printk("#0 BootStrap Processor (BSP)\n");
return;
}
if (boot_cpu(proc)) {
/* XXXXX add OS-specific setup for secondary CPUs here */
imps_cpu_apic_map[imps_num_cpus] = apicid;
imps_apic_cpu_map[apicid] = imps_num_cpus;
imps_num_cpus++;
}
}
static void
add_bus(imps_bus *bus)
{
char str[8];
memcpy(str, bus->bus_type, 6);
str[6] = 0;
printk(" Bus id %d is %s\n", bus->id, str);
/* XXXXX add OS-specific code here */
}
static void
add_ioapic(imps_ioapic *ioapic)
{
printk(" I/O APIC id %d ver %d, address: 0x%x ",
ioapic->id, ioapic->ver, ioapic->addr);
if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
printk("DISABLED\n");
return;
}
printk("\n");
/* XXXXX add OS-specific code here */
}
static void
imps_read_config_table(unsigned start, int count)
{
while (count-- > 0) {
switch (*((unsigned char *)start)) {
case IMPS_BCT_PROCESSOR:
if ( imps_num_cpus < rtems_configuration_get_maximum_processors() ) {
if (_SMP_Should_start_processor((uint32_t) imps_num_cpus)) {
add_processor((imps_processor *)start);
}
} else
imps_num_cpus++;
start += 12; /* 20 total */
break;
case IMPS_BCT_BUS:
add_bus((imps_bus *)start);
break;
case IMPS_BCT_IOAPIC:
add_ioapic((imps_ioapic *)start);
break;
#if 0 /* XXXXX uncomment this if "add_io_interrupt" is implemented */
case IMPS_BCT_IO_INTERRUPT:
add_io_interrupt((imps_interrupt *)start);
break;
#endif
#if 0 /* XXXXX uncomment this if "add_local_interrupt" is implemented */
case IMPS_BCT_LOCAL_INTERRUPT:
add_local_interupt((imps_interrupt *)start);
break;
#endif
default:
break;
}
start += 8;
}
if ( imps_num_cpus > rtems_configuration_get_maximum_processors() ) {
printk(
"WARNING!! Found more CPUs (%d) than configured for (%d)!!\n",
imps_num_cpus - 1,
rtems_configuration_get_maximum_processors()
);
imps_num_cpus = rtems_configuration_get_maximum_processors();
return;
}
}
static int
imps_bad_bios(imps_fps *fps_ptr)
{
int sum;
imps_cth *local_cth_ptr
= (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
printk(" Invalid MP System Configuration type %d\n",
fps_ptr->feature_info[0]);
return 1;
}
if (fps_ptr->cth_ptr) {
sum = get_checksum((unsigned)local_cth_ptr,
local_cth_ptr->base_length);
if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
printk(
" Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
(unsigned)(fps_ptr->cth_ptr),
sum
);
return 1;
}
if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
printk(
" Bad MP Config Table sub-revision # %d\n",
local_cth_ptr->spec_rev
);
return 1;
}
if (local_cth_ptr->extended_length) {
sum = (get_checksum(((unsigned)local_cth_ptr)
+ local_cth_ptr->base_length,
local_cth_ptr->extended_length)
+ local_cth_ptr->extended_checksum) & 0xFF;
if (sum) {
printk(" Bad Extended MP Config Table checksum 0x%x\n", sum);
return 1;
}
}
} else if (!fps_ptr->feature_info[0]) {
printk(" Missing configuration information\n");
return 1;
}
return 0;
}
static void
imps_read_bios(imps_fps *fps_ptr)
{
int apicid;
unsigned cth_start, cth_count;
imps_cth *local_cth_ptr
= (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
char *str_ptr;
printk("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
fps_ptr->spec_rev);
/*
* Do all checking of errors which would definitely
* lead to failure of the SMP boot here.
*/
if (imps_bad_bios(fps_ptr)) {
printk(" Disabling MPS support\n");
return;
}
if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
str_ptr = "IMCR and PIC";
} else {
str_ptr = "Virtual Wire";
}
if (fps_ptr->cth_ptr) {
imps_lapic_addr = local_cth_ptr->lapic_addr;
} else {
imps_lapic_addr = LAPIC_ADDR_DEFAULT;
}
printk(" APIC config: \"%s mode\" Local APIC address: 0x%x\n",
str_ptr, imps_lapic_addr);
if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
printk("Inconsistent Local APIC address, Disabling SMP support\n");
return;
}
imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
/*
* Setup primary CPU.
*/
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
imps_cpu_apic_map[0] = apicid;
imps_apic_cpu_map[apicid] = 0;
if (fps_ptr->cth_ptr) {
char str1[16], str2[16];
memcpy(str1, local_cth_ptr->oem_id, 8);
str1[8] = 0;
memcpy(str2, local_cth_ptr->prod_id, 12);
str2[12] = 0;
printk(" OEM id: %s Product id: %s\n", str1, str2);
cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
cth_count = local_cth_ptr->entry_count;
} else {
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID;
defconfig.ioapic.id
= APIC_ID(*((volatile unsigned *)
(IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER;
defconfig.ioapic.ver
= APIC_VERSION(*((volatile unsigned *)
(IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
defconfig.proc[apicid].flags
= IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
imps_num_cpus = 2;
if (fps_ptr->feature_info[0] == 1
|| fps_ptr->feature_info[0] == 5) {
memcpy(defconfig.bus[0].bus_type, "ISA ", 6);
}
if (fps_ptr->feature_info[0] == 4
|| fps_ptr->feature_info[0] == 7) {
memcpy(defconfig.bus[0].bus_type, "MCA ", 6);
}
if (fps_ptr->feature_info[0] > 4) {
defconfig.proc[0].apic_ver = 0x10;
defconfig.proc[1].apic_ver = 0x10;
defconfig.bus[1].type = IMPS_BCT_BUS;
}
if (fps_ptr->feature_info[0] == 2) {
defconfig.intin[2].type = 255;
defconfig.intin[13].type = 255;
}
if (fps_ptr->feature_info[0] == 7) {
defconfig.intin[0].type = 255;
}
cth_start = (unsigned) &defconfig;
cth_count = DEF_ENTRIES;
}
imps_read_config_table(cth_start, cth_count);
/* %%%%% ESB read extended entries here */
imps_enabled = 1;
}
/*
* Given a region to check, this actually looks for the "MP Floating
* Pointer Structure". The return value indicates if the correct
* signature and checksum for a floating pointer structure of the
* appropriate spec revision was found. If so, then do not search
* further.
*
* NOTE: The memory scan will always be in the bottom 1 MB.
*
* This function presumes that "start" will always be aligned to a 16-bit
* boundary.
*
* Function finished.
*/
static int
imps_scan(unsigned start, unsigned length)
{
printk("Scanning from 0x%x for %d bytes\n", start, length);
while (length > 0) {
imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
if (fps_ptr->sig == IMPS_FPS_SIGNATURE
&& fps_ptr->length == 1
&& (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
&& !get_checksum(start, 16)) {
printk("Found MP Floating Structure Pointer at %x\n", start);
imps_read_bios(fps_ptr);
return 1;
}
length -= 16;
start += 16;
}
return 0;
}
#if !defined(__rtems__)
/*
* This is the primary function to "force" SMP support, with
* the assumption that you have consecutively numbered APIC ids.
*/
int
imps_force(int ncpus)
{
int apicid, i;
imps_processor p;
printk("Intel MultiProcessor \"Force\" Support\n");
imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
/*
* Setup primary CPU.
*/
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
imps_cpu_apic_map[0] = apicid;
imps_apic_cpu_map[apicid] = 0;
p.type = 0;
p.apic_ver = 0x10;
p.signature = p.features = 0;
for (i = 0; i < ncpus; i++) {
if (apicid == i) {
p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
} else {
p.flags = IMPS_FLAG_ENABLED;
}
p.apic_id = i;
add_processor(&p);
}
return imps_num_cpus;
}
#endif
/*
* This is the primary function for probing for MPS compatible hardware
* and BIOS information. Call this during the early stages of OS startup,
* before memory can be messed up.
*
* The probe looks for the "MP Floating Pointer Structure" at locations
* listed at the top of page 4-2 of the spec.
*
* Environment requirements from the OS to run:
*
* (1) : A non-linear virtual to physical memory mapping is probably OK,
* as (I think) the structures all fall within page boundaries,
* but a linear mapping is recommended. Currently assumes that
* the mapping will remain identical over time (which should be
* OK since it only accesses memory which shouldn't be munged
* by the OS anyway).
* (2) : The OS only consumes memory which the BIOS says is OK to use,
* and not any of the BIOS standard areas (the areas 0x400 to
* 0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
* RAM). Sometimes a small amount of physical RAM is not
* reported by the BIOS, to be used to store MPS and other
* information.
* (3) : It must be possible to read the CMOS.
* (4) : There must be between 512K and 640K of lower memory (this is a
* sanity check).
*
* Function finished.
*/
static int
imps_probe(void)
{
/*
* Determine possible address of the EBDA
*/
unsigned ebda_addr = *((unsigned short *)
PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
/*
* Determine amount of installed lower memory (not *available*
* lower memory).
*
* NOTE: This should work reliably as long as we verify the
* machine is at least a system that could possibly have
* MPS compatibility to begin with.
*/
unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
| CMOS_READ_BYTE(CMOS_BASE_MEMORY)) << 10;
#ifdef IMPS_DEBUG
imps_enabled = 0;
imps_num_cpus = 1;
#endif
/*
* Sanity check : if this isn't reasonable, it is almost impossibly
* unlikely to be an MPS compatible machine, so return failure.
*/
if (mem_lower < 512*1024 || mem_lower > 640*1024) {
return 0;
}
if (ebda_addr > mem_lower - 1024
|| ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
* 1024 > mem_lower) {
ebda_addr = 0;
}
if (((ebda_addr && imps_scan(ebda_addr, 1024))
|| (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
|| imps_scan(0xF0000, 0x10000)) && imps_enabled) {
return imps_num_cpus;
}
/*
* If no BIOS info on MPS hardware is found, then return failure.
*/
return 0;
}
/*
* RTEMS SMP BSP Support
*/
static void smp_apic_ack(void)
{
(void) IMPS_LAPIC_READ(LAPIC_SPIV); /* dummy read */
IMPS_LAPIC_WRITE(LAPIC_EOI, 0 ); /* ACK the interrupt */
}
/* FIXME: There should be a header file for this */
void Clock_isr(void *arg);
static void bsp_inter_processor_interrupt(void *arg)
{
unsigned long message;
(void) arg;
smp_apic_ack();
message = _SMP_Inter_processor_interrupt_handler();
if ((message & SMP_MESSAGE_CLOCK_TICK) != 0) {
Clock_isr(NULL);
}
}
static void ipi_install_irq(void)
{
rtems_status_code status;
status = rtems_interrupt_handler_install(
16,
"smp-imps",
RTEMS_INTERRUPT_UNIQUE,
bsp_inter_processor_interrupt,
NULL
);
assert(status == RTEMS_SUCCESSFUL);
}
#ifdef __SSE__
extern void enable_sse(void);
#endif
/* pc386 specific initialization */
static void secondary_cpu_initialize(void)
{
int apicid;
asm volatile( "lidt IDT_Descriptor" );
apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
#ifdef __SSE__
enable_sse();
#endif
_SMP_Start_multitasking_on_secondary_processor();
}
uint32_t _CPU_SMP_Initialize( void )
{
/* XXX need to deal with finding too many cores */
return (uint32_t) imps_probe();
}
void _CPU_SMP_Prepare_start_multitasking( void )
{
/* Do nothing */
}
bool _CPU_SMP_Start_processor( uint32_t cpu_index )
{
(void) cpu_index;
return true;
}
void _CPU_SMP_Finalize_initialization( uint32_t cpu_count )
{
if ( cpu_count > 1 )
ipi_install_irq();
}
void _CPU_SMP_Send_interrupt( uint32_t target_processor_index )
{
send_ipi( target_processor_index, 0x30 );
}