Kyösti Mälkki (kyosti.malkki@gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/860
-gerrit
commit a50001d222b62ba64971184b84e8fda6b94dfae7 Author: Kyösti Mälkki kyosti.malkki@gmail.com Date: Mon Apr 23 15:53:12 2012 +0300
Modularized SerialICE scripting
This is script/serialice.lua split into separate files. It expects to find a match for the *mb command response and load a correct .lua file for that particular mainboard.
There is a bit improved filter for PCI configuration registers, even when written in multiple parts. Good for catching BARs. There is some decoding of CMOS nvram and PnP cycles to SuperIO.
Some filters for which I did not yet find proper location are in the file leftover.lua, some of these were vendor bios binary specific.
As for the name simba/, it stands for simulated bus access. Plan is to modify these further and divert some SMBus traffic to qemu and not the real hw. One could then read SPD data from a local file.
Change-Id: Idba1c3dc7e80ebf169ff277ab7918a1564822bfe Signed-off-by: Kyösti Mälkki kyosti.malkki@gmail.com --- SerialICE/simba/aopen_dxpl_plus.lua | 96 ++++++++++++ SerialICE/simba/core_io.lua | 283 +++++++++++++++++++++++++++++++++++ SerialICE/simba/cpu.lua | 94 ++++++++++++ SerialICE/simba/i82801dx.lua | 109 ++++++++++++++ SerialICE/simba/leftover.lua | 135 +++++++++++++++++ SerialICE/simba/memory.lua | 239 +++++++++++++++++++++++++++++ SerialICE/simba/misc.lua | 198 ++++++++++++++++++++++++ SerialICE/simba/serialice.lua | 158 +++++++++++++++++++ SerialICE/simba/superio.lua | 235 +++++++++++++++++++++++++++++ 9 files changed, 1547 insertions(+), 0 deletions(-)
diff --git a/SerialICE/simba/aopen_dxpl_plus.lua b/SerialICE/simba/aopen_dxpl_plus.lua new file mode 100644 index 0000000..e215e32 --- /dev/null +++ b/SerialICE/simba/aopen_dxpl_plus.lua @@ -0,0 +1,96 @@ + + +dofile("i82801dx.lua") + +-- ********************************************************** +-- + +function mainboard_io_read(port, size, data, filter) + -- KBD controller returns status=0xff, clear 0x02 + -- as I cannot modify data, fake timeout errors + if ( port == 0x64 and size == 1 ) then + filter.filter = true + filter.data = 0x60 + return true + end + + -- Timer loop + if ( port == 0x61 ) then + -- if ( regs.cs == 0x1000 and regs.eip == 0x1634 ) then + if ( regs.eip == 0x1634 ) then + printf("Skipping delay at %04x:%04x\n", regs.cs, regs.eip); + regs.ecx = 0x01 + return true, 0x20 + end + -- if ( regs.cs == 0x1000 and regs.eip == 0x163a ) then + if ( regs.eip == 0x163a ) then + printf("Skipping delay at %04x:%04x\n", regs.cs, regs.eip); + regs.ecx = 0x01 + return true, 0x30 + end + end + +end + + +function mainboard_io_write(port, size, data, filter) + + -- ********************************************************** + -- Not catching the end of RAM init is not problematic, except + -- that it makes decompression of the BIOS core to RAM incredibly + -- slow as the decompressor inner loop has to be fetched + -- permanently from the target (several reads/writes per + -- decompressed byte). + + if port == 0x80 and data == 0x2c and ram_is_initialized == false then + ram_is_initialized = true + -- Register low RAM 0x00000000 - 0x000dffff + SerialICE_register_physical(0x00000000, 0xa0000) + -- SMI/VGA memory should go to the target... + SerialICE_register_physical(0x000c0000, 0x20000) + printf("\nLow RAM accesses are now directed to Qemu.\n") + + return false, data + end + + if ( port == 0xed ) then + -- if ( regs.cs == 0x1000 and regs.eip == 0x1792 and regs.ecx == 0x0000fff0 ) then + if ( regs.eip == 0x1792 ) then + printf("Skipping IO delay... #2 \n") + regs.ecx = 0x01 + end + +if false then + -- SIPI delay + -- if ( regs.cs == 0xe000 and regs.eip == 0xb3bc and regs.ecx == 0x200) then + if ( regs.eip == 0xb3bc or regs.eip == 0xb3bf ) then + printf("Skipping IO delay... IPI #0\n") + regs.ecx = 0x01 + end + -- if ( regs.cs == 0xe000 and regs.eip == 0xb4af and regs.ecx == 0x200) then + if ( regs.eip == 0xb4ad or regs.eip == 0xb4af ) then + printf("Skipping IO delay... IPI #1\n") + regs.ecx = 0x01 + end +end + + return false, data + end + +end + +function mainboard_io_write_log(port, size, data, target) + if port == 0xed then + return true -- Ignore + end + if port == 0xcfb then + return true -- Ignore + end +end + + +prepend_to_list(io_read_hooks, mainboard_io_read) +prepend_to_list(io_write_hooks, mainboard_io_write) +prepend_to_list(io_write_log_hooks, mainboard_io_write_log) + + diff --git a/SerialICE/simba/core_io.lua b/SerialICE/simba/core_io.lua new file mode 100644 index 0000000..128b132 --- /dev/null +++ b/SerialICE/simba/core_io.lua @@ -0,0 +1,283 @@ + +-- ********************************************************** +-- +-- IO access + + +io_read_hooks = new_list() +io_write_hooks = new_list() +io_read_log_hooks = new_list() +io_write_log_hooks = new_list() + + +-- SerialICE_io_read_filter is the filter function for IO reads. +-- +-- Parameters: +-- port IO port to be read +-- data_size Size of the IO read +-- Return values: +-- caught True if the filter intercepted the read +-- data Value returned if the read was intercepted + +function SerialICE_io_read_filter(port, size) + local data = 0 + filter = { filter = false, data = data } + if walk_list(io_read_hooks, port, size, data, filter) then + return filter.filter, filter.data + end + + return false, data +end + +-- SerialICE_io_write_filter is the filter function for IO writes. +-- +-- Parameters: +-- port IO port to be written to +-- size Size of the IO write +-- data Data to be written to +-- Return values: +-- caught True if the filter intercepted the write +-- data Value returned if the write was *not* intercepted + +function SerialICE_io_write_filter(port, size, data) + filter = { filter = false, data = data } + if walk_list(io_write_hooks, port, size, data, filter) then + return filter.filter, filter.data + end + + return false, data +end + + +function SerialICE_io_write_log(port, size, data, target) + log_cs_ip() + if walk_list(io_write_log_hooks, port, size, data, target) then + return + end + + printf("IO: out%s %04x <= %s\n", size_suffix(size), port, size_data(size, data)) +end + +function SerialICE_io_read_log(port, size, data, target) + log_cs_ip() + if walk_list(io_read_log_hooks, port, size, data, target) then + return + end + + printf("IO: in%s %04x => %s\n", size_suffix(size), port, size_data(size, data)) +end + + + +-- ********************************************************** +-- +-- PCI IO config access + + +pci_cfg_hooks = new_list() + +function add_pci_cfg_hook(bdf, size, func) + value = { bdf = bdf, size = size, func = func } + prepend_to_list(pci_cfg_hooks, value) +end + +function is_pci_cfg_hooked(bdf) + local l = pci_cfg_hooks.list + while l do + if bdf == bit.band(l.value.bdf, bit.bnot(0x03)) then + return true + end + l = l.next + end + return false +end + +function call_pci_cfg_hook(bdf, size, data) + local l = pci_cfg_hooks.list + while l do + if l.value.bdf == bdf and l.value.size == size then + l.value.func(bdf, size, data) + end + l = l.next + end + return false +end + +function pci_bdf(bus, dev, func, reg) + return 0x80000000 + bus*65536 + dev*2048 + func*256 + reg +end + +-- Remember the PCI device selected via IO CF8 +SerialICE_pci_device = 0 + + +-- Catch partial PCI configuration register writes. +-- This synthesizes 32/16 bit wide access from separate +-- 16/8 bit accesses for pci_cfg_hooks. + +port_0cf8 = 0 +port_0cfc = 0 +bv = {} + +function port_0cf8_reset(data) + local port_0cf8_new = bit.band(data,bit.bnot(0x3)) + if not (port_0cf8 == port_0cf8_new) then + port_0cf8 = 0 + if (is_pci_cfg_hooked(port_0cf8_new)) then + port_0cf8 = port_0cf8_new + end + for i = 0, 3, 1 do bv[i] = false end + end +end + +function port_0cfc_access(port, size, data) + + local av = {} + + for i = 0, 3, 1 do av[i] = false end + + ll = 8 * (port%4) + if (size == 1) then + av[port%4] = true + bv[port%4] = true + amask = bit.lshift(0xff, ll) + omask = bit.lshift(data, ll) + port_0cfc = bit.band(port_0cfc, bit.bnot(amask)) + port_0cfc = bit.bor(port_0cfc, omask) + elseif (size == 2) then + av[port%4] = true + bv[port%4] = true + av[port%4+1] = true + bv[port%4+1] = true + amask = bit.lshift(0xffff, ll) + omask = bit.lshift(data, ll) + port_0cfc = bit.band(port_0cfc, bit.bnot(amask)) + port_0cfc = bit.bor(port_0cfc, omask) + elseif (size == 4) then + port_0cfc = data + for i = 0, 3, 1 do av[i] = true end + for i = 0, 3, 1 do bv[i] = true end + end + + for i = 0, 3, 1 do + if (bv[i] and av[i]) then + call_pci_cfg_hook(port_0cf8 + i, 1, + bit.band(0xff, bit.rshift(port_0cfc, i*8))) + end + end + if ((bv[0] and bv[1]) and (av[0] or av[1])) then + call_pci_cfg_hook(port_0cf8 + 0x00, 2, + bit.band(0xffff, bit.rshift(port_0cfc, 0))) + end + if (bv[2] and bv[3] and (av[2] or av[3])) then + call_pci_cfg_hook(port_0cf8 + 0x02, 2, + bit.band(0xffff, bit.rshift(port_0cfc, 16))) + end + if (bv[0] and bv[1] and bv[2] and bv[3]) then + call_pci_cfg_hook(port_0cf8, 4, port_0cfc) + end +end + + +function pci_io_cfg_write(port, size, data, filter) + if port == 0xcf8 then + port_0cf8_reset(data) + SerialICE_pci_device = data + return false + end + if port_0cf8 ~= 0 and port >= 0xcfc and port <= 0xcff then + port_0cfc_access(port, size, data) + end + return false +end + +-- not enabled +function pci_io_cfg_read(port, size, data, filter) + if port_0cf8 ~= 0 and port >= 0xcfc and port <= 0xcff then + port_0cfc_access(port, size, data) + end + return false +end + +function pci_io_cfg_write_log(port, size, data, target) + if port == 0xcf8 then + return not log_pci_io_cfg + end + if port >= 0xcfc and port <= 0xcff then + printf("PCI %x:%02x.%x R.%02x <= %s\n", + bit.band(0xff,bit.rshift(SerialICE_pci_device, 16)), + bit.band(0x1f,bit.rshift(SerialICE_pci_device, 11)), + bit.band(0x7,bit.rshift(SerialICE_pci_device, 8)), + bit.band(0xff,SerialICE_pci_device + (port - 0xcfc)), + size_data(size, data)) + return not log_pci_io_cfg + end +end + +function pci_io_cfg_read_log(port, size, data, target) + if port == 0xcf8 then + return not log_pci_io_cfg + end + if port >= 0xcfc and port <= 0xcff then + printf("PCI %x:%02x.%x R.%02x => %s\n", + bit.band(0xff,bit.rshift(SerialICE_pci_device, 16)), + bit.band(0x1f,bit.rshift(SerialICE_pci_device, 11)), + bit.band(0x7,bit.rshift(SerialICE_pci_device, 8)), + bit.band(0xff,SerialICE_pci_device + (port - 0xcfc)), + size_data(size, data)) + return not log_pci_io_cfg + end +end + +if decode_pci_io_cfg then + prepend_to_list(io_write_hooks, pci_io_cfg_write) +-- prepend_to_list(io_read_hooks, pci_io_cfg_read) + prepend_to_list(io_write_log_hooks, pci_io_cfg_write_log) + prepend_to_list(io_read_log_hooks, pci_io_cfg_read_log) +end + +-- ********************************************************** +-- +-- PCIe MM config access + +PCIe_bar = 0 +PCIe_size = 0 + +function pci_mm_cfg_read_log(addr, size, data, target) + if addr >= PCIe_bar and addr < (PCIe_bar + PCIe_size) then + printf("PCIe %x:%02x.%x R.%02x => %s\n", + bit.band(0xff,bit.rshift(addr, 20)), + bit.band(0x1f,bit.rshift(addr, 15)), + bit.band(0x7,bit.rshift(addr, 12)), + bit.band(0xfff,addr), + size_data(size, data)) + return not log_pci_mm_cfg + end +end + +function pci_mm_cfg_write_log(addr, size, data, target) + if addr >= PCIe_bar and addr < (PCIe_bar + PCIe_size) then + printf("PCIe %x:%02x.%x R.%02x <= %s\n", + bit.band(0xff,bit.rshift(addr, 20)), + bit.band(0x1f,bit.rshift(addr, 15)), + bit.band(0x7,bit.rshift(addr, 12)), + bit.band(0xfff,addr), + size_data(size, data)) + return not log_pci_mm_cfg + end +end + +function pcie_mm_cfg_bar(base, size) + + PCIe_bar = base + PCIe_size = size + printf("PCIe MM config BAR: 0x%08x\n", PCIe_bar) + + if decode_pci_mm_cfg then + --prepend_to_list(mem_write_hooks, pci_mm_cfg_write) + --prepend_to_list(mem_read_hooks, pci_mm_cfg_read) + prepend_to_list(mem_write_log_hooks, pci_mm_cfg_write_log) + prepend_to_list(mem_read_log_hooks, pci_mm_cfg_read_log) + end +end + diff --git a/SerialICE/simba/cpu.lua b/SerialICE/simba/cpu.lua new file mode 100644 index 0000000..760fb16 --- /dev/null +++ b/SerialICE/simba/cpu.lua @@ -0,0 +1,94 @@ + + +msr_read_log_hooks = new_list() +msr_write_log_hooks = new_list() + + +function var_mtrr_log_write(addr, hi, lo, filtered) + if addr >= 0x200 and addr < 0x210 then + if addr % 2 == 0 then + mt = lo % 0x100 + if mt == 0 then memtype = "Uncacheable" + elseif mt == 1 then memtype = "Write-Combine" + elseif mt == 4 then memtype = "Write-Through" + elseif mt == 5 then memtype = "Write-Protect" + elseif mt == 6 then memtype = "Write-Back" + else memtype = "Unknown" + end + printf("CPU: Set MTRR %x base to %08x.%08x (%s)\n", (addr - 0x200) / 2, hi, bit.band(lo, 0xffffff00), memtype) + else + if bit.band(lo, 0x800) == 0x800 then + valid = "valid" + else + valid = "disabled" + end + printf("CPU: Set MTRR %x mask to %08x.%08x (%s)\n", (addr - 0x200) / 2, hi, bit.band(lo, 0xfffff000), valid) + end + return true + end + return false +end + + + +function SerialICE_msr_read_filter(addr, hi, lo) + -- Intel CPU microcode revision check. + if addr == 0x8b then + -- fake microcode revision of my 0x6f6 Core 2 Duo Mobile + return true, 0xc7, 0x00 + end + + return false, hi, lo +end + +function SerialICE_msr_write_filter(addr, hi, lo) + -- Intel CPU microcode update + if addr == 0x79 then + return true, 0, 0xffff0000 + end + + return false, hi, lo +end + +function SerialICE_msr_write_log(addr, hi, lo, filtered) + log_cs_ip() + if not walk_list(msr_write_log_hooks, addr, hi, lo, filtered) then + printf("CPU: wrmsr %08x <= %08x.%08x\n", addr, hi, lo) + end +end + +function SerialICE_msr_read_log(addr, hi, lo, filtered) + log_cs_ip() + if not walk_list(msr_read_log_hooks, addr, hi, lo, filtered) then + printf("CPU: rdmsr %08x => %08x.%08x\n", addr, hi, lo) + end +end + + +prepend_to_list(msr_write_log_hooks, var_mtrr_log_write) + + +-- ********************************************************** +-- +-- CPUID instruction + +function SerialICE_cpuid_filter(in_eax, in_ecx, eax, ebx, ecx, edx) + -- Set number of cores to 1 on Core Duo and Atom to trick the + -- firmware into not trying to wake up non-BSP nodes. + if in_eax == 1 then + ebx = bit.band(0xff00ffff, ebx); + ebx = bit.bor(0x00010000, ebx); + return true, eax, ebx, ecx, edx + end + + -- return false, so the result is not filtered. + return false, eax, ebx, ecx, edx +end + + +function SerialICE_cpuid_log(in_eax, in_ecx, out_eax, out_ebx, out_ecx, out_edx, filtered) + log_cs_ip() + printf("CPU: CPUID eax: %08x; ecx: %08x => %08x.%08x.%08x.%08x\n", + in_eax, in_ecx, out_eax, out_ebx, out_ecx, out_edx) +end + diff --git a/SerialICE/simba/i82801dx.lua b/SerialICE/simba/i82801dx.lua new file mode 100644 index 0000000..ec19c79 --- /dev/null +++ b/SerialICE/simba/i82801dx.lua @@ -0,0 +1,109 @@ +-- ********************************************************** +-- +-- LPC decode ranges + +function lpc_decode_write(port, size, data, filter) + -- LPC decode registers + if SerialICE_pci_device == pci_bdf(0x0,0x1f,0x0,0xe4) then + if (port == 0xcfc and size == 4) or (port == 0xcfe) then + printf("LPC decode register 0:1f.0 R.e6 (filtered)\n") + filter.filter = true + filter.data = data + return true + end + end +end + +function lpc_decode_hook(bdfr, size, data) + printf("got LPC %08x %d %08x\n", bdfr, size, data); +end + +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x0,0xe4), 4, lpc_decode_hook) +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x0,0xe6), 2, lpc_decode_hook) + + +-- ********************************************************** +-- +-- SMBus controller handling + +smbus_host_base = 0 +smbus_host_size = 0 + +function smbus_io_write_log(port, size, data, target) + if port >= smbus_host_base and port < smbus_host_base+smbus_host_size then + return not log_smbus_io + end +end + +function smbus_io_read_log(port, size, data, target) + if port >= smbus_host_base and port < smbus_host_base+smbus_host_size then + return not log_smbus_io + end +end + + +function smbus_bar_setup(base, size) + + smbus_host_base = base + smbus_host_size = size + + printf("SMBus BAR set up: 0x%08x\n", smbus_host_base) + if decode_smbus then + prepend_to_list(io_write_log_hooks, smbus_io_write_log) + prepend_to_list(io_read_log_hooks, smbus_io_read_log) + end +end + +function smbus_bar_hook(bdfr, size, data) + smbus_bar_setup(bit.band(data, 0x10000-0x20), 0x20) +end + +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x3,0x20), 2, smbus_bar_hook) + + +-- ********************************************************** +-- + +function acpi_bar_hook(bdfr, size, data) + printf("ACPI BAR set up: 0x%08x\n", data) + printf("TCO BAR set up: 0x%08x\n", data + 0x60) + --acpi_bar_setup(bit.band(data, 0x10000-0x80), 0x80) +end + + +function gpio_bar_hook(bdfr, size, data) + printf("GPIO BAR set up: 0x%08x\n", data) + --gpio_bar_setup(bit.band(data, 0x10000-0x40), 0x40) +end + +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x0,0x40), 2, acpi_bar_hook) +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x0,0x58), 2, gpio_bar_hook) + + +-- ********************************************************** +-- +-- AC '97 controller handling + +function audio_nambar_hook(bdfr, size, data) + printf("AUDIO NAMBAR set up: 0x%08x\n", data) +end +function audio_nabmbar_hook(bdfr, size, data) + printf("AUDIO NABMBAR set up: 0x%08x\n", data) +end +function audio_mmbar_hook(bdfr, size, data) + printf("AUDIO MMBAR set up: 0x%08x\n", data) +end +function audio_mbbar_hook(bdfr, size, data) + printf("AUDIO MBBAR set up: 0x%08x\n", data) +end + +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x5,0x10), 2, audio_nambar_hook) +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x5,0x14), 2, audio_nabmbar_hook) +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x5,0x18), 4, audio_mmbar_hook) +add_pci_cfg_hook(pci_bdf(0x0,0x1f,0x5,0x1c), 4, audio_mbbar_hook) + + + + + + diff --git a/SerialICE/simba/leftover.lua b/SerialICE/simba/leftover.lua new file mode 100644 index 0000000..4624ffe --- /dev/null +++ b/SerialICE/simba/leftover.lua @@ -0,0 +1,135 @@ + + + -- ********************************************************** + -- + -- Dell 1850 BMC filter + + if port == 0xe8 then + -- lua lacks a switch statement + if data == 0x44656c6c then printf("BMC: Dell\n") + elseif data == 0x50726f74 then printf("BMC: Prot\n") + elseif data == 0x496e6974 then + printf("BMC: Init (filtered)\n") + return true, data + else + printf("BMC: unknown %08x\n", data) + end + return false, data + end + + -- ********************************************************** + -- + -- Phoenix BIOS reconfigures 0:1f.0 reg 0x80/0x82. + -- This effectively wipes out our communication channel + -- so we mut not allow it. + + if port == 0xcfc then + if SerialICE_pci_device == 0x8000f880 then + printf("LPC (filtered)\n") + return true, data + end + + return false, data + end + + + -- ********************************************************** + -- + -- Intel 82945 (reference BIOS) RAM switch + -- + + -- The RAM initialization code for the i945 used by AMI and + -- Phoenix uses the same POST codes. We use this to determine + -- when RAM init is done on that chipset. + + + if port == 0x80 and data == 0xff37 and ram_is_initialized == false then + ram_is_initialized = true + -- Register low RAM 0x00000000 - 0x000dffff + SerialICE_register_physical(0x00000000, 0xa0000) + -- SMI/VGA memory should go to the target... + SerialICE_register_physical(0x000c0000, 0x20000) + printf("\nLow RAM accesses are now directed to Qemu.\n") + + return false, data + end + + + -- ********************************************************** + -- + -- unknown io_write delay hooks + -- + + if ( port == 0xed and data == 0x40 ) then + if ( regs.eip == 0x3ed and regs.ecx == 0x00000290 ) then + printf("Skipping IO delay...\n") + -- f100:03ed + regs.ecx = 0x05 + end + end + + if ( port == 0xed and data == 0x83 ) + then + if ( regs.eip == 0x1bb and regs.ecx == 0x0000fff0 ) then + printf("Skipping IO delay...\n") + -- e002:01bb + regs.ecx = 0x10 + regs.ebx = 0x01 + end + end + + + + + -- ********************************************************** + -- + -- io_read hooks, unknown vendor + + -- if port == 0x42 then + -- printf("WARNING: Hijacking timer port 0x42\n") + -- data = 0x80 + -- caught = true + -- end + + -- + -- + + if ( port == 0x60 and data_size == 1 ) then + if ( regs.eip == 0xbd6d and regs.eax == 0x8aa and regs.ecx == 0x00fffff0 ) then + -- f000:bd6d + printf("Skipping keyboard timeout...\n") + regs.eax = 0x01aa + regs.ecx = 0x0010 + end + end + + +-- ********************************************************** +-- Intel 82945 PCIe BAR + +function pcie_bar_hook(bdfr, size, data) + -- size is hard coded 64k for now. + pcie_mm_cfg_bar(bit.band(0xfc000000,data) % 0x100000000, 64 * 1024) +end + +if northbridge == "intel-i945" then + add_pci_cfg_hook(pci_bdf(0,0,0,0x48), 4, pcie_bar_hook) +end + +-- ********************************************************** +-- +-- Vendor specific Cache-As-Ram regions + +printf("SerialICE: Registering physical memory areas for Cache-As-Ram:\n") + +-- Register Phoenix BIOS Cache as RAM area as normal RAM +-- 0xffd80000 - 0xffdfffff +new_car_region(0xffd80000, 0x80000) + +-- Register AMI BIOS Cache as RAM area as normal RAM +-- 0xffbc0000 - 0xffbfffff +new_car_region(0xffbc0000, 0x40000) + +-- current Phoenix BIOS +new_car_region(0xde000, 0x2000) + diff --git a/SerialICE/simba/memory.lua b/SerialICE/simba/memory.lua new file mode 100644 index 0000000..3e13838 --- /dev/null +++ b/SerialICE/simba/memory.lua @@ -0,0 +1,239 @@ + + +mem_read_log_hooks = new_list() +mem_write_log_hooks = new_list() + + +car_regions = { list = nil } + +function new_car_region(start, size) + car_regions.list = { next = car_regions.list, start = start, size = size } + SerialICE_register_physical(start, size) +end + +function is_car(addr) + if car_regions.list == nil then + return false + end + local l = car_regions.list + while l do + if addr >= l.start and addr < l.start + l.size then + return true + end + l = l.next + end + return false +end + + +-- SerialICE_memory_read_filter is the filter function for memory reads +-- +-- Parameters: +-- addr memory address to be read +-- size Size of the memory read +-- Return values: +-- to_hw True if the read should be directed to the target +-- to_qemu True if the read should be directed to Qemu +-- result Read result if both to_hw and to_qemu are false + +function SerialICE_memory_read_filter(addr, size) + + -- Example: catch memory read and return a value + -- defined in this script: + -- + -- if addr == 0xfec14004 and size == 4 then + -- return false, false, 0x23232323 + -- end + + -- Cache-As-RAM is exclusively + -- handled by Qemu (RAM backed) + if is_car(addr) then + return false, true, 0 + end + + if addr >= rom_base and addr <= 0xffffffff then + -- ROM accesses go to Qemu only + return false, true, 0 + elseif addr >= PCIe_bar and addr <= (PCIe_bar + PCIe_size) then + -- PCIe MMIO config space accesses are + -- exclusively handled by the SerialICE + -- target + return true, false, 0 + elseif addr >= 0xfed10000 and addr <= 0xfed1ffff then + -- Intel chipset BARs are exclusively + -- handled by the SerialICE target + return true, false, 0 + elseif addr >= 0xfee00000 and addr <= 0xfeefffff then + -- Local APIC.. Hm, not sure what to do here. + -- We should avoid that someone wakes up cores + -- on the target system that go wild. + return true, false, 0 -- XXX Handled by target + elseif addr >= 0xfec00000 and addr <= 0xfecfffff then + -- IO APIC.. Hm, not sure what to do here. + return true, false, 0 -- XXX Handled by target + elseif addr >= 0xfed40000 and addr <= 0xfed45000 then + -- ICH7 TPM + -- Phoenix "Secure" Core bails out if we don't pass this on ;-) + return true, false, 0 + elseif addr >= 0x000e0000 and addr <= 0x000fffff then + -- Low ROM accesses go to Qemu memory + return false, true, 0 + elseif addr >= 0x000a0000 and addr <= 0x000affff then + -- SMI/VGA go to target + return true, false, 0 + elseif addr >= 0x00000000 and addr <= 0x000dffff then + -- RAM access. This is handled by SerialICE + -- but *NOT* exclusively. Writes should end + -- up in Qemu memory, too + if not ram_is_initialized then + -- RAM init has not not been marked done yet. + -- so send reads to the target only. + return true, false, 0 + end + -- RAM init is done. Send all RAM accesses + -- to Qemu. Using the target as storage would + -- only slow execution down. + -- TODO handle VGA / SMI memory correctly + return false, true, 0 + elseif addr >= 0x00100000 and addr <= 0xcfffffff then + -- 3.25GB RAM. This is handled by SerialICE. + -- We refrain from backing up this memory in Qemu + -- because Qemu would need 3.25G RAM on the host + -- and firmware usually does not intensively use + -- high memory anyways. + return true, false, 0 + else + printf("\nWARNING: undefined load operation @%08x\n", addr) + -- Fall through and handle by Qemu + end + return false, true, 0 +end + +-- SerialICE_memory_write_filter is the filter function for memory writes +-- +-- Parameters: +-- addr memory address to write to +-- size Size of the memory write +-- data Data to be written +-- Return values: +-- to_hw True if the write should be directed to the target +-- to_qemu True if the write should be directed to Qemu +-- result Data to be written (may be changed in filter) + +function SerialICE_memory_write_filter(addr, size, data) + -- Cache-As-RAM is exclusively + -- handled by Qemu (RAM backed) + if is_car(addr) then + return false, true, data + end + + if addr >= rom_base and addr <= 0xffffffff then + printf("\nWARNING: write access to ROM?\n") + -- ROM accesses go to Qemu only + return false, true, data + elseif addr >= PCIe_bar and addr <= (PCIe_bar + PCIe_size) then + -- PCIe MMIO config space accesses are + -- exclusively handled by the SerialICE + -- target + return true, false, data + elseif addr >= 0xfed10000 and addr <= 0xfed1ffff then + -- Intel chipset BARs are exclusively + -- handled by the SerialICE target + return true, false, data + elseif addr >= 0xfee00000 and addr <= 0xfeefffff then + -- Local APIC.. Hm, not sure what to do here. + -- We should avoid that someone wakes up cores + -- on the target system that go wild. + return true, false, data + elseif addr >= 0xfec00000 and addr <= 0xfecfffff then + -- IO APIC.. Hm, not sure what to do here. + return true, false, data + elseif addr >= 0xfed40000 and addr <= 0xfed45000 then + -- ICH7 TPM + return true, false, data + elseif addr >= 0x000e0000 and addr <= 0x000fffff then + -- Low ROM accesses go to Qemu memory + return false, true, data + elseif addr >= 0x000a0000 and addr <= 0x000affff then + -- SMI/VGA go to target + return true, true, data + elseif addr >= 0x00000000 and addr <= 0x000dffff then + -- RAM access. This is handled by SerialICE during + -- RAM initialization and by Qemu later on. + if not ram_is_initialized then + return true, true, data + end + -- Don't send writes to the target for speed reasons. + return false, true, data + elseif addr >= 0x00100000 and addr <= 0xcfffffff then + if addr == 0x00100000 then + if regs.cs == 0xe002 and regs.eip == 0x07fb then + -- skip high memory wipe + regs.ecx = 0x10 + end + if regs.cs == 0xe002 and regs.eip == 0x076c and regs.edi == 0x3f then + -- skip high memory test + regs.edi=1; + end + end + + -- 3.25 GB RAM ... This is handled by SerialICE + return true, false, data + else + printf("\nWARNING: undefined store operation @%08x\n", addr) + -- Fall through, send to SerialICE + end + + return true, false, data +end + + + +function SerialICE_memory_write_log(addr, size, data, target) + if addr >= 0x00000000 and addr <= 0x0009ffff and ram_is_initialized then + return + end + if addr >= 0x000c0000 and addr <= 0x000dffff and ram_is_initialized then + return + end + + log_cs_ip() + + if walk_list(mem_write_log_hooks, addr, size, data, target) then + return + end + + printf("MEM: write%s %08x <= %s", size_suffix(size), addr, size_data(size, data)) + if target then + printf(" *") + end + printf("\n") +end + +function SerialICE_memory_read_log(addr, size, data, target) + if addr >= 0x00000000 and addr <= 0x0009ffff and ram_is_initialized then + return + end + if addr >= 0x000c0000 and addr <= 0x000dffff and ram_is_initialized then + return + end + if addr >= 0xe0000 and addr <= 0xfffff and not log_rom_access then + return + end + if addr >= rom_base and addr <= 0xffffffff and not log_rom_access then + return + end + + log_cs_ip() + + if walk_list(mem_read_log_hooks, addr, size, data, target) then + return + end + + printf("MEM: read%s %08x => %s", size_suffix(size), addr, size_data(size, data)) + if target then + printf(" *") + end + printf("\n") +end + diff --git a/SerialICE/simba/misc.lua b/SerialICE/simba/misc.lua new file mode 100644 index 0000000..e7be535 --- /dev/null +++ b/SerialICE/simba/misc.lua @@ -0,0 +1,198 @@ +-- ********************************************************** +-- +-- KBD controller 8042 + +i8042_data = 0x0 +i8042_sts = 0x0 +i8042_cmd = 0x0 + +function i8042_write(port, size, data, filter) + if port == 0x60 then + i8042_data = data + i8042_sts = bit.band(i8042_sts, 0xf7) + if (i8042_cmd == 0xd1) then + gate_A20 = (bit.band(0x02, data) == 0x02) + end + end + if port == 0x64 then + i8042_cmd = data + i8042_sts = bit.bor(i8042_sts, 0x0a) + end + return false +end + +function i8042_read(port, size, data, filter) + if port == 0x60 then + i8042_data = data + i8042_sts = bit.band(i8042_sts, 0xfe) + end + return false +end + +function i8042_write_log(port, size, data, target) + if port == 0x60 and i8042_cmd == 0xd1 then + if gate_A20 then + printf("i8042: A20 enabled\n") + else + printf("i8042: A20 disabled\n") + end + + end + return false +end + +function i8042_read_log(port, size, data, target) + return false +end + + +if decode_i8042 then + prepend_to_list(io_write_hooks, i8042_write) + prepend_to_list(io_read_hooks, i8042_read) + prepend_to_list(io_write_log_hooks, i8042_write_log) + prepend_to_list(io_read_log_hooks, i8042_read_log) +end + +-- ********************************************************** +-- +-- CMOS nvram + +port_70_reg = 0x0 +port_72_reg = 0x0 + +nvram_data = {} +nvram_set = {} + +for i = 0, 0xff, 1 do nvram_data[i] = 0 end +for i = 0, 0xff, 1 do nvram_set[i] = false end + +function nvram_write(port, size, data, filter) + if port < 0x70 or port >= 0x74 then + return false + end + + + if port == 0x70 then + port_70_reg = bit.band(0x7f, data) + if port_70_reg < 0x0E then + filter.filter = false + else + filter.filter = true + end + elseif port == 0x71 then + nvram_data[port_70_reg] = data + nvram_set[port_70_reg] = true + if port_70_reg < 0x0E then + filter.filter = false + else + filter.filter = true + end + elseif port == 0x72 then + port_72_reg = bit.band(0x7f, data) + filter.filter = true + elseif port == 0x73 then + local index = 0x80 + port_72_reg + nvram_data[index] = data + nvram_set[index] = true + filter.filter = true + end + if cache_nvram then + return filter.filter + end + return false +end + +function nvram_read(port, size, data, filter) + if port < 0x70 or port >= 0x74 then + return false + end + filter.data = 0 + if port == 0x70 then + -- NMI returned as 0 + filter.data = port_70_reg + if port_70_reg < 0x0E then + filter.filter = false + else + filter.filter = true + end + elseif port == 0x71 then + if port_70_reg < 0x0E then + filter.filter = false + elseif nvram_set[port_70_reg] then + filter.data = nvram_data[port_70_reg] + filter.filter = true + else + filter.filter = true + end + elseif port == 0x72 then + -- NMI returned as 0 + filter.data = port_72_reg + filter.filter = false + elseif port == 0x73 then + local index = 0x80 + port_72_reg + if nvram_set[index] then + filter.data = nvram_data[index] + end + filter.filter = true + end + if cache_nvram then + return filter.filter + end + return false +end + +function nvram_write_log(port, size, data, target) + if port < 0x70 or port >= 0x74 then + return false + end + if port == 0x71 then + printf("NVram: [%02x] <= %02x\n", port_70_reg, data) + elseif port == 0x73 then + printf("NVram: [%02x] <= %02x\n", 0x80 + port_72_reg, data) + end + return not log_nvram_io +end + +function nvram_read_log(port, size, data, target) + if port < 0x70 or port >= 0x74 then + return false + end + if port == 0x71 then + printf("NVram: [%02x] => %02x\n", port_70_reg, data) + elseif port == 0x73 then + printf("NVram: [%02x] => %02x\n", 0x80 + port_72_reg, data) + end + return not log_nvram_io +end + + +if decode_nvram then + prepend_to_list(io_write_hooks, nvram_write) + prepend_to_list(io_read_hooks, nvram_read) + prepend_to_list(io_write_log_hooks, nvram_write_log) + prepend_to_list(io_read_log_hooks, nvram_read_log) +end + +-- ********************************************************** +-- +-- System reset + +function sys_rst(port, size, data, filter) + if port == 0xcf9 and data == 0x06 then + SerialICE_system_reset() + return false + end +end + +function sys_rst_log(port, size, data, target) + if port == 0xcf9 then + printf("Reset triggered at %04x:%04x\n", regs.cs, regs.eip); + return true + end +end + +if decode_sys_rst then + prepend_to_list(io_write_hooks, sys_rst) + prepend_to_list(io_write_log_hooks, sys_rst_log) +end + diff --git a/SerialICE/simba/serialice.lua b/SerialICE/simba/serialice.lua new file mode 100644 index 0000000..1c00204 --- /dev/null +++ b/SerialICE/simba/serialice.lua @@ -0,0 +1,158 @@ +-- SerialICE +-- +-- Copyright (c) 2009 coresystems GmbH +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- + +io.write("SerialICE: Starting LUA script\n") + +-- If you get an error here, install bitlib +-- (ie. http://luaforge.net/projects/bitlib/) +require("bit") + + +-- ------------------------------------------------------------------- +-- logging functions + +function log_cs_ip() + if (ip_logging) then printf("[%04x:%04x] -- ", regs.cs, regs.eip) end +end + +function printf(s,...) + return io.write(s:format(...)) +end + +function trim (s) + return (string.gsub(s, "^%s*(.-)%s*$", "%1")) +end + +function size_suffix(size) + if size == 1 then return "b" + elseif size == 2 then return "w" + elseif size == 4 then return "l" + elseif size == 8 then return "ll" + else return string.format("invalid size: %d", size) + end +end + +function size_data(size, data) + if size == 1 then return string.format("%02x", data) + elseif size == 2 then return string.format("%04x", data) + elseif size == 4 then return string.format("%08x", data) + elseif size == 8 then return string.format("%16x", data) + else return string.format("Error: size=%x", size) + end +end + + + +function new_list() + return { list = nil } +end + +function prepend_to_list(list, value) + list.list = { next = list.list, value = value } +end + +function walk_list(list, ...) + if list == nil or list.list == nil then + return false + end + local l = list.list + while l do + if l.value(...) then + return true + end + l = l.next + end + return false +end + + + +-- In the beginning, during RAM initialization, it is essential that +-- all DRAM accesses are handled by the target, or RAM will not work +-- correctly. After RAM initialization, RAM access has no "special" +-- meaning anymore, so we can just use Qemu's memory (and thus get +-- an incredible speed-up) + +-- Not catching the end of RAM init is not problematic, except +-- that it makes decompression of the BIOS core to RAM incredibly +-- slow as the decompressor inner loop has to be fetched +-- permanently from the target (several reads/writes per +-- decompressed byte). + +ram_is_initialized = false + + +-- Set to "true" to log read access (code fetches) to 0xe0000 to 0xfffff + +log_rom_access = false + +-- Set to "true" to log CS:IP for each access + +ip_logging = false + + +rom_size = 4 * 1024 * 1024 +rom_base = 0x100000000 - rom_size + + +decode_pci_io_cfg = true +decode_pci_mm_cfg = true +decode_nvram = true +decode_sys_rst = true +decode_smbus = true +decode_superio = true +decode_i8042 = true + +-- Log hooks only apply when decode above is enabled +log_pci_io_cfg = false +log_pci_mm_cfg = false +log_superio_cfg = false +log_nvram_io = false +log_smbus_io = false + +-- Use lua table for NVram +cache_nvram = false + +-- This initialization is executed right after target communication +-- has been established + +dofile("core_io.lua") +dofile("superio.lua") +dofile("memory.lua") +dofile("cpu.lua") +dofile("misc.lua") + +printf("SerialICE: LUA script initialized.\n") + +mainboard_file = string.format("%s.lua", string.lower(string.gsub(SerialICE_mainboard, " ", "_"))) + +local mainboard_lua = loadfile(mainboard_file) +if (mainboard_lua) then + mainboard_lua() + printf("SerialICE: Mainboard script %s initialized.\n", mainboard_file) +else + printf("SerialICE: Mainboard script %s not found.\n", mainboard_file) +end + +return true + diff --git a/SerialICE/simba/superio.lua b/SerialICE/simba/superio.lua new file mode 100644 index 0000000..9f0c93b --- /dev/null +++ b/SerialICE/simba/superio.lua @@ -0,0 +1,235 @@ +-- ********************************************************** +-- +-- SuperIO config handling + +port_2e_reg = 0x0 +port_2e_ldn = 0x0 +port_2f_reg = 0x0 + +port_4e_reg = 0x0 +port_4e_ldn = 0x0 +port_4f_reg = 0x0 + +if 1 then + -- SMSC? + port_2e_ldn_register = 0x07 + port_4e_ldn_register = 0x07 +else + -- Winbond + port_2e_ldn_register = 0x06 + port_4e_ldn_register = 0x06 +end + + +ldn_set = {} +ldn_data = {} + +function reset_2e_ldn(data) + if not (port_2e_ldn == data) then + port_2e_ldn = data + for i = 0, 0xff, 1 do ldn_data[i] = 0 end + for i = 0, 0xff, 1 do ldn_set[i] = false end + end +end + +function set_2e_reg(data) + port_2e_reg = data +end + +function set_2e_data(data) + + if port_2e_reg == port_2e_ldn_register then + reset_2e_ldn(data) + end + ldn_data[port_2e_reg] = data; + ldn_set[port_2e_reg] = true; +end + + +function port_2f_write_log(port, size, data, target) + + local pnpdev = bit.band(port, 0xfe) + + if port_2e_reg == port_2e_ldn_register then + return true + end + + if ((port_2e_reg == 0x30) or (port_2e_reg == 0x60) or (port_2e_reg == 0x61)) then + if (ldn_set[0x30] and not (ldn_data[0x30]==0x0)) then + if (ldn_set[0x60] and ldn_set[0x61]) then + local iobase = bit.bor(bit.lshift(ldn_data[0x60], 8), ldn_data[0x61]) + printf("PnP: %02x:%02x enabled @ iobase = 0x%04x\n", + pnpdev, port_2e_ldn, iobase) + elseif (port_2e_reg == 0x30) then + printf("PnP: %02x:%02x enabled\n", pnpdev, port_2e_ldn) + end + elseif (port_2e_reg == 0x30) and (ldn_set[0x30]==true) and (ldn_data[0x30]==0x0) then + printf("PnP: %02x:%02x disabled\n", pnpdev, port_2e_ldn) + end + return true + end + + if port_2e_reg == 0x70 then + printf("PnP: %02x:%02x irq = %d\n", pnpdev, port_2e_ldn, ldn_data[0x70]) + return true + end + if port_2e_reg == 0x72 then + printf("PnP: %02x:%02x irq2 = %d\n", pnpdev, port_2e_ldn, ldn_data[0x72]) + return true + end + + printf("PnP: %02x:%02x R.%02x <= %02x\n", pnpdev, port_2e_ldn, port_2e_reg, ldn_data[port_2e_reg]) + return true +end + +function port_2f_read_log(port, size, data, target) + local pnpdev = bit.band(port, 0xfe) + printf("PnP %02x:%02x R.%02x => %02x\n", pnpdev, port_2e_ldn, port_2e_reg, data) + return true +end + +function superio_write(port, size, data, filter) + + if port == 0x2e then + set_2e_reg(data) + return false + end + + if port == 0x2f then + set_2e_data(data) + + -- Don't allow that our SIO power gets disabled. + if port_2e_reg == 0x02 then + printf("SuperIO (filtered)\n") + filter.filter = true + filter.data = data + return true + end + + -- Don't mess with oscillator setup. + if port_2e_reg == 0x24 then + printf("SuperIO (filtered)\n") + filter.filter = true + filter.data = data + return true + end + end + + if port == 0x4e then + port_4e_reg = data + filter.filter = false + filter.data = data + return false + end + + if port == 0x4f then + if port_4e_reg == port_4e_ldn_register then + port_4e_ldn = data + return false + end + -- Don't allow that our Serial power gets disabled. + if port_4e_reg == 0x02 then + printf("SuperIO (filtered)\n") + return true, data + end + -- Don't mess with oscillator setup. + if port_4e_reg == 0x24 then + printf("SuperIO (filtered)\n") + filter.filter = true + filter.data = data + return true + end + + end + + return false +end + + + +function superio_write_log(port, size, data, target) + + if port == 0x2e then + return not log_superio_cfg + end + if port == 0x2f then + port_2f_write_log(port, size, data, target) + return not log_superio_cfg + end + + if port == 0x4e then + return not log_superio_cfg + end + if port == 0x4f then + if not (port_4e_reg == port_4e_ldn_register) then + printf("PnP: %02x:%02x R.%02x <= %02x\n", 0x4e, port_4e_ldn, port_4e_reg, data) + end + return not log_superio_cfg + end + return false +end + +function superio_read_log(port, size, data, target) + + if port == 0x2e then + return not log_superio_cfg + end + if port == 0x2f then + port_2f_read_log(port, size, data, target) + return not log_superio_cfg + end + + if port == 0x4e then + return not log_superio_cfg + end + if port == 0x4f then + printf("PnP %02x:%02x R.%02x => %02x\n", 0x4e, port_4e_ldn, port_4e_reg, data) + return not log_superio_cfg + end + return false +end + +if decode_superio then + prepend_to_list(io_write_hooks, superio_write) + prepend_to_list(io_write_log_hooks, superio_write_log) + prepend_to_list(io_read_log_hooks, superio_read_log) +end + + + +-- ********************************************************** +-- +-- Serial Port handling + +com1_base = 0x3f8 +com1_size = 0x8 + + +function uart_write(port, size, data, filter) + if port > com1_base and port < com1_base + com1_size then + printf("serial I/O (filtered)\n") + filter.filter = true + filter.data = data + return true + end + + if port == com1_base then + printf("COM1: %c\n", data) + filter.filter = true + filter.data = data + return true + end +end + +function uart_read(port, size, data, filter) + if port >= com1_base and port < com1_base + com1_size then + printf("Serial I/O read (filtered)\n") + filter.filter = true + filter.data = 0xff + return true + end +end + +prepend_to_list(io_write_hooks, uart_write) +prepend_to_list(io_read_hooks, uart_read) +