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 c5872570b7bad3a74a201cd1e5cae5b0bc6ea08a Author: Kyösti Mälkki kyosti.malkki@gmail.com Date: Wed Apr 4 17:26:04 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.
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 | 94 ++++++++++++ SerialICE/simba/core_io.lua | 283 +++++++++++++++++++++++++++++++++++ SerialICE/simba/cpu.lua | 94 ++++++++++++ SerialICE/simba/i82801dx.lua | 108 +++++++++++++ SerialICE/simba/leftover.lua | 135 +++++++++++++++++ SerialICE/simba/memory.lua | 239 +++++++++++++++++++++++++++++ SerialICE/simba/misc.lua | 79 ++++++++++ SerialICE/simba/serialice.lua | 155 +++++++++++++++++++ SerialICE/simba/superio.lua | 172 +++++++++++++++++++++ 9 files changed, 1359 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..db528e5 --- /dev/null +++ b/SerialICE/simba/aopen_dxpl_plus.lua @@ -0,0 +1,94 @@ + + +dofile("i82801dx.lua") + +-- ********************************************************** +-- + +function mainboard_io_read(port, size, data, filter) + + -- KBD controller is stuck, clear 0x02 + if ( port == 0x64 and size == 1 ) then + filter.filter = true + filter.data = 0x80 + 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 + + -- 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 + + 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..1cdc2a0 --- /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..f7d2dbd --- /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..712be69 --- /dev/null +++ b/SerialICE/simba/i82801dx.lua @@ -0,0 +1,108 @@ +-- ********************************************************** +-- +-- LPC decode ranges + + +function lpc_decode_write(port, size, data, filter) + -- LPC decode registers + if SerialICE_pci_device == pci_bdfr(0x0,0x1f,0x3,0xe6) then + printf("LPC (filtered)\n") + filter.filter = true + filter.data = data + return true + 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..8839248 --- /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..4c1dbc5 --- /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..dc8f00e --- /dev/null +++ b/SerialICE/simba/misc.lua @@ -0,0 +1,79 @@ +-- ********************************************************** +-- +-- CMOS nvram + +port_70_reg = 0x0 +port_72_reg = 0x0 + +function nvram_write(port, size, data, filter) + if port == 0x70 then + port_70_reg = bit.band(0x7f, data) + return false, data + end + if port == 0x72 then + port_72_reg = bit.band(0x7f, data) + return false, data + end + +end + +function nvram_write_log(port, size, data, target) + if port == 0x71 then + printf("NVram: [%02x] <= %02x\n", port_70_reg, data) + return not log_nvram_cfg + end + if port == 0x73 then + printf("NVram: [%02x] <= %02x\n", 0x80 + port_72_reg, data) + return not log_nvram_cfg + end + if port >= 0x70 and port < 0x74 then + return not log_nvram_cfg + end + return false +end + +function nvram_read_log(port, size, data, target) + if port == 0x71 then + printf("NVram: [%02x] => %02x\n", port_70_reg, data) + return not log_nvram_cfg + end + if port == 0x73 then + printf("NVram: [%02x] => %02x\n", 0x80 + port_72_reg, data) + return not log_nvram_cfg + end + if port >= 0x70 and port < 0x74 then + return not log_nvram_cfg + end + return false +end + + +if decode_nvram then + prepend_to_list(io_write_hooks, nvram_write) + 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, data + 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..a5535bb --- /dev/null +++ b/SerialICE/simba/serialice.lua @@ -0,0 +1,155 @@ +-- 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 + +-- 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_cfg = false +log_smbus_io = 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..eba5de4 --- /dev/null +++ b/SerialICE/simba/superio.lua @@ -0,0 +1,172 @@ +-- ********************************************************** +-- +-- 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 + + +function superio_write(port, size, data, filter) + + if port == 0x2e then + -- We start requiring a decent state machine + port_2e_reg = data + filter.filter = false + filter.data = data + return false + end + + if port == 0x2f then + if port_2e_reg == port_2e_ldn_register then + port_2e_ldn = data + filter.filter = false + filter.data = data + return false + end + + -- 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 + filter.filter = false + filter.data = 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 or port == 0x4e then + return not log_superio_cfg + end + if port == 0x2f then + if not (port_2e_reg == port_2e_ldn_register) then + printf("PnP %02x:%02x R.%02x <= %02x\n", 0x2e, port_2e_ldn, port_2e_reg, data) + end + 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 or port == 0x4e then + return not log_superio_cfg + end + if port == 0x2f then + printf("PnP %02x:%02x R.%02x => %02x\n", 0x2e, port_2e_ldn, port_2e_reg, data) + 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) +