mirror of
https://gitlab.com/qemu-project/openbios.git
synced 2024-02-13 08:34:06 +08:00
git-svn-id: svn://coreboot.org/openbios/openbios-devel@224 f158a5a8-5612-0410-a976-696ce0be7e32
590 lines
12 KiB
C
590 lines
12 KiB
C
/*
|
|
* Copyright (C) 2003, 2004 Stefan Reinauer
|
|
*
|
|
* See the file "COPYING" for further information about
|
|
* the copyright and warranty status of this work.
|
|
*/
|
|
|
|
#include "openbios/config.h"
|
|
#include "openbios/bindings.h"
|
|
#include "openbios/kernel.h"
|
|
#include "openbios.h"
|
|
#include "video_subr.h"
|
|
#include "libc/vsprintf.h"
|
|
#include "sys_info.h"
|
|
#include "boot.h"
|
|
|
|
#define REGISTER_NAMED_NODE( name, path ) do { \
|
|
bind_new_node( name##_flags_, name##_size_, \
|
|
path, name##_m, sizeof(name##_m)/sizeof(method_t)); \
|
|
} while(0)
|
|
|
|
#define REGISTER_NODE_METHODS( name, path ) do { \
|
|
const char *paths[1]; \
|
|
\
|
|
paths[0] = path; \
|
|
bind_node( name##_flags_, name##_size_, \
|
|
paths, 1, name##_m, sizeof(name##_m)/sizeof(method_t)); \
|
|
} while(0)
|
|
|
|
/* ******************************************************************
|
|
* serial console functions
|
|
* ****************************************************************** */
|
|
|
|
#define SER_SIZE 8
|
|
|
|
#define RBR(x) x==2?0x2f8:0x3f8
|
|
#define THR(x) x==2?0x2f8:0x3f8
|
|
#define IER(x) x==2?0x2f9:0x3f9
|
|
#define IIR(x) x==2?0x2fa:0x3fa
|
|
#define LCR(x) x==2?0x2fb:0x3fb
|
|
#define MCR(x) x==2?0x2fc:0x3fc
|
|
#define LSR(x) x==2?0x2fd:0x3fd
|
|
#define MSR(x) x==2?0x2fe:0x3fe
|
|
#define SCR(x) x==2?0x2ff:0x3ff
|
|
#define DLL(x) x==2?0x2f8:0x3f8
|
|
#define DLM(x) x==2?0x2f9:0x3f9
|
|
|
|
static int uart_charav(int port)
|
|
{
|
|
return ((inb(LSR(port)) & 1) != 0);
|
|
}
|
|
|
|
static char uart_getchar(int port)
|
|
{
|
|
while (!uart_charav(port));
|
|
return ((char) inb(RBR(port)) & 0177);
|
|
}
|
|
|
|
static void uart_putchar(int port, unsigned char c)
|
|
{
|
|
if (c == '\n')
|
|
uart_putchar(port, '\r');
|
|
while (!(inb(LSR(port)) & 0x20));
|
|
outb(c, THR(port));
|
|
}
|
|
|
|
static void uart_init_line(int port, unsigned long baud)
|
|
{
|
|
int i, baudconst;
|
|
|
|
switch (baud) {
|
|
case 115200:
|
|
baudconst = 1;
|
|
break;
|
|
case 57600:
|
|
baudconst = 2;
|
|
break;
|
|
case 38400:
|
|
baudconst = 3;
|
|
break;
|
|
case 19200:
|
|
baudconst = 6;
|
|
break;
|
|
case 9600:
|
|
default:
|
|
baudconst = 12;
|
|
break;
|
|
}
|
|
|
|
outb(0x87, LCR(port));
|
|
outb(0x00, DLM(port));
|
|
outb(baudconst, DLL(port));
|
|
outb(0x07, LCR(port));
|
|
outb(0x0f, MCR(port));
|
|
|
|
for (i = 10; i > 0; i--) {
|
|
if (inb(LSR(port)) == (unsigned int) 0)
|
|
break;
|
|
inb(RBR(port));
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_CONSOLE
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
|
|
static void serial_cls(void);
|
|
static void serial_putchar(int c);
|
|
|
|
int uart_init(int port, unsigned long speed)
|
|
{
|
|
uart_init_line(port, speed);
|
|
return -1;
|
|
}
|
|
|
|
static void serial_putchar(int c)
|
|
{
|
|
uart_putchar(CONFIG_SERIAL_PORT, (unsigned char) (c & 0xff));
|
|
}
|
|
|
|
static void serial_cls(void)
|
|
{
|
|
serial_putchar(27);
|
|
serial_putchar('[');
|
|
serial_putchar('H');
|
|
serial_putchar(27);
|
|
serial_putchar('[');
|
|
serial_putchar('J');
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************************************
|
|
* simple polling video/keyboard console functions
|
|
* ****************************************************************** */
|
|
|
|
#ifdef CONFIG_DEBUG_CONSOLE_VGA
|
|
|
|
/* raw vga text mode */
|
|
#define COLUMNS 80 /* The number of columns. */
|
|
#define LINES 25 /* The number of lines. */
|
|
#define ATTRIBUTE 7 /* The attribute of an character. */
|
|
|
|
#define APB_MEM_BASE 0x1ff00000000ULL
|
|
#define VGA_BASE (APB_MEM_BASE + 0x4a0000ULL) /* Beginning of video memory address. */
|
|
#define TEXT_BASE (VGA_BASE + 0x18000ULL) /* The text memory address. */
|
|
|
|
/* VGA Index and Data Registers */
|
|
#define VGA_REG_INDEX 0x03D4 /* VGA index register */
|
|
#define VGA_REG_DATA 0x03D5 /* VGA data register */
|
|
|
|
#define VGA_IDX_CURMSL 0x09 /* cursor maximum scan line */
|
|
#define VGA_IDX_CURSTART 0x0A /* cursor start */
|
|
#define VGA_IDX_CUREND 0x0B /* cursor end */
|
|
#define VGA_IDX_CURLO 0x0F /* cursor position (low 8 bits) */
|
|
#define VGA_IDX_CURHI 0x0E /* cursor position (high 8 bits) */
|
|
|
|
/* Save the X and Y position. */
|
|
static int xpos, ypos;
|
|
/* Point to the video memory. */
|
|
static unsigned char *video = (unsigned char *) TEXT_BASE;
|
|
|
|
static void video_initcursor(void)
|
|
{
|
|
u8 val;
|
|
outb(VGA_IDX_CURMSL, VGA_REG_INDEX);
|
|
val = inb(VGA_REG_DATA) & 0x1f; /* maximum scan line -1 */
|
|
|
|
outb(VGA_IDX_CURSTART, VGA_REG_INDEX);
|
|
outb(0, VGA_REG_DATA);
|
|
|
|
outb(VGA_IDX_CUREND, VGA_REG_INDEX);
|
|
outb(val, VGA_REG_DATA);
|
|
}
|
|
|
|
|
|
|
|
static void video_poscursor(unsigned int x, unsigned int y)
|
|
{
|
|
unsigned short pos;
|
|
|
|
/* Calculate new cursor position as a function of x and y */
|
|
pos = (y * COLUMNS) + x;
|
|
|
|
/* Output the new position to VGA card */
|
|
outb(VGA_IDX_CURLO, VGA_REG_INDEX); /* output low 8 bits */
|
|
outb((u8) (pos), VGA_REG_DATA);
|
|
outb(VGA_IDX_CURHI, VGA_REG_INDEX); /* output high 8 bits */
|
|
outb((u8) (pos >> 8), VGA_REG_DATA);
|
|
|
|
};
|
|
|
|
|
|
static void video_newline(void)
|
|
{
|
|
xpos = 0;
|
|
|
|
if (ypos < LINES - 1) {
|
|
ypos++;
|
|
} else {
|
|
int i;
|
|
memmove((void *) video, (void *) (video + 2 * COLUMNS),
|
|
(LINES - 1) * COLUMNS * 2);
|
|
|
|
for (i = ((LINES - 1) * 2 * COLUMNS);
|
|
i < 2 * COLUMNS * LINES;) {
|
|
video[i++] = 0;
|
|
video[i++] = ATTRIBUTE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Put the character C on the screen. */
|
|
static void video_putchar(int c)
|
|
{
|
|
int p=1;
|
|
|
|
if (c == '\n' || c == '\r') {
|
|
video_newline();
|
|
return;
|
|
}
|
|
|
|
if (c == '\b') {
|
|
if (xpos) xpos--;
|
|
c=' ';
|
|
p=0;
|
|
}
|
|
|
|
|
|
if (xpos >= COLUMNS)
|
|
video_newline();
|
|
|
|
*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
|
|
*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;
|
|
|
|
if (p)
|
|
xpos++;
|
|
|
|
video_poscursor(xpos, ypos);
|
|
}
|
|
|
|
static void video_cls(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 2 * COLUMNS * LINES;) {
|
|
video[i++] = 0;
|
|
video[i++] = ATTRIBUTE;
|
|
}
|
|
|
|
|
|
xpos = 0;
|
|
ypos = 0;
|
|
|
|
video_initcursor();
|
|
video_poscursor(xpos, ypos);
|
|
}
|
|
|
|
#ifdef CONFIG_DRIVER_VGA
|
|
#include "../../modules/font_8x16.c"
|
|
#endif
|
|
|
|
void video_init(void)
|
|
{
|
|
video=(unsigned char *)TEXT_BASE;
|
|
|
|
#ifdef CONFIG_DRIVER_VGA
|
|
vga_load_regs();
|
|
vga_font_load((unsigned char *)VGA_BASE, fontdata_8x16, 16, 256);
|
|
vga_set_amode();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* keyboard driver
|
|
*/
|
|
|
|
static const char normal[] = {
|
|
0x0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-',
|
|
'=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o',
|
|
'p', '[', ']', 0xa, 0x0, 'a', 's', 'd', 'f', 'g', 'h', 'j',
|
|
'k', 'l', ';', 0x27, 0x60, 0x0, 0x5c, 'z', 'x', 'c', 'v', 'b',
|
|
'n', 'm', ',', '.', '/', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '0', 0x7f
|
|
};
|
|
|
|
static const char shifted[] = {
|
|
0x0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_',
|
|
'+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O',
|
|
'P', '{', '}', 0xa, 0x0, 'A', 'S', 'D', 'F', 'G', 'H', 'J',
|
|
'K', 'L', ':', 0x22, '~', 0x0, '|', 'Z', 'X', 'C', 'V', 'B',
|
|
'N', 'M', '<', '>', '?', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0,
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '7', '8',
|
|
'9', 0x0, '4', '5', '6', 0x0, '1', '2', '3', '0', 0x7f
|
|
};
|
|
|
|
static int key_ext;
|
|
static int key_lshift = 0, key_rshift = 0, key_caps = 0;
|
|
|
|
static char last_key;
|
|
|
|
static void keyboard_cmd(unsigned char cmd, unsigned char val)
|
|
{
|
|
outb(cmd, 0x60);
|
|
/* wait until keyboard controller accepts cmds: */
|
|
while (inb(0x64) & 2);
|
|
outb(val, 0x60);
|
|
while (inb(0x64) & 2);
|
|
}
|
|
|
|
static void keyboard_controller_cmd(unsigned char cmd, unsigned char val)
|
|
{
|
|
outb(cmd, 0x64);
|
|
/* wait until keyboard controller accepts cmds: */
|
|
while (inb(0x64) & 2);
|
|
outb(val, 0x60);
|
|
while (inb(0x64) & 2);
|
|
}
|
|
|
|
static char keyboard_poll(void)
|
|
{
|
|
unsigned int c;
|
|
if (inb(0x64) & 1) {
|
|
c = inb(0x60);
|
|
switch (c) {
|
|
case 0xe0:
|
|
key_ext = 1;
|
|
return 0;
|
|
case 0x2a:
|
|
key_lshift = 1;
|
|
return 0;
|
|
case 0x36:
|
|
key_rshift = 1;
|
|
return 0;
|
|
case 0xaa:
|
|
key_lshift = 0;
|
|
return 0;
|
|
case 0xb6:
|
|
key_rshift = 0;
|
|
return 0;
|
|
case 0x3a:
|
|
if (key_caps) {
|
|
key_caps = 0;
|
|
keyboard_cmd(0xed, 0);
|
|
} else {
|
|
key_caps = 1;
|
|
keyboard_cmd(0xed, 4); /* set caps led */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (key_ext) {
|
|
// void printk(const char *format, ...);
|
|
printk("extended keycode: %x\n", c);
|
|
|
|
key_ext = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (c & 0x80) /* unhandled key release */
|
|
return 0;
|
|
|
|
if (key_lshift || key_rshift)
|
|
return key_caps ? normal[c] : shifted[c];
|
|
else
|
|
return key_caps ? shifted[c] : normal[c];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int keyboard_dataready(void)
|
|
{
|
|
if (last_key)
|
|
return 1;
|
|
|
|
last_key = keyboard_poll();
|
|
|
|
return (last_key != 0);
|
|
}
|
|
|
|
static unsigned char keyboard_readdata(void)
|
|
{
|
|
char tmp;
|
|
while (!keyboard_dataready());
|
|
tmp = last_key;
|
|
last_key = 0;
|
|
return tmp;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* ******************************************************************
|
|
* common functions, implementing simple concurrent console
|
|
* ****************************************************************** */
|
|
|
|
int putchar(int c)
|
|
{
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
serial_putchar(c);
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_CONSOLE_VGA
|
|
video_putchar(c);
|
|
#endif
|
|
return c;
|
|
}
|
|
|
|
int availchar(void)
|
|
{
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
if (uart_charav(CONFIG_SERIAL_PORT))
|
|
return 1;
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_CONSOLE_VGA
|
|
if (keyboard_dataready())
|
|
return 1;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int getchar(void)
|
|
{
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
if (uart_charav(CONFIG_SERIAL_PORT))
|
|
return (uart_getchar(CONFIG_SERIAL_PORT));
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_CONSOLE_VGA
|
|
if (keyboard_dataready())
|
|
return (keyboard_readdata());
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void cls(void)
|
|
{
|
|
#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
|
|
serial_cls();
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_CONSOLE_VGA
|
|
video_cls();
|
|
#endif
|
|
}
|
|
|
|
#endif // CONFIG_DEBUG_CONSOLE
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
su_read(unsigned long *address)
|
|
{
|
|
char *addr;
|
|
int len;
|
|
|
|
len = POP();
|
|
addr = (char *)POP();
|
|
|
|
if (len != 1)
|
|
printk("su_read: bad len, addr %lx len %x\n", (unsigned long)addr, len);
|
|
|
|
if (uart_charav(*address)) {
|
|
*addr = (char)uart_getchar(*address);
|
|
PUSH(1);
|
|
} else {
|
|
PUSH(0);
|
|
}
|
|
}
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
su_read_keyboard(void)
|
|
{
|
|
unsigned char *addr;
|
|
int len;
|
|
|
|
len = POP();
|
|
addr = (unsigned char *)POP();
|
|
|
|
if (len != 1)
|
|
printk("su_read: bad len, addr %lx len %x\n", (unsigned long)addr, len);
|
|
|
|
if (keyboard_dataready()) {
|
|
*addr = keyboard_readdata();
|
|
PUSH(1);
|
|
} else {
|
|
PUSH(0);
|
|
}
|
|
}
|
|
|
|
/* ( addr len -- actual ) */
|
|
static void
|
|
su_write(unsigned long *address)
|
|
{
|
|
unsigned char *addr;
|
|
int i, len;
|
|
|
|
len = POP();
|
|
addr = (unsigned char *)POP();
|
|
|
|
for (i = 0; i < len; i++) {
|
|
uart_putchar(*address, addr[i]);
|
|
}
|
|
PUSH(len);
|
|
}
|
|
|
|
static void
|
|
su_close(void)
|
|
{
|
|
}
|
|
|
|
static void
|
|
su_open(unsigned long *address)
|
|
{
|
|
int len;
|
|
phandle_t ph;
|
|
unsigned long *prop;
|
|
char *args;
|
|
|
|
fword("my-self");
|
|
fword("ihandle>phandle");
|
|
ph = (phandle_t)POP();
|
|
prop = (unsigned long *)get_property(ph, "address", &len);
|
|
*address = *prop;
|
|
fword("my-args");
|
|
args = pop_fstr_copy();
|
|
|
|
RET ( -1 );
|
|
}
|
|
|
|
DECLARE_UNNAMED_NODE(su, INSTALL_OPEN, sizeof(unsigned long));
|
|
|
|
NODE_METHODS(su) = {
|
|
{ "open", su_open },
|
|
{ "close", su_close },
|
|
{ "read", su_read },
|
|
{ "write", su_write },
|
|
};
|
|
|
|
DECLARE_UNNAMED_NODE(su_keyboard, INSTALL_OPEN, sizeof(unsigned long));
|
|
|
|
NODE_METHODS(su_keyboard) = {
|
|
{ "open", su_open },
|
|
{ "close", su_close },
|
|
{ "read", su_read_keyboard },
|
|
};
|
|
|
|
void
|
|
ob_su_init(uint64_t base, uint64_t offset, int intr)
|
|
{
|
|
push_str("/pci/isa");
|
|
fword("find-device");
|
|
fword("new-device");
|
|
|
|
push_str("su");
|
|
fword("device-name");
|
|
|
|
push_str("serial");
|
|
fword("device-type");
|
|
|
|
PUSH((base + offset) >> 32);
|
|
fword("encode-int");
|
|
PUSH((base + offset) & 0xffffffff);
|
|
fword("encode-int");
|
|
fword("encode+");
|
|
PUSH(SER_SIZE);
|
|
fword("encode-int");
|
|
fword("encode+");
|
|
push_str("reg");
|
|
fword("property");
|
|
|
|
fword("finish-device");
|
|
|
|
REGISTER_NODE_METHODS(su, "/pci/isa/su");
|
|
|
|
push_str("/chosen");
|
|
fword("find-device");
|
|
|
|
push_str("/pci/isa/su");
|
|
fword("open-dev");
|
|
fword("encode-int");
|
|
push_str("stdin");
|
|
fword("property");
|
|
|
|
push_str("/pci/isa/su");
|
|
fword("open-dev");
|
|
fword("encode-int");
|
|
push_str("stdout");
|
|
fword("property");
|
|
|
|
keyboard_controller_cmd(0x60, 0x40); // Write mode command, translated mode
|
|
}
|