diff options
Diffstat (limited to 'util/x86emu/yabel/io.c')
-rw-r--r-- | util/x86emu/yabel/io.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/util/x86emu/yabel/io.c b/util/x86emu/yabel/io.c new file mode 100644 index 0000000000..16a8b58331 --- /dev/null +++ b/util/x86emu/yabel/io.c @@ -0,0 +1,483 @@ +/****************************************************************************** + * 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)); +} |