coreboot-kgpe-d16/util/x86emu/yabel/io.c
Stefan Reinauer 38cd29ebd7 Don't pull in x86emu from a foreign directory anymore. This
produced numerous problems in the past, including the fact that
x86emu doesn't work in v3 anymore even though it lives in the v3
repository.

Since this is a cross-repository move, keeping the history in the v2 tree 
would make life hard for everone. So check the v3 repository for x86emu history
since the merger. The his commit is based on an svn export of r1175 of the
coreboot-v3 repository.

Signed-off-by: Stefan Reinauer <stepan@coresystems.de>
Acked-by: Patrick Georgi <patrick.georgi@coresystems.de>



git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4532 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
2009-08-11 21:28:25 +00:00

483 lines
14 KiB
C

/******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation
* Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
* All rights reserved.
* This program and the accompanying materials
* are made available under the terms of the BSD License
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/bsd-license.php
*
* Contributors:
* IBM Corporation - initial implementation
*****************************************************************************/
#include <types.h>
#ifdef CONFIG_COREBOOT_V2
#include "compat/rtas.h"
#include "compat/time.h"
#else
#include <cpu.h>
#include "rtas.h"
#include <time.h>
#endif
#include "device.h"
#include "debug.h"
#include <x86emu/x86emu.h>
#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL
#include <device/pci.h>
#include <device/pci_ops.h>
#endif
// those are defined in net-snk/oflib/pci.c
extern unsigned int read_io(void *, size_t);
extern int write_io(void *, unsigned int, size_t);
//defined in net-snk/kernel/timer.c
extern u64 get_time(void);
// these are not used, only needed for linking, must be overridden using X86emu_setupPioFuncs
// with the functions and struct below
void
outb(u8 val, u16 port)
{
printf("WARNING: outb not implemented!\n");
HALT_SYS();
}
void
outw(u16 val, u16 port)
{
printf("WARNING: outw not implemented!\n");
HALT_SYS();
}
void
outl(u32 val, u16 port)
{
printf("WARNING: outl not implemented!\n");
HALT_SYS();
}
u8
inb(u16 port)
{
printf("WARNING: inb not implemented!\n");
HALT_SYS();
return 0;
}
u16
inw(u16 port)
{
printf("WARNING: inw not implemented!\n");
HALT_SYS();
return 0;
}
u32
inl(u16 port)
{
printf("WARNING: inl not implemented!\n");
HALT_SYS();
return 0;
}
u32 pci_cfg_read(X86EMU_pioAddr addr, u8 size);
void pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size);
u8 handle_port_61h(void);
u8
my_inb(X86EMU_pioAddr addr)
{
u8 rval = 0xFF;
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
addr);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
rval = read_io((void *)translated_addr, 1);
DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __func__,
addr, rval);
return rval;
} else {
switch (addr) {
case 0x61:
//8254 KB Controller / Timer Port
rval = handle_port_61h();
//DEBUG_PRINTF_IO("%s(%04x) KB / Timer Port B --> %02x\n", __func__, addr, rval);
return rval;
break;
case 0xCFC:
case 0xCFD:
case 0xCFE:
case 0xCFF:
// PCI Config Mechanism 1 Ports
return (u8) pci_cfg_read(addr, 1);
break;
case 0x0a:
CHECK_DBG(DEBUG_INTR) {
X86EMU_trace_on();
}
M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
//HALT_SYS();
// no break, intentional fall-through to default!!
default:
DEBUG_PRINTF_IO
("%s(%04x) reading from bios_device.io_buffer\n",
__func__, addr);
rval = *((u8 *) (bios_device.io_buffer + addr));
DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n",
__func__, addr, rval);
return rval;
break;
}
}
}
u16
my_inw(X86EMU_pioAddr addr)
{
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
addr);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
u16 rval;
if ((translated_addr & (u64) 0x1) == 0) {
// 16 bit aligned access...
u16 tempval = read_io((void *)translated_addr, 2);
//little endian conversion
rval = in16le((void *) &tempval);
} else {
// unaligned access, read single bytes, little-endian
rval = (read_io((void *)translated_addr, 1) << 8)
| (read_io((void *)(translated_addr + 1), 1));
}
DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %04x\n", __func__,
addr, rval);
return rval;
} else {
switch (addr) {
case 0xCFC:
case 0xCFE:
//PCI Config Mechanism 1
return (u16) pci_cfg_read(addr, 2);
break;
default:
DEBUG_PRINTF_IO
("%s(%04x) reading from bios_device.io_buffer\n",
__func__, addr);
u16 rval =
in16le((void *) bios_device.io_buffer + addr);
DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n",
__func__, addr, rval);
return rval;
break;
}
}
}
u32
my_inl(X86EMU_pioAddr addr)
{
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __func__,
addr);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
u32 rval;
if ((translated_addr & (u64) 0x3) == 0) {
// 32 bit aligned access...
u32 tempval = read_io((void *) translated_addr, 4);
//little endian conversion
rval = in32le((void *) &tempval);
} else {
// unaligned access, read single bytes, little-endian
rval = (read_io((void *)(translated_addr), 1) << 24)
| (read_io((void *)(translated_addr + 1), 1) << 16)
| (read_io((void *)(translated_addr + 2), 1) << 8)
| (read_io((void *)(translated_addr + 3), 1));
}
DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %08x\n", __func__,
addr, rval);
return rval;
} else {
switch (addr) {
case 0xCFC:
//PCI Config Mechanism 1
return pci_cfg_read(addr, 4);
break;
default:
DEBUG_PRINTF_IO
("%s(%04x) reading from bios_device.io_buffer\n",
__func__, addr);
u32 rval =
in32le((void *) bios_device.io_buffer + addr);
DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n",
__func__, addr, rval);
return rval;
break;
}
}
}
void
my_outb(X86EMU_pioAddr addr, u8 val)
{
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
__func__, addr, val);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
write_io((void *) translated_addr, val, 1);
DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __func__,
addr, val);
} else {
switch (addr) {
case 0xCFC:
case 0xCFD:
case 0xCFE:
case 0xCFF:
// PCI Config Mechanism 1 Ports
pci_cfg_write(addr, val, 1);
break;
default:
DEBUG_PRINTF_IO
("%s(%04x,%02x) writing to bios_device.io_buffer\n",
__func__, addr, val);
*((u8 *) (bios_device.io_buffer + addr)) = val;
break;
}
}
}
void
my_outw(X86EMU_pioAddr addr, u16 val)
{
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
__func__, addr, val);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
if ((translated_addr & (u64) 0x1) == 0) {
// little-endian conversion
u16 tempval = in16le((void *) &val);
// 16 bit aligned access...
write_io((void *) translated_addr, tempval, 2);
} else {
// unaligned access, write single bytes, little-endian
write_io(((void *) (translated_addr + 1)),
(u8) ((val & 0xFF00) >> 8), 1);
write_io(((void *) translated_addr),
(u8) (val & 0x00FF), 1);
}
DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __func__,
addr, val);
} else {
switch (addr) {
case 0xCFC:
case 0xCFE:
// PCI Config Mechanism 1 Ports
pci_cfg_write(addr, val, 2);
break;
default:
DEBUG_PRINTF_IO
("%s(%04x,%04x) writing to bios_device.io_buffer\n",
__func__, addr, val);
out16le((void *) bios_device.io_buffer + addr, val);
break;
}
}
}
void
my_outl(X86EMU_pioAddr addr, u32 val)
{
unsigned long translated_addr = addr;
u8 translated = biosemu_dev_translate_address(&translated_addr);
if (translated != 0) {
//translation successfull, access Device I/O (BAR or Legacy...)
DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n",
__func__, addr, val);
//DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __func__, addr, translated_addr);
if ((translated_addr & (u64) 0x3) == 0) {
// little-endian conversion
u32 tempval = in32le((void *) &val);
// 32 bit aligned access...
write_io((void *) translated_addr, tempval, 4);
} else {
// unaligned access, write single bytes, little-endian
write_io(((void *) translated_addr + 3),
(u8) ((val & 0xFF000000) >> 24), 1);
write_io(((void *) translated_addr + 2),
(u8) ((val & 0x00FF0000) >> 16), 1);
write_io(((void *) translated_addr + 1),
(u8) ((val & 0x0000FF00) >> 8), 1);
write_io(((void *) translated_addr),
(u8) (val & 0x000000FF), 1);
}
DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __func__,
addr, val);
} else {
switch (addr) {
case 0xCFC:
// PCI Config Mechanism 1 Ports
pci_cfg_write(addr, val, 4);
break;
default:
DEBUG_PRINTF_IO
("%s(%04x,%08x) writing to bios_device.io_buffer\n",
__func__, addr, val);
out32le((void *) bios_device.io_buffer + addr, val);
break;
}
}
}
u32
pci_cfg_read(X86EMU_pioAddr addr, u8 size)
{
u32 rval = 0xFFFFFFFF;
struct device * dev;
if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
// PCI Configuration Mechanism 1 step 1
// write to 0xCF8, sets bus, device, function and Config Space offset
// later read from 0xCFC-0xCFF returns the value...
u8 bus, devfn, offs;
u32 port_cf8_val = my_inl(0xCF8);
if ((port_cf8_val & 0x80000000) != 0) {
//highest bit enables config space mapping
bus = (port_cf8_val & 0x00FF0000) >> 16;
devfn = (port_cf8_val & 0x0000FF00) >> 8;
offs = (port_cf8_val & 0x000000FF);
offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
DEBUG_PRINTF_INTR("%s(): PCI Config Read from device: bus: %02x, devfn: %02x, offset: %02x\n",
__func__, bus, devfn, offs);
#if defined(CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES) && CONFIG_YABEL_PCI_ACCESS_OTHER_DEVICES==1
dev = dev_find_slot(bus, devfn);
DEBUG_PRINTF_INTR("%s(): dev_find_slot() returned: %s\n",
__func__, dev_path(dev));
if (dev == 0) {
// fail accesses to non-existent devices...
#else
dev = bios_device.dev;
if ((bus != bios_device.bus)
|| (devfn != bios_device.devfn)) {
// fail accesses to any device but ours...
#endif
printf
("%s(): Config read access invalid device! bus: %02x (%02x), devfn: %02x (%02x), offs: %02x\n",
__func__, bus, bios_device.bus, devfn,
bios_device.devfn, offs);
SET_FLAG(F_CF);
HALT_SYS();
return 0;
} else {
#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL
switch (size) {
case 1:
rval = pci_read_config8(dev, offs);
break;
case 2:
rval = pci_read_config16(dev, offs);
break;
case 4:
rval = pci_read_config32(dev, offs);
break;
}
#else
rval =
(u32) rtas_pci_config_read(bios_device.
puid, size,
bus, devfn,
offs);
#endif
DEBUG_PRINTF_IO
("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n",
__func__, addr, offs, size, rval);
}
}
}
return rval;
}
void
pci_cfg_write(X86EMU_pioAddr addr, u32 val, u8 size)
{
if ((addr >= 0xCFC) && ((addr + size) <= 0xD00)) {
// PCI Configuration Mechanism 1 step 1
// write to 0xCF8, sets bus, device, function and Config Space offset
// later write to 0xCFC-0xCFF sets the value...
u8 bus, devfn, offs;
u32 port_cf8_val = my_inl(0xCF8);
if ((port_cf8_val & 0x80000000) != 0) {
//highest bit enables config space mapping
bus = (port_cf8_val & 0x00FF0000) >> 16;
devfn = (port_cf8_val & 0x0000FF00) >> 8;
offs = (port_cf8_val & 0x000000FF);
offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly
if ((bus != bios_device.bus)
|| (devfn != bios_device.devfn)) {
// fail accesses to any device but ours...
printf
("Config write access invalid! PCI device %x:%x.%x, offs: %x\n",
bus, devfn >> 3, devfn & 7, offs);
HALT_SYS();
} else {
#ifdef CONFIG_PCI_OPTION_ROM_RUN_YABEL
switch (size) {
case 1:
pci_write_config8(bios_device.dev, offs, val);
break;
case 2:
pci_write_config16(bios_device.dev, offs, val);
break;
case 4:
pci_write_config32(bios_device.dev, offs, val);
break;
}
#else
rtas_pci_config_write(bios_device.puid,
size, bus, devfn, offs,
val);
#endif
DEBUG_PRINTF_IO
("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n",
__func__, addr, offs, size, val);
}
}
}
}
u8
handle_port_61h(void)
{
static u64 last_time = 0;
u64 curr_time = get_time();
u64 time_diff; // time since last call
u32 period_ticks; // length of a period in ticks
u32 nr_periods; //number of periods passed since last call
// bit 4 should toggle with every (DRAM) refresh cycle... (66kHz??)
time_diff = curr_time - last_time;
// at 66kHz a period is ~ 15 ns long, converted to ticks: (tb_freq is ticks/second)
// TODO: as long as the frequency does not change, we should not calculate this every time
period_ticks = (15 * tb_freq) / 1000000;
nr_periods = time_diff / period_ticks;
// if the number if ticks passed since last call is odd, we toggle bit 4
if ((nr_periods % 2) != 0) {
*((u8 *) (bios_device.io_buffer + 0x61)) ^= 0x10;
}
//finally read the value from the io_buffer
return *((u8 *) (bios_device.io_buffer + 0x61));
}