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 66955fa55672261a0b98426b815b64ac08ba01b3 Author: Kyösti Mälkki kyosti.malkki@gmail.com Date: Sun May 6 22:25:50 2012 +0300
Modularized SerialICE scripting [NOTFORMERGE]
I will try to do some kind of progressive patch for this one. Until then, this one can be used with the SerialICE patches for Qemu 0.15.x where lua interface is modified.
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 | 143 +++++++++++ SerialICE/simba/core_io.lua | 305 ++++++++++++++++++++++ SerialICE/simba/cpu.lua | 126 ++++++++++ SerialICE/simba/e7505.lua | 12 + SerialICE/simba/hooks.lua | 291 +++++++++++++++++++++ SerialICE/simba/i82801dx.lua | 149 +++++++++++ SerialICE/simba/interface.lua | 161 ++++++++++++ SerialICE/simba/leftover.lua | 136 ++++++++++ SerialICE/simba/memory.lua | 239 ++++++++++++++++++ SerialICE/simba/mmio.lua | 83 ++++++ SerialICE/simba/output.lua | 81 ++++++ SerialICE/simba/pc80.lua | 473 +++++++++++++++++++++++++++++++++++ SerialICE/simba/serialice.lua | 118 +++++++++ SerialICE/simba/superio.lua | 200 +++++++++++++++ 14 files changed, 2517 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..d199e27 --- /dev/null +++ b/SerialICE/simba/aopen_dxpl_plus.lua @@ -0,0 +1,143 @@ +-- SerialICE +-- +-- Copyright (c) 2012 Kyösti Mälkki kyosti.malkki@gmail.com +-- +-- 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. +-- + +dofile("i82801dx.lua") +dofile("e7505.lua") +dofile("mmio.lua") + +-- ********************************************************** +-- + +function mainboard_io_read(f, action) + -- Some timer loop + if ( action.addr == 0x61 ) then + if ( regs.eip == 0x1634 ) then + regs.ecx = 0x01 + return fake_action(f, action, 0x20) + end + if ( regs.eip == 0x163a ) then + regs.ecx = 0x01 + return fake_action(f, action, 0x30) + end + end + + -- IO slowdown + if action.addr == 0xed then + ignore_action(f, action) + return drop_action(f, action, 0) + end + + if action.addr == 0xcfb then + ignore_action(f, action) + return drop_action(f, action, 0) + end + + return skip_filter(f, action) +end + + +function mainboard_io_write(f, action) + + -- Catch RAM controller ready. + if action.addr == 0x80 and action.data == 0x2c and not ram_enabled() then + enable_ram() + end + + if action.addr == 0xcfb then + ignore_action(f, action) + return drop_action(f, action, 0) + end + + if action.addr == 0xed then + if ( regs.eip == 0x1792 ) then + regs.ecx = 0x01 + end +if false then + -- SIPI delay + if ( regs.eip == 0xb3bc or regs.eip == 0xb3bf ) then + regs.ecx = 0x01 + end + if ( regs.eip == 0xb4ad or regs.eip == 0xb4af ) then + regs.ecx = 0x01 + end +end + ignore_action(f, action) + return drop_action(f, action, action.data) + end + + return skip_filter(f, action) +end + +function mainboard_io_pre(f, action) + if action.write then + return mainboard_io_write(f, action) + else + return mainboard_io_read(f, action) + end +end + +function mainboard_io_post(f, action) + if action.addr == 0xed or action.addr == 0xcfb then + return true + end + + -- If KBD controller returns status=0xff, clear 0x02. + if action.addr == 0x64 and not action.write and action.size == 1 then + if action.data == 0xff then + -- tag these but give out correct data + fake_action(f, action, action.data) + end + end +end + +filter_mainboard = { + id = -1, + name = "AOpen", + pre = mainboard_io_pre, + post = mainboard_io_post, + hide = hide_mainboard_io, + base = 0x0, + size = 0x10000 +} + + +function do_mainboard_setup() + enable_hook(io_hooks, filter_pci_io_cfg) + enable_hook(mem_hooks, filter_lapic) + enable_hook(mem_hooks, filter_ioapic) + + enable_hook(cpumsr_hooks, filter_intel_microcode) + enable_hook(cpuid_hooks, filter_multiprocessor) + + -- I have a hook to detect RAM initialisation from + -- a POST code I can skip this here + --enable_ram() + + enable_hook_pc80() + enable_hook_superio(0x2e, 0x07) + --enable_hook(io_hooks, filter_com1) + enable_hook_i82801dx() + + -- Apply mainboard hooks last, so they are the first ones to check + enable_hook(io_hooks, filter_mainboard) +end diff --git a/SerialICE/simba/core_io.lua b/SerialICE/simba/core_io.lua new file mode 100644 index 0000000..b47b088 --- /dev/null +++ b/SerialICE/simba/core_io.lua @@ -0,0 +1,305 @@ + +function io_undefined(f, action) + action.to_hw = true + action.to_qemu = false + action.undefined = true + return true +end + +function io_post(f, action) + if (action.write) then + printk(f, action, "out%s %04x <= %s\n", size_suffix(action.size), action.addr, size_data(action.size, action.data)) + else + printk(f, action, " in%s %04x => %s\n", size_suffix(action.size), action.addr, size_data(action.size, action.data)) + end + return true +end + +function io_base_post(f, action) + if (action.write) then + printk(f, action, "[%04x] <= %s\n", bit.band(action.addr, (f.size - 1)), size_data(action.size, action.data)) + else + printk(f, action, "[%04x] => %s\n", bit.band(action.addr, (f.size - 1)), size_data(action.size, action.data)) + end + return true +end + +filter_io_fallback = { + id = -1, + name = "IO", + pre = io_undefined, + post = io_post, + base = 0x0, + size = 0x10000, +} + + +-- ********************************************************** +-- +-- PCI IO config access + + +pci_cfg_hooks = new_list() + +function add_pci_cfg_hook(dev, reg, size, func) + value = { + bdf = bit.bor(dev.pci_dev, reg), + dev = dev, + reg = reg, + size = size, + func = func + } + prepend_to_list(pci_cfg_hooks, value) +end + + + +function hook_pci_cfg8(dev, reg, hook) + add_pci_cfg_hook(dev, reg, 1, hook) +end + +function hook_pci_cfg16(dev, reg, hook) + add_pci_cfg_hook(dev, reg, 2, hook) +end + +function hook_pci_cfg32(dev, reg, hook) + add_pci_cfg_hook(dev, reg, 4, hook) +end + + + +function io_bar_hook(dev, reg, data) + local bar = dev.bar[reg] + if bar then + bar.base = bit.band(data, bit.bnot(bar.size-1)) + generic_io_bar(bar) + end +end + +function mem_bar_hook(dev, reg, data) + local bar = dev.bar[reg] + if bar then + bar.base = bit.band(data, bit.bnot(bar.size-1)) + generic_mmio_bar(bar) + end +end + +function add_bar(dev, reg, name, size) + if not dev.bar then + dev.bar = {} + end + if not dev.bar[reg] then + dev.bar[reg] = {} + end + dev.bar[reg].f = nil + dev.bar[reg].name = name + dev.bar[reg].base = 0x0 + dev.bar[reg].size = size +end + +function add_io_bar(dev, reg, name, size) + add_bar(dev, reg, name, size) + hook_pci_cfg16(dev, reg, io_bar_hook) +end + +function add_mem_bar(dev, reg, name, size) + add_bar(dev, reg, name, size) + hook_pci_cfg32(dev, reg, mem_bar_hook) +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(l.value.dev, l.value.reg, 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 + +-- Catch partial PCI configuration register writes. +-- This synthesizes 32/16 bit wide access from separate +-- 16/8 bit accesses for pci_cfg_hooks. + +bv = {} + +function is_pci_io_cfg(bdf) + local f = filter_pci_io_cfg + return f.reg.bdfr == bdf +end + +function pci_cfg_0cf8_reset(f, data) + f.reg.bdfr = bit.band(data,bit.bnot(0x3)) + if bit.band(0x80000000, f.reg.bdfr) ~= 0 then + f.reg.reset = true + new_parent_action() + end + if not (f.reg.bdfr_hook == f.reg.bdfr) then + f.reg.bdfr_hook = 0 + if (is_pci_cfg_hooked(f.reg.bdfr)) then + f.reg.bdfr_hook = f.reg.bdfr + end + for i = 0, 3, 1 do bv[i] = false end + end +end + +function pci_cfg_0cfc_access(f, addr, size, data) + + if not f.reg.reset and ((f.reg.prev_addr ~= addr) or (f.reg.prev_size ~= size)) then + new_parent_action() + end + f.reg.reset = false + f.reg.prev_addr = addr + f.reg.prev_size = size + + if f.reg.bdfr_hook == 0 then + return + end + + local av = {} + + for i = 0, 3, 1 do av[i] = false end + + ll = 8 * (addr%4) + if (size == 1) then + av[addr%4] = true + bv[addr%4] = true + amask = bit.lshift(0xff, ll) + omask = bit.lshift(data, ll) + f.reg.data = bit.band(f.reg.data, bit.bnot(amask)) + f.reg.data = bit.bor(f.reg.data, omask) + elseif (size == 2) then + av[addr%4] = true + bv[addr%4] = true + av[addr%4+1] = true + bv[addr%4+1] = true + amask = bit.lshift(0xffff, ll) + omask = bit.lshift(data, ll) + f.reg.data = bit.band(f.reg.data, bit.bnot(amask)) + f.reg.data = bit.bor(f.reg.data, omask) + elseif (size == 4) then + f.reg.data = 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(f.reg.bdfr + i, 1, + bit.band(0xff, bit.rshift(f.reg.data, i*8))) + end + end + if ((bv[0] and bv[1]) and (av[0] or av[1])) then + call_pci_cfg_hook(f.reg.bdfr + 0x00, 2, + bit.band(0xffff, bit.rshift(f.reg.data, 0))) + end + if (bv[2] and bv[3] and (av[2] or av[3])) then + call_pci_cfg_hook(f.reg.bdfr + 0x02, 2, + bit.band(0xffff, bit.rshift(f.reg.data, 16))) + end + if (bv[0] and bv[1] and bv[2] and bv[3]) then + call_pci_cfg_hook(f.reg.bdfr, 4, f.reg.data) + end +end + +function pci_io_cfg_pre(f, action) + if action.addr == 0xcf8 and action.size == 4 then + if action.write then + pci_cfg_0cf8_reset(f, action.data) + end + return handle_action(f, action) + end + if action.addr >= 0xcfc and action.addr <= 0xcff then + if action.write then + pci_cfg_0cfc_access(f, action.addr, action.size, action.data) + end + return handle_action(f, action) + end + return skip_filter(f, action) +end + +function pci_io_cfg_post(f, action) + if action.addr == 0xcf8 and action.size == 4 then + return true + end + if action.addr >= 0xcfc and action.addr <= 0xcff then + if (action.write) then + dir_str = "<=" + else + dir_str = "=>" + end + printk(f, action, "%x:%02x.%x [%02x] %s %s\n", + bit.band(0xff,bit.rshift(f.reg.bdfr, 16)), + bit.band(0x1f,bit.rshift(f.reg.bdfr, 11)), + bit.band(0x7,bit.rshift(f.reg.bdfr, 8)), + bit.band(0xff,f.reg.bdfr + (action.addr - 0xcfc)), + dir_str, size_data(action.size, action.data)) + return true + end + return false +end + +filter_pci_io_cfg = { + id = -1, + name = "PCI", + pre = pci_io_cfg_pre, + post = pci_io_cfg_post, + hide = hide_pci_io_cfg, + base = 0xcf8, + size = 0x08, + reg = { bdfr = 0, bdfr_hook = 0, data = 0, + reset = true, prev_addr = 0, prev_size = 0 } +} + +-- ********************************************************** +-- +-- PCIe MM config access + +function pci_mm_cfg_post(f, action) + + if (action.write) then + dir_str = "<=" + else + dir_str = "=>" + end + printk(f, action, "%x:%02x.%x [%02x] %s %s\n", + bit.band(0xff,bit.rshift(action.addr, 20)), + bit.band(0x1f,bit.rshift(action.addr, 15)), + bit.band(0x7,bit.rshift(action.addr, 12)), + bit.band(0xfff,action.addr), + dir_str, size_data(action.size, action.data)) + return true +end + +filter_pci_mm_cfg = { + id = -1, + name = "PCIe", + pre = mem_target_only, + post = pci_mm_cfg_post, + hide = hide_pci_mm_cfg, + base = 0x0, + size = 0x0, +} + +function pcie_mm_cfg_bar(base, size) + filter_pci_mm_cfg.base = base + filter_pci_mm_cfg.size = size + enable_hook(mem_hooks, filter_pci_mm_cfg) +end diff --git a/SerialICE/simba/cpu.lua b/SerialICE/simba/cpu.lua new file mode 100644 index 0000000..d162f62 --- /dev/null +++ b/SerialICE/simba/cpu.lua @@ -0,0 +1,126 @@ + +function var_mtrr_post(f, action) + + local addr = action.rin.ecx + local hi = action.rin.edx + local lo = action.rin.eax + + 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 + printk(f, action, "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 + printk(f, action, "Set MTRR %x mask to %08x.%08x (%s)\n", (addr - 0x200) / 2, hi, bit.band(lo, 0xfffff000), valid) + end +end + +function cpumsr_pre(f, action) + return handle_action(f, action) +end + +function cpumsr_post(f, action) + if action.write then + printk(f, action, "[%08x] <= %08x.%08x\n", + action.rin.ecx, action.rin.edx, action.rin.eax) + if action.addr >= 0x200 and action.addr < 0x210 then + var_mtrr_post(f, action) + end + else + printk(f, action, "[%08x] => %08x.%08x\n", + action.rin.ecx, action.rout.edx, action.rout.eax) + end + return true +end + + +function cpuid_pre(f, action) + return handle_action(f, action) +end + +function cpuid_post(f, action) + printk(f, action, "eax: %08x; ecx: %08x => %08x.%08x.%08x.%08x\n", + action.rin.eax, action.rin.ecx, + action.rout.eax, action.rout.ebx, action.rout.ecx, action.rout.edx) + return true +end + + + +filter_cpumsr_fallback = { + id = -1, + name = "CPU MSR", + pre = cpumsr_pre, + post = cpumsr_post, +} +filter_cpuid_fallback = { + id = -1, + name = "CPUID", + pre = cpuid_pre, + post = cpuid_post, +} + + + +function multicore_pre(f, action) + return skip_filter(f, action) +end + +function multicore_post(f, action) + local rout = action.rout + local rin = action.rin + -- 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 not action.write and rin.eax == 0x01 then + rout.ebx = bit.band(0xff00ffff, rout.ebx); + rout.ebx = bit.bor(0x00010000, rout.ebx); + fake_action(f, action, 0) + end + return skip_filter(f, action) +end + +filter_multiprocessor = { + id = -1, + name = "Multiprocessor Count", + pre = multicore_pre, + post = multicore_post, +} + +-- Intel CPU microcode update +function intel_microcode_pre(f, action) + if action.rin.ecx == 0x79 then + --action.dropped = true + --action.rout.edx = 0 + --action.rout.eax = 0xffff0000 + return drop_action(f, action) + end + return skip_filter(f, action) +end + +-- Intel CPU microcode revision check +-- Fakes microcode revision of my 0x6f6 Core 2 Duo Mobile +function intel_microcode_post(f, action) + if action.rin.ecx == 0x8b then + action.rout.edx = 0xc7 + action.rout.eax = 0 + return fake_action(f, action, 0) + end + return skip_filter(f, action) +end + +filter_intel_microcode = { + id = -1, + name = "Microcode Update", + pre = intel_microcode_pre, + post = intel_microcode_post, +} diff --git a/SerialICE/simba/e7505.lua b/SerialICE/simba/e7505.lua new file mode 100644 index 0000000..70d96ea --- /dev/null +++ b/SerialICE/simba/e7505.lua @@ -0,0 +1,12 @@ + + +dev_e7505_mch = { + pci_dev = pci_bdf(0x0,0x0,0x0,0x0), + name = "MCH", + bar = {} +} + +add_mem_bar(dev_e7505_mch, 0x14, "RCOMP", 0x1000) + + + diff --git a/SerialICE/simba/hooks.lua b/SerialICE/simba/hooks.lua new file mode 100644 index 0000000..81a8439 --- /dev/null +++ b/SerialICE/simba/hooks.lua @@ -0,0 +1,291 @@ + + +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 + + + +next_filter_id = 1 +next_action_id = 1 +current_parent_id = 0 + +function action_id() + local n = next_action_id + next_action_id = next_action_id + 1 + return n +end + +function new_parent_action() + current_parent_id = action_id() +end + + +io_hooks = new_list() +mem_hooks = new_list() + +cpumsr_hooks = new_list() +cpuid_hooks = new_list() + + +function enable_hook(list, filter) + if not filter then + printks(froot, "Enable_hook called with filter==nil\n") + return + end + + local l = list.list + local found = false + while l and not found do + found = (l.hook == filter) + l = l.next + end + if not found then + if (filter.id < 0) then + filter.id = next_filter_id + next_filter_id = next_filter_id + 1 + end + list.list = { next = list.list, hook = filter } + end + if (list == io_hooks) then + printks(fresource, "[%04x] IO [%04x-%04x] = %s\n", + filter.id, filter.base, filter.base + filter.size - 1, filter.name) + elseif (list == mem_hooks) then + printks(fresource, "[%04x] MEM [%08x-%08x] = %s\n", + filter.id, filter.base, filter.base + filter.size - 1, filter.name) + else + printks(fresource, "[%04x] %s\n", filter.id, filter.name) + end + filter.enable = true +end + +function disable_hook(list, filter) + printks(froot, "id=%04x disabled\n", filter.id) + filter.enable = false +end + + + + + + +prev_filter = nil + +function walk_pre_hooks(list, action) + if list == nil or list.list == nil then + return false + end + local logged = false + local l = list.list + local f = nil + + if list == io_hooks or list == mem_hooks then + + while l and not logged do + f = l.hook + if action.addr >= f.base and action.addr < f.base + f.size then + if f.enable and f.pre then + logged = f.pre(f, action) + end + end + l = l.next + end + + else + + while l and not logged do + f = l.hook + if f.enable and f.pre then + logged = f.pre(f, action) + end + l = l.next + end + + end + + if prev_filter ~= f and not action.ignore then + prev_filter = f + new_parent_action() + end + if action.dropped then + action.to_hw = false + action.to_qemu = false + end +end + +function walk_post_hooks(list, action) + if list == nil or list.list == nil then + return false + end + local logged = false + local l = list.list + local f = nil + + if list == io_hooks or list == mem_hooks then + + while l and not logged do + f = l.hook + if action.addr >= f.base and action.addr < f.base + f.size then + if f.enable and f.post then + if f.post(f, action) then + action.f = f + logged = f.hide and not log_everything + end + end + end + l = l.next + end + + else + + while l and not logged do + f = l.hook + if f.enable and f.post then + logged = f.post(f, action) + end + l = l.next + end + + end + + +end + +function generic_io_bar(bar) + if not bar.f then + f = {} + f.id = -1 + f.pre = handle_action + f.post = io_base_post + f.hide = true + f.name = bar.name + f.size = bar.size + bar.f = f + end + bar.f.base = bit.band(bar.base, bit.bnot(bar.size-1)) + if bar.f.base ~= 0 then + enable_hook(io_hooks, bar.f) + else + disable_hook(io_hooks, bar.f) + end +end + +function generic_mmio_bar(bar) + if not bar.f then + f = {} + f.id = -1 + f.pre = handle_action + f.post = mem_base_post + f.hide = true + f.name = bar.name + f.size = bar.size + bar.f = f + end + bar.f.base = bit.band(bar.base, bit.bnot(bar.size-1)) + if bar.f.base ~= 0 then + enable_hook(mem_hooks, bar.f) + else + disable_hook(mem_hooks, bar.f) + end +end + +PRE_HOOK = 1 +POST_HOOK = 2 + +function handle_action(f, action) + action.to_hw = true + return true +end + +function drop_action(f, action, data) + if action.stage == POST_HOOK then + printk(f, action, "ERROR: Cannot drop action in a post-hook.\n") + return true + end + action.dropped = true + action.data = data + return true +end + +function ignore_action(f, action) + action.ignore = true +end + +function fake_action(f, action, data) + if action.stage == POST_HOOK and action.write then + printk(f, action, "ERROR: Cannot fake write in a post-hook.\n") + return true + end + action.faked = true + action.data = data + return true +end + + +function skip_filter(f, action) + return false +end + +function pre_action(action, dir_wr, addr, size, data) + action.stage = PRE_HOOK + -- CS:IP logging + action.cs = regs.cs + action.eip = regs.eip + + -- no filter, not filtered + action.f = nil + action.ignore = false + action.undefined = false + action.faked = false + action.dropped = false + action.to_hw = false + action.to_qemu = false + + action.write = dir_wr + action.addr = addr + action.size = size + if action.write then + action.data = data + else + action.data = 0 + end +end + +function post_action(action, data) + action.stage = POST_HOOK + action.parent_id = current_parent_id + action.my_id = action_id() + + if not action.write then + if not action.faked and not action.dropped then + action.data = data + end + end +end + +function load_regs(regs, eax, ebx, ecx, edx) + regs.eax = eax + regs.ebx = ebx + regs.ecx = ecx + regs.edx = edx +end + + diff --git a/SerialICE/simba/i82801dx.lua b/SerialICE/simba/i82801dx.lua new file mode 100644 index 0000000..fb7dfc8 --- /dev/null +++ b/SerialICE/simba/i82801dx.lua @@ -0,0 +1,149 @@ +-- ********************************************************** +-- +-- LPC decode ranges + +function lpc_decode_write(f, action) + -- LPC decode registers + if action.write and is_pci_io_cfg(pci_bdf(0x0,0x1f,0x0,0xe4)) then + if action.addr == 0xcfc and action.size == 4 or action.addr == 0xcfe then + return drop_action(f, action, action.data) + end + end +end + +filter_lpc_lock = { + id = -1, + name = "LPC", + pre = lpc_decode_write, + base = 0xcfc, + size = 0x4 +} + + +-- ********************************************************** +-- +-- SMBus controller handling + +require("package") + +simba_init, err, str = package.loadlib("./libsimba.so", "luaopen_simba") +if (simba_init) then + simba_init() + printks(froot, "SIMBA initialized\n") +else + printks(froot, "%s : %s\n", err, str) +end + +function smbus_host_post(f, action) + if smbus_host then + local val + local str + if (action.write) then + smbus_host.write_log(action.addr, action.size, action.data, true) + else + smbus_host.read_log(action.addr, action.size, action.data, true) + end + val, str = smbus_host.get_output() + if val == 0 then + printk(f, action, "%s", str) + new_parent_action() + end + end + return true +end + +filter_smbus_host = { + id = -1, + name = "SMBus-00", + pre = handle_action, + post = smbus_host_post, + hide = hide_smbus_io, + base = 0x0, + size = 0x0, +} + +function smbus_bar_setup(base, size) + filter_smbus_host.base = bit.band(base, bit.bnot(size-1)) + filter_smbus_host.size = size + + if simba then + simba.smbus_init(filter_smbus_host.base, filter_smbus_host.size) + end + if filter_smbus_host.id < 0 then + enable_hook(io_hooks, filter_smbus_host) + end +end + +function smbus_bar_hook(dev, reg, base) + smbus_bar_setup(base, 0x20) +end + +dev_sb_lpc = { + pci_dev = pci_bdf(0x0,0x1f,0x3,0x0), + name = "Smbus", + bar = {}, +} + +function enable_smbus_host_bar() + hook_pci_cfg16(dev_sb_lpc, 0x20, smbus_bar_hook) +end + + +-- ********************************************************** +-- + +dev_power = { + pci_dev = pci_bdf(0x0,0x1f,0x0,0x0), + name = "SYS", + bar = {}, + acpi = { f = nil }, + tco = { f = nil }, +} + +function pm_io_bar(dev, reg, base) + dev.acpi.name = "ACPI" + dev.acpi.base = base + dev.acpi.size = 0x60 + generic_io_bar(dev.acpi) + + dev.tco.name = "TCO" + dev.tco.base = base + 0x60 + dev.tco.size = 0x20 + generic_io_bar(dev.tco) +end + +function enable_power_bars() + --add_io_bar(dev_power, 0x40, "PM", 0x80) + hook_pci_cfg16(dev_power, 0x40, pm_io_bar) + add_io_bar(dev_power, 0x58, "GPIO", 0x40) +end + +-- ********************************************************** +-- +-- AC '97 controller handling + + +dev_audio = { + pci_dev = pci_bdf(0x0,0x1f,0x5,0x0), + name = "Audio", + bar = {} +} + +function enable_audio_bars() + add_io_bar(dev_audio, 0x10, "NAMBAR", 0x100) + add_io_bar(dev_audio, 0x14, "NABMBAR", 0x40) + add_mem_bar(dev_audio, 0x18, "MMBAR", 0x200) + add_mem_bar(dev_audio, 0x1C, "MBBAR", 0x100) +end + + + +function enable_hook_i82801dx() + enable_hook(io_hooks, filter_lpc_lock) + enable_smbus_host_bar() + enable_power_bars() + enable_audio_bars() +end + + + diff --git a/SerialICE/simba/interface.lua b/SerialICE/simba/interface.lua new file mode 100644 index 0000000..48ff8de --- /dev/null +++ b/SerialICE/simba/interface.lua @@ -0,0 +1,161 @@ + +-- IO, MMIO, RAM and ROM access +io_action = {} +mem_action = {} + +IO_READ = false +IO_WRITE = true +MEM_READ = false +MEM_WRITE = true + +-- CPUID and CPU MSR +MSR_READ = false +MSR_WRITE = true +CPUID = false + +cpu_action = {} +cpu_action.rin = {} +cpu_action.rout = {} + + + +-- SerialICE_io_read_filter is the filter function for IO reads. +-- +-- Parameters: +-- addr IO port to be read +-- size Size of the IO 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 + +function SerialICE_io_read_filter(addr, size) + pre_action(io_action, IO_READ, addr, size, 0) + walk_pre_hooks(io_hooks, io_action) + return io_action.to_hw, io_action.to_qemu +end + +-- Parameters: +-- data Data from hw or Qemu +-- Return values: +-- result Data to give back to Qemu + +function SerialICE_io_read_log(data) + post_action(io_action, data) + walk_post_hooks(io_hooks, io_action) + return io_action.data +end + +-- SerialICE_io_write_filter is the filter function for IO writes. +-- +-- Parameters: +-- addr IO port to be written to +-- size Size of the IO write +-- data Data to be written to +-- 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 +-- data Data to be written (possible changed in filter) + +function SerialICE_io_write_filter(addr, size, data) + pre_action(io_action, IO_WRITE, addr, size, data) + walk_pre_hooks(io_hooks, io_action) + return io_action.to_hw, io_action.to_qemu, io_action.data +end + +function SerialICE_io_write_log() + post_action(io_action) + walk_post_hooks(io_hooks, io_action) +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 + +function SerialICE_memory_read_filter(addr, size) + pre_action(mem_action, MEM_READ, addr, size, 0) + walk_pre_hooks(mem_hooks, mem_action) + return mem_action.to_hw, mem_action.to_qemu +end + +function SerialICE_memory_read_log(data) + post_action(mem_action, data) + walk_post_hooks(mem_hooks, mem_action) + return mem_action.data +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) + pre_action(mem_action, MEM_WRITE, addr, size, data) + walk_pre_hooks(mem_hooks, mem_action) + return mem_action.to_hw, mem_action.to_qemu, mem_action.data +end + +function SerialICE_memory_write_log() + post_action(mem_action, 0) + walk_post_hooks(mem_hooks, mem_action) +end + +function SerialICE_msr_read_filter(addr) + pre_action(cpu_action, MSR_READ, 0, 0, 0) + load_regs(cpu_action.rin, 0, 0, addr, 0) + + walk_pre_hooks(cpumsr_hooks, cpu_action) + return cpu_action.to_hw, cpu_action.to_qemu +end + +function SerialICE_msr_read_log(hi, lo) + local rout = cpu_action.rout + post_action(cpu_action, 0) + load_regs(cpu_action.rout, lo, 0, 0, hi) + walk_post_hooks(cpumsr_hooks, cpu_action) + return rout.edx, rout.eax +end + +function SerialICE_msr_write_filter(addr, hi, lo) + local rin = cpu_action.rin + pre_action(cpu_action, MSR_WRITE, 0, 0, 0) + load_regs(cpu_action.rin, lo, 0, addr, hi) + walk_pre_hooks(cpumsr_hooks, cpu_action) + return cpu_action.to_hw, cpu_action.to_qemu, rin.edx, rin.eax +end + +function SerialICE_msr_write_log() + post_action(cpu_action, 0) + load_regs(cpu_action.rout, 0, 0, 0, 0) + walk_post_hooks(cpumsr_hooks, cpu_action) +end + +function SerialICE_cpuid_filter(eax, ecx) + pre_action(cpu_action, CPUID, 0, 0, 0) + load_regs(cpu_action.rin, eax, 0, ecx, 0) + walk_pre_hooks(cpuid_hooks, cpu_action) + return cpu_action.to_hw, cpu_action.to_qemu +end + +function SerialICE_cpuid_log(eax, ebx, ecx, edx) + local rout = cpu_action.rout + post_action(cpu_action, 0) + load_regs(cpu_action.rout, eax, ebx, ecx, edx) + walk_post_hooks(cpuid_hooks, cpu_action) + return rout.eax, rout.ebx, rout.ecx, rout.edx +end + diff --git a/SerialICE/simba/leftover.lua b/SerialICE/simba/leftover.lua new file mode 100644 index 0000000..05e516e --- /dev/null +++ b/SerialICE/simba/leftover.lua @@ -0,0 +1,136 @@ + + + -- ********************************************************** + -- + -- Dell 1850 BMC filter + + if action.addr == 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 action.addr == 0xcfc then + if is_pci_io_cfg(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 action.addr == 0x80 and action.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 ( action.addr == 0xed and action.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 ( action.addr == 0xed and action.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 + + -- ********************************************************** + -- high memory write + + if action.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 + + + -- ********************************************************** + -- + -- io_read hooks, unknown vendor + + -- if action.addr == 0x42 then + -- printf("WARNING: Hijacking timer action.addr 0x42\n") + -- data = 0x80 + -- caught = true + -- end + + -- + -- + + if ( action.addr == 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 + + + +-- ********************************************************** +-- +-- Vendor specific Cache-As-Ram regions + +printks(froot, "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..df476d2 --- /dev/null +++ b/SerialICE/simba/memory.lua @@ -0,0 +1,239 @@ +-- ********************************************************** +-- + +function mem_undefined(f, action) + if (action.write) then + action.to_hw = true + action.to_qemu = false + else + action.to_hw = false + action.to_qemu = true + end + action.undefined = true + return true +end + +function mem_post(f, action) + if (action.write) then + printk(f, action, "write%s %08x <= %s", size_suffix(action.size), action.addr, size_data(action.size, action.data)) + else + printk(f, action, " read%s %08x => %s", size_suffix(action.size), action.addr, size_data(action.size, action.data)) + end + if action.to_hw then + printf(" *") + end + printf("\n") + return true +end + +function mem_base_post(f, action) + if (action.write) then + printk(f, action, "[%08x] <= %s\n", bit.band(action.addr, (f.size - 1)), size_data(action.size, action.data)) + else + printk(f, action, "[%08x] => %s\n", bit.band(action.addr, (f.size - 1)), size_data(action.size, action.data)) + end + return true +end + + +filter_mem_fallback = { + id = -1, + name = "MEM", + pre = mem_undefined, + post = mem_post, + base = 0x0, + size = 0x100000000 +} + +-- ********************************************************** +-- ROM access + +function mem_qemu_rom_pre(f, action) + action.to_hw = false + action.to_qemu = true + -- Reads from ROM space do not count for filter change. + if not action.write then + ignore_action(f, action) + end + return true +end + +function mem_rom_post(f, action) + if not action.write then + return true + end + -- Writes to ROM space fall-thru to the fallback filter, + -- so they get logged there. + return false +end + +filter_rom_low = { + id = -1, + name = "ROM_LO", + pre = mem_qemu_rom_pre, + post = mem_rom_post, + hide = hide_rom_access, + base = 0xE0000, + size = 0x20000 +} +filter_rom_high = { + id = -1, + name = "ROM_HI", + pre = mem_qemu_rom_pre, + post = mem_rom_post, + hide = hide_rom_access, + base = rom_base, + size = rom_size, +} + +-- ********************************************************** +-- CAR access + +function car_qemu_only(f, action) + action.to_hw = false + action.to_qemu = true + return true +end + +function new_car_region(start, size) + f = {} + f.id = -1 + f.name = "CAR" + f.start = start + f.size = size + f.pre = car_qemu_only + f.post = mem_post + enable_hook(mem_hooks, f) + SerialICE_register_physical(start, size) +end + + +-- ********************************************************** +-- RAM access. + +-- 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 + +-- This is handled by SerialICE but *NOT* exclusively. +-- Writes end up in Qemu memory, too +function mem_ram_low(f, action) + if ram_is_initialized then + -- RAM init is done. Send all RAM accesses + -- to Qemu. Using the target as storage would + -- only slow execution down. + action.to_hw = false + action.to_qemu = true + else + -- RAM init has not been marked done yet. + -- so send reads to the target only. + action.to_hw = true + action.to_qemu = action.write + end + return true +end + +-- SMI/VGA writes go to target and qemu +-- SMI/VGA reads come from target +function mem_smi_vga(f, action) + if action.write then + action.to_hw = true + action.to_qemu = true + else + action.to_hw = true + action.to_qemu = false + end + return true +end + + +function mem_post_pre_ram_only(f, action) + return ram_is_initialized +end + +filter_ram_low = { + id = -1, + name = "MEM", + pre = mem_ram_low, + post = mem_post_pre_ram_only, + hide = true, + base = 0x0, + size = 0xa0000 +} + +filter_smi_vga = { + id = -1, + name = "SMI_VGA", + pre = mem_smi_vga, + post = mem_post, + hide = true, + base = 0x000a0000, + size = 0x00010000, +} + +filter_ram_low_2 = { + id = -1, + name = "MEM", + pre = mem_ram_low, + post = mem_post_pre_ram_only, + hide = true, + base = 0xc0000, + size = 0x20000 +} + + +function mem_target_only(f, action) + action.to_hw = true + action.to_qemu = false + return true +end + +-- 3.25GB RAM. This is handled by SerialICE. +-- FIXME: use TOLM here + +-- We refrain from backing up this memory in Qemu because Qemu would +-- need lots of ram on the host and firmware usually does not intensively +-- use high memory anyways. +filter_ram_high = { + id = -1, + name = "MEM", + pre = mem_target_only, + post = mem_post_pre_ram_only, + hide = true, + base = 0x100000, + size = 0xd0000000 - 0x100000 +} + + +function ram_enabled() + return ram_is_initialized +end + +function enable_ram() + + enable_hook(mem_hooks, filter_ram_low) + enable_hook(mem_hooks, filter_smi_vga) + enable_hook(mem_hooks, filter_ram_low_2) + enable_hook(mem_hooks, filter_ram_high) + + -- Register low RAM 0x00000000 - 0x000dffff + SerialICE_register_physical(0x00000000, 0xa0000) + -- SMI/VGA memory should go to the target... + SerialICE_register_physical(0x000c0000, 0x20000) + --printf("Low RAM accesses are now directed to Qemu.\n") + ram_is_initialized = true +end + + + + diff --git a/SerialICE/simba/mmio.lua b/SerialICE/simba/mmio.lua new file mode 100644 index 0000000..7f08f4f --- /dev/null +++ b/SerialICE/simba/mmio.lua @@ -0,0 +1,83 @@ + +-- ********************************************************** +-- +-- Vendor independent X86 memory mapped IO + +-- Local APIC +-- We should avoid that someone wakes up cores +-- on the target system that go wild. +function mem_lapic(f, action) + if bit.band(action.addr, f.size-1) == 0x300 then + -- replace Start-Up IPI with Init IPI + if action.write and bit.band(action.data, 0xf0f00) == 0xc0600 then + return fake_action(f, action, 0xc0500) + end + end + return handle_action(f, action) +end + +filter_lapic = { + id = -1, + name = "LAPIC", + pre = mem_lapic, + post = mem_base_post, + hide = true, + base = 0xfee00000, + size = 0x00100000, +} + +-- IOAPIC +filter_ioapic = { + id = -1, + name = "IOAPIC", + pre = mem_target_only, + post = mem_base_post, + hide = true, + base = 0xfec00000, + size = 0x00100000, +} + + +-- ********************************************************** +-- Intel 82945 PCIe BAR + +function i945_pcie_bar(dev, reg, base) + -- size is hard coded 64k for now. + pcie_mm_cfg_bar(bit.band(0xfc000000, base) % 0x100000000, 64 * 1024) +end + +dev_i945 = { + pci_dev = pci_bdf(0,0,0,0), + name = "i945", + bar = {}, +} + +function enable_pcie_mm_cfg() + add_mem_bar(dev_i945, 0x48, i945_pcie_bar) +end + +-- ********************************************************** +-- +-- Intel chipset BARs are exclusively handled by the +-- SerialICE target + +filter_intel_bars = { + id = -1, + name = "IntelBAR", + pre = mem_target_only, + post = mem_base_post, + base = 0xfed10000, + size = 0x00010000, +} + +-- ICH7 TPM +-- Phoenix "Secure" Core bails out if we don't pass the read on ;-) +filter_ich7_tpm = { + id = -1, + name = "ICH7 TPM", + pre = mem_target_only, + post = mem_base_post, + base = 0xfed40000, + size = 0x00005000, +} + diff --git a/SerialICE/simba/output.lua b/SerialICE/simba/output.lua new file mode 100644 index 0000000..49e0650 --- /dev/null +++ b/SerialICE/simba/output.lua @@ -0,0 +1,81 @@ + + +froot = { + id = 0, + name = "SerialICE", +} + +fresource = { + id = -1, + name = "Resource", +} + +-- ------------------------------------------------------------------- +-- logging functions + +function printf(s,...) + return io.write(s:format(...)) +end + +function printk(f, action, fmt, ...) + if ip_logging then + printf("[%04x:%04x] ", action.cs, action.eip) + end + + if ip_logging then + printf("%04x.%04x ", action.parent_id, action.my_id) + end + + local str = " " + if action.dropped or action.faked then + str = "!" + end + if action.undefined then + str = "#" + end + + if action.f then + printf("%s %s,%s: ", str, f.name, action.f.name) + printf(fmt, ...) + else + printf("%s %s: ", str, f.name) + printf(fmt, ...) + end +end + +function printks(f, fmt, ...) + if ip_logging then + printf("[%04x:%04x] ", 0, 0) + end + + if ip_logging then + printf("%04x:%04x ", 0, 0) + end + printf(" %s: ", f.name) + printf(fmt, ...) +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 + + + diff --git a/SerialICE/simba/pc80.lua b/SerialICE/simba/pc80.lua new file mode 100644 index 0000000..c8f7618 --- /dev/null +++ b/SerialICE/simba/pc80.lua @@ -0,0 +1,473 @@ +-- ********************************************************** +-- +-- Debug POST port at IO 0x80 + +function debugport_post(f, action) + if action.write then + printk(f, action, "*** %02x ***\n", action.data) + return true + end + return false +end + +filter_debugport = { + id = -1, + name = "POST", + pre = handle_action, + post = debugport_post, + hide = true, + base = 0x80, + size = 0x1 +} + + +-- ********************************************************** +-- +-- i8259 PIC + +function i8259_pre(f, action) + local master = (bit.rshift(0x05, action.addr) == 0x1) + local slave = (bit.rshift(0x05, action.addr) == 0x5) + local reg = bit.band(0x03, action.addr) + if reg == 0 or reg == 1 then + return handle_action(f, action) + end + return skip_filter(f, action) +end + +function i8259_post(f,action) + local reg = bit.band(0x03, action.addr) + if reg == 0 or reg == 1 then + return true + end + return false +end + +filter_i8259_master = { + id = -1, + name = "i8259 A", + pre = i8259_pre, + post = i8259_post, + hide = hide_i8259_io, + base = 0x20, + size = 0x20 +} + +filter_i8259_slave = { + id = -1, + name = "i8259 B", + pre = i8259_pre, + post = i8259_post, + hide = hide_i8259_io, + base = 0xa0, + size = 0x20 +} + + +-- ********************************************************** +-- +-- i8237 DMA + +function i8237_pre(f, action) + if action.addr == 0x80 then + return skip_filter(f, action) + end + return handle_action(f, action) +end + +function i8237_post(f, action) + if action.addr == 0x80 then + return false + end + return true +end + + +filter_i8237_a = { + id = -1, + name = "i8237 A", + pre = i8237_pre, + post = i8237_post, + hide = hide_i8237_io, + base = 0x00, + size = 0x20 +} +filter_i8237_b = { + id = -1, + name = "i8237 B", + pre = i8237_pre, + post = i8237_post, + hide = hide_i8237_io, + base = 0x80, + size = 0x20 +} +filter_i8237_c = { + id = -1, + name = "i8237 C", + pre = i8237_pre, + post = i8237_post, + hide = hide_i8237_io, + base = 0xc0, + size = 0x20 +} + + +-- ********************************************************** +-- +-- i8254 IRQ0 and Speaker + +function i8254_pre(f, action) + + -- nothing to do on reads + if not action.write then + return handle_action(f, action) + end + + local reg = bit.band(0x03, action.addr) + if reg >= 0x0 and reg < 0x03 then + local counter_n = 0 + local counter_p = 0 + if f.counter[reg].lsb then + f.counter[reg].lsb = f.counter[reg].after_lsb + counter_n = action.data + counter_p = bit.band(0xff00, f.counter[reg].init) + else + counter_n = bit.lshift(action.data, 8) + counter_p = bit.band(0x00ff, f.counter[reg].init) + end + f.counter[reg].init = bit.bor(counter_n, counter_p) + elseif reg == 0x03 then + local reg2 = bit.rshift(action.data, 6) + local rwsel = bit.band(0x3, bit.rshift(action.data, 4)) + if reg2 == 0x3 then + if bit.band(0x10, action.data) == 0 then + f.counter[0].readback = (bit.band(0x2, action.data) ~= 0) + f.counter[1].readback = (bit.band(0x4, action.data) ~= 0) + f.counter[2].readback = (bit.band(0x8, action.data) ~= 0) + end + if bit.band(0x20, action.data) == 0 then + f.counter[0].latch = (bit.band(0x2, action.data) ~= 0) + f.counter[1].latch = (bit.band(0x4, action.data) ~= 0) + f.counter[2].latch = (bit.band(0x8, action.data) ~= 0) + end + elseif rwsel == 0x0 then + f.counter[reg2].latch = true + else + f.counter[reg2].mode = bit.band(0xf, action.data) + if rwsel == 0x1 then + f.counter[reg2].lsb = true + f.counter[reg2].after_lsb = true + elseif rwsel == 0x2 then + f.counter[reg2].lsb = false + f.counter[reg2].after_lsb = false + elseif rwsel == 03 then + f.counter[reg2].lsb = true + f.counter[reg2].after_lsb = false + end + end + end + return handle_action(f, action) +end + +function i8254_post(f, action) + local reg = bit.band(0x03, action.addr) + if reg >= 0x0 and reg < 0x03 then + if action.write then + local mode = bit.band(0x0f, f.counter[reg].mode); + local modestr = "Mode" .. mode + if mode == 0x4 then + modestr = "Square Wave" + elseif mode == 0x6 then + modestr = "Rate Generator" + end + if bit.band(0x01, mode) ~= 0 then + modestr = modestr .. " (BCD)" + end + + local period = 838 * f.counter[reg].init + if reg == 0 then + if period == 0 then + printk(f, action, "IRQ0 disabled\n") + else + printk(f, action, "IRQ0 (%s): %d ns\n", modestr, period) + end + elseif reg == 1 then + if period == 0 then + printk(f, action, "Refresh disabled\n") + else + printk(f, action, "Refresh (%s): %d ns\n", modestr, period) + end + elseif reg == 2 then + if period ~= 0 then + local spktone = 1193000 / f.counter[reg].init + printk(f, action, "Speaker Tone (%s): %d kHz\n", modestr, spktone) + end + end + else + if f.counter[reg].readback then + f.counter[reg].readback = false + f.counter[reg].status = action.data + printk(f, action, "[%d] status = %02x\n", reg, f.counter[reg].status) + end + if f.counter[reg].latch then + f.counter[reg].latch = false + f.counter[reg].current = action.data + printk(f, action, "[%d] current = %d\n", reg, f.counter[reg].current) + end + end + elseif reg == 0x03 then + end + return true +end + +i8254_counters = {} +i8254_counters[0x0] = { init=0, current=0, latch, readback, status=0 } +i8254_counters[0x1] = { init=0, current=0, latch, readback, status=0 } +i8254_counters[0x2] = { init=0, current=0, latch, readback, status=0 } + +filter_i8254_a = { + id = -1, + name = "i8254 A", + pre = i8254_pre, + post = i8254_post, + base = 0x40, + hide = hide_i8254_io, + size = 4, + counter = i8254_counters, +} +filter_i8254_b = { + id = -1, + name = "i8254 B", + pre = i8254_pre, + post = i8254_post, + base = 0x50, + hide = hide_i8254_io, + size = 4, + counter = i8254_counters, +} + + +-- ********************************************************** +-- +-- i8042 KBD, A20, Reset(?) + +function i8042_write(f, action) + if action.addr == 0x60 then + f.reg.data = action.data + f.reg.sts = bit.band(f.reg.sts, 0xf7) + if (f.reg.cmd == 0xd1) then + f.reg.A20 = (bit.band(0x02, action.data) == 0x02) + end + return handle_action(f, action) + end + if action.addr == 0x64 then + f.reg.cmd = action.data + f.reg.sts = bit.bor(f.reg.sts, 0x0a) + return handle_action(f, action) + end + return skip_filter(f, action) +end + +function i8042_read(f, action) + if action.addr == 0x60 then + f.reg.sts = bit.band(f.reg.sts, 0xfe) + return handle_action(f, action) + end + if action.addr == 0x64 then + return handle_action(f, action) + end + return skip_filter(f, action) +end + +function i8042_pre(f, action) + if (action.write) then + return i8042_write(f, action) + else + return i8042_read(f, action) + end +end + +function i8042_post(f, action) + if action.addr == 0x60 then + if action.write and f.reg.cmd == 0xd1 then + if f.reg.A20 then + printk(f, action, "A20 enabled\n") + else + printk(f, action, "A20 disabled\n") + end + end + return true + end + if action.addr == 0x64 then + return true + end + return false +end + +filter_i8042 = { + id = -1, + name = "i8042", + pre = i8042_pre, + post = i8042_post, + hide = hide_i8042_io, + base = 0x60, + size = 0x5, + reg = { data = 0, sts = 0, cmd = 0, A20 = 0 } +} + + +-- ********************************************************** +-- +-- CMOS nvram + +function nvram_write(f, action) + local val = action.data + local rtc = false + if action.addr == 0x70 then + f.reg.p70 = bit.band(0x7f, val) + if f.reg.p70 < 0x0E then + rtc = true + end + elseif action.addr == 0x71 then + f.nvram_data[f.reg.p70] = val + f.nvram_set[f.reg.p70] = true + if f.reg.p70 < 0x0E then + rtc = true + end + elseif action.addr == 0x72 then + f.reg.p72 = bit.band(0x7f, val) + elseif action.addr == 0x73 then + local index = 0x80 + f.reg.p72 + f.nvram_data[index] = val + f.nvram_set[index] = true + end + if cache_nvram and not rtc then + return fake_action(f, action, val) + else + return handle_action(f, action) + end +end + +function nvram_read(f, action) + local val = 0 + local rtc = false + if action.addr == 0x70 then + -- NMI returned as 0 + val = f.reg.p70 + if f.reg.p70 < 0x0E then + rtc = true + end + elseif action.addr == 0x71 then + if f.reg.p70 < 0x0E then + rtc = true + elseif f.nvram_set[f.reg.p70] then + val = f.nvram_data[f.reg.p70] + end + elseif action.addr == 0x72 then + -- NMI returned as 0 + val = f.reg.p72 + elseif action.addr == 0x73 then + local index = 0x80 + f.reg.p72 + if f.nvram_set[index] then + val = f.nvram_data[index] + end + end + if cache_nvram and not rtc then + return fake_action(f, action, val) + else + return handle_action(f, action) + end +end + +function nvram_pre(f, action) + if (action.write) then + return nvram_write(f, action) + else + return nvram_read(f, action) + end +end + +function nvram_post(f, action) + if (action.write) then + if action.addr == 0x71 then + printk(f, action, "[%02x] <= %02x\n", f.reg.p70, action.data) + elseif action.addr == 0x73 then + printk(f, action, "[%02x] <= %02x\n", 0x80 + f.reg.p72, action.data) + end + else + if action.addr == 0x71 then + printk(f, action, "[%02x] => %02x\n", f.reg.p70, action.data) + elseif action.addr == 0x73 then + printk(f, action, "[%02x] => %02x\n", 0x80 + f.reg.p72, action.data) + end + end + return true +end + +filter_nvram = { + id = -1, + name = "NVram", + pre = nvram_pre, + post = nvram_post, + base = 0x70, + size = 4, + hide = hide_nvram_io, + reg = { p70 = 0, p72 = 0 }, + nvram_data = {}, + nvram_set = {}, +} + + +-- ********************************************************** +-- +-- Reset at 0xcf9 + +function sys_rst_pre(f, action) + if action.size == 1 then + if action.write and bit.band(action.data, 0x04) == 0x04 then + SerialICE_system_reset() + end + return handle_action(f, action) + end + return skip_filter(f, action) +end + +function sys_rst_post(f, action) + if action.size == 1 then + if action.write then + printk(f, action, "Control = %02x\n", action.data) + return true + end + end + return false +end + +filter_reset = { + id = -1, + name = "Reset", + pre = sys_rst_pre, + post = sys_rst_post, + hide = false, + base = 0xcf9, + size = 1 +} + + +-- ********************************************************** +-- +-- Enable all PC80 stuff + +function enable_hook_pc80() + enable_hook(io_hooks, filter_i8237_a) + enable_hook(io_hooks, filter_i8237_b) + enable_hook(io_hooks, filter_i8237_c) + enable_hook(io_hooks, filter_i8259_master) + enable_hook(io_hooks, filter_i8259_slave) + enable_hook(io_hooks, filter_i8042) + enable_hook(io_hooks, filter_i8254_a) + enable_hook(io_hooks, filter_i8254_b) + enable_hook(io_hooks, filter_reset) + enable_hook(io_hooks, filter_nvram) + enable_hook(io_hooks, filter_debugport) +end + diff --git a/SerialICE/simba/serialice.lua b/SerialICE/simba/serialice.lua new file mode 100644 index 0000000..cccfcda --- /dev/null +++ b/SerialICE/simba/serialice.lua @@ -0,0 +1,118 @@ +-- 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") + + +-- Set to "false" to show undecoded IO accesses +-- for the specified class +hide_pci_io_cfg = true +hide_pci_mm_cfg = true +hide_superio_cfg = true +hide_nvram_io = true +hide_smbus_io = true +hide_mainboard_io = true +hide_i8254_io = true +hide_i8042_io = false +hide_i8237_io = true +hide_i8259_io = true + +-- Set to "true" to log every memory and IO access +log_everything = false + + + +-- Use lua table for NVram +-- RTC registers 0x0-0xd go to HW +cache_nvram = false + +-- SMSC 0x07, Winbond 0x06 ? +DEFAULT_SUPERIO_LDN_REGISTER = 0x07 + +-- Set to "false" to log access to ROM regions + +hide_rom_access = true + +-- Set to "true" to log CS:IP for each access + +ip_logging = true + + +rom_size = 4 * 1024 * 1024 +rom_base = 0x100000000 - rom_size + + +-- -------------------------------------------------------------------- +-- This initialization is executed right after target communication +-- has been established + +dofile("interface.lua") +dofile("output.lua") +dofile("hooks.lua") +dofile("core_io.lua") +dofile("memory.lua") +dofile("cpu.lua") +dofile("superio.lua") +dofile("pc80.lua") + +function do_minimal_setup() + enable_hook(io_hooks, filter_io_fallback) + enable_hook(mem_hooks, filter_mem_fallback) + enable_hook(cpumsr_hooks, filter_cpumsr_fallback) + enable_hook(cpuid_hooks, filter_cpuid_fallback) + enable_hook(mem_hooks, filter_rom_low) + enable_hook(mem_hooks, filter_rom_high) +end + +function do_default_setup() + enable_ram() + enable_hook(mem_hooks, filter_lapic) + enable_hook(mem_hooks, filter_ioapic) + enable_hook(io_hooks, filter_pci_io_cfg) + enable_hook_pc80() + enable_hook_superio(0x2e, DEFAULT_SUPERIO_LDN_REGISTER) + enable_hook_superio(0x4e, DEFAULT_SUPERIO_LDN_REGISTER) + enable_hook(io_hooks, filter_com1) +end + +mainboard_file = string.format("%s.lua", string.lower(string.gsub(SerialICE_mainboard, " ", "_"))) +local mainboard_lua = loadfile(mainboard_file) +if (mainboard_lua) then + mainboard_lua() + printks(froot, "Mainboard script %s initialized.\n", mainboard_file) + do_minimal_setup() + do_mainboard_setup() +else + printks(froot, "Mainboard script %s not found.\n", mainboard_file) + do_minimal_setup() + do_default_setup() +end + +printks(froot, "LUA script initialized.\n") + +return true + diff --git a/SerialICE/simba/superio.lua b/SerialICE/simba/superio.lua new file mode 100644 index 0000000..a856961 --- /dev/null +++ b/SerialICE/simba/superio.lua @@ -0,0 +1,200 @@ + + +function pnp_switch_ldn(f, data) + if not f.ldn[data] then + f.ldn[data] = { data = {}, set = {} } + end + f.pnp.active_ldn = data +end + +function pnp_select_cfg(f, data) + f.pnp.reg = data +end + +function pnp_store_cfg(f, data) + local ldn = f.pnp.active_ldn + local reg = f.pnp.reg + f.ldn[ldn].data[reg] = data; + f.ldn[ldn].set[reg] = true; +end + + +-- ********************************************************** +-- +-- SuperIO device handling + +function superio_pnpdev(f) + return string.format("%s %02x:%02x", f.name, f.base, f.pnp.active_ldn) +end + +function superio_enable_io(f, iobase) + local ldn = f.pnp.active_ldn + if not f.bar[ldn] then + f.bar[ldn] = {} + end + f.bar[ldn].name = superio_pnpdev(f) + f.bar[ldn].base = iobase + local size = 0 + if ldn == 0xa then + size = 0x80 + f.bar[ldn].name = "SIO_GPIO" + elseif ldn == 0x9 then + size = 0x1 + end + f.bar[ldn].size = size + generic_io_bar(f.bar[ldn]) +end + +function superio_dev_post(f, action) + printk(f, action, "%02x:%02x CFG ", f.base, f.pnp.active_ldn) +end + +function superio_read_post(f, data) + printf("[%02x] => %02x\n", f.pnp.reg, data) +end +function superio_write_post(f, data) + printf("[%02x] <= %02x\n", f.pnp.reg, data) +end + + +function superio_pre(f, action) + if not action.write then + return handle_action(f, action) + end + + if action.addr == f.base then + pnp_select_cfg(f, action.data) + return handle_action(f, action) + end + + if action.addr == f.base + 0x01 then + -- Also creates new LDN instance, if necessary. + if f.pnp.reg == f.pnp.ldn_register then + pnp_switch_ldn(f, action.data) + end + + pnp_store_cfg(f, action.data) + + -- Don't allow that our SIO power gets disabled. + if f.pnp.reg == 0x02 then + return drop_action(f, action, 0) + end + + -- Don't mess with oscillator setup. + if f.pnp.reg == 0x24 then + return drop_action(f, action, 0) + end + return handle_action(f, action) + end + + -- should not reach here + return skip_filter(f, action) +end + +function superio_post(f, action) + + -- Do not log change of register or LDN. + if action.addr == f.base or f.pnp.reg == f.pnp.ldn_register then + return true + end + + if not action.write then + superio_dev_post(f, action) + superio_read_post(f, action.data) + return true + end + + local ldn = f.ldn[f.pnp.active_ldn] + + -- Log base address once both bytes are set. + if f.pnp.reg == 0x30 or f.pnp.reg == 0x60 or f.pnp.reg == 0x61 then + if ldn.set[0x30] and 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]) + superio_enable_io(f, iobase) + elseif f.pnp.reg == 0x30 then + superio_dev_post(f, action) + printf("enabled\n") + end + elseif f.pnp.reg == 0x30 and ldn.set[0x30]==true and ldn.data[0x30]==0x0 then + superio_dev_post(f, action) + printf("disabled\n") + end + return true + end + + if f.pnp.reg == 0x70 then + superio_dev_post(f, action) + printf("irq = %d\n", ldn.data[0x70]) + return true + end + if f.pnp.reg == 0x72 then + superio_dev_post(f, action) + printf("irq2 = %d\n", ldn.data[0x72]) + return true + end + + superio_dev_post(f, action) + superio_write_post(f, action.data) + return true +end + + +filter_superio_2e = { + id = -1, + name = "PnP", + pre = superio_pre, + post = superio_post, + base = 0x2e, + size = 0x02, + hide = hide_superio_cfg, + pnp = { reg = 0, active_ldn = -1, ldn_register = 0 }, + ldn = {}, + bar = {}, +} +filter_superio_4e = { + id = -1, + name = "PnP", + pre = superio_pre, + post = superio_post, + base = 0x4e, + size = 0x02, + hide = hide_superio_cfg, + pnp = { reg = 0, active_ldn = -1, ldn_register = 0 }, + ldn = {}, + bar = {}, +} + +function enable_hook_superio(base, ldn_register) + if base == 0x2e then + filter_superio_2e.pnp.ldn_register = ldn_register + pnp_switch_ldn(filter_superio_2e, 0) + enable_hook(io_hooks, filter_superio_2e) + elseif base == 0x4e then + filter_superio_4e.pnp.ldn_register = ldn_register + pnp_switch_ldn(filter_superio_4e, 0) + enable_hook(io_hooks, filter_superio_4e) + end +end + +-- ********************************************************** +-- +-- Serial Port handling + +function com_pre(f, action) + if (action.write) then + return drop_action(f, action, action.data) + else + return drop_action(f, action, 0xff) + end +end + +filter_com1 = { + id = -1, + name = "COM1", + pre = com_pre, + post = io_post, + base = 0x3f8, + size = 8, + hide = false, +}