Lua: memory2: More functions corresponding to memory.*

This commit is contained in:
Ilari Liusvaara 2014-02-13 13:48:01 +02:00
parent a8fa742a36
commit d1176cbf9e
6 changed files with 480 additions and 74 deletions

View file

@ -173,8 +173,6 @@ public:
return tostring(hashout);
}
private:
sha256(const sha256& x) throw();
sha256& operator=(const sha256& x) throw();
uint32_t state[8];
uint32_t datablock[16];
unsigned blockbytes;

13
include/lua/debug.hpp Normal file
View file

@ -0,0 +1,13 @@
#ifndef _lua_debug__hpp__included__
#define _lua_debug__hpp__included__
#include "internal.hpp"
#include "core/debug.hpp"
template<debug_type type>
void handle_registerX(lua::state& L, uint64_t addr, int lfn);
template<debug_type type>
void handle_unregisterX(lua::state& L, uint64_t addr, int lfn);
#endif

135
lua.lyx
View file

@ -6909,6 +6909,141 @@ Write value <val> to given memory area <marea> at byte offsets <addr>..., given
True increments address by 1, and false decrements address by 1.
\end_layout
\begin_layout Subsection
memory2.<marea>:cheat: Set/Clear cheat
\end_layout
\begin_layout Itemize
Syntax: none memory2.<marea>:cheat(number addr, [number value])
\end_layout
\begin_layout Standard
Set/Clear cheat at offset <addr> of memory area <marea>.
If <value> is given, cheat with specified value is set.
Otherwise cheat on address is removed.
\end_layout
\begin_layout Subsection
memory2.<marea>:sha256: SHA-256
\end_layout
\begin_layout Itemize
Syntax: string memory2.<marea>:sha256(number addr, number size[, number rows,
number stride])
\end_layout
\begin_layout Standard
Compute SHA-256 of <rows> (default 1) chunks of <size> bytes each, starting
from offset <addr> of area <marea>.
The chunks are separated by <stride>.
\end_layout
\begin_layout Subsection
memory2.<marea>:skein: Skein-512-256
\end_layout
\begin_layout Itemize
Syntax: string memory2.<marea>:skein(number addr, number size[, number rows,
number stride])
\end_layout
\begin_layout Standard
Same as memory2.<marea>:sha256, except with Skein-512-256 as hash function.
\end_layout
\begin_layout Subsection
memory2.<marea>:store{,cmp}: Copy region to Lua memory with compare
\end_layout
\begin_layout Itemize
Syntax: none memory2.<marea>:store(number addr, number daddr, number size[,
number rows, number stride])
\end_layout
\begin_layout Itemize
Syntax: boolean memory2.<marea>:storecmp(number addr, number daddr, number
size[, number rows, number stride])
\end_layout
\begin_layout Standard
Copy <rows> (default 1) chunks of <size> bytes each, starting from offset
<addr> of area <marea>.
The chunks are separated by <stride>.
The target is Lua host memory, starting from offset <daddr>.
\end_layout
\begin_layout Standard
Additionally, the storecmp method returns false if target was modified (otherwis
e true).
\end_layout
\begin_layout Subsection
memory2.<marea>:readregion: Read region
\end_layout
\begin_layout Itemize
Syntax table memory2.<marea>:readregion(number addr, number size)
\end_layout
\begin_layout Standard
Read <size> bytes starting from <addr> in <marea> and return as array.
\end_layout
\begin_layout Subsection
memory2.<marea>:writeregion: Write region
\end_layout
\begin_layout Itemize
Syntax none memory2.<marea>:writeregion(number addr, table data)
\end_layout
\begin_layout Standard
Write array <data> to bytes starting from <addr> in <marea>.
\end_layout
\begin_layout Subsection
memory2.<marea>:register{read,write,exec}: Register hook
\end_layout
\begin_layout Itemize
Syntax: function memory2.<marea>:registerread(number addr, function fn);
\end_layout
\begin_layout Itemize
Syntax: function memory2.<marea>:registerwrite(number addr, function fn);
\end_layout
\begin_layout Itemize
Syntax: function memory2.<marea>:registerexec(number addr, function fn);
\end_layout
\begin_layout Standard
Register debug callback <fn> of specified type at offset <addr> of memory
area <marea>.
Returns <fn>.
\end_layout
\begin_layout Subsection
memory2.<marea>:unregister{read,write,exec}: Unregister hook
\end_layout
\begin_layout Itemize
Syntax: none memory2.<marea>:unregisterread(number addr, function fn);
\end_layout
\begin_layout Itemize
Syntax: none memory2.<marea>:unregisterwrite(number addr, function fn);
\end_layout
\begin_layout Itemize
Syntax: none memory2.<marea>:unregisterexec(number addr, function fn);
\end_layout
\begin_layout Standard
Unregister debug callback <fn> of specified type at offset <addr> of memory
area <marea>.
\end_layout
\begin_layout Standard
\begin_inset Newpage pagebreak
\end_inset

BIN
lua.pdf

Binary file not shown.

View file

@ -244,81 +244,84 @@ namespace
return;
}
}
}
template<debug_type type, bool reg>
void handle_registerX(lua::state& L, uint64_t addr, int lfn)
{
auto& cbl = cbs[addr];
template<debug_type type>
void handle_registerX(lua::state& L, uint64_t addr, int lfn)
{
auto& cbl = cbs[addr];
//Put the context in userdata so it can be gc'd when Lua context is terminated.
lua_debug_callback* D = (lua_debug_callback*)L.newuserdata(sizeof(lua_debug_callback));
L.newtable();
L.pushstring("__gc");
L.pushcclosure(&lua_debug_callback::dtor, 0);
L.rawset(-3);
L.setmetatable(-2);
L.pushlightuserdata(&D->addr);
L.pushvalue(-2);
L.rawset(LUA_REGISTRYINDEX);
L.pop(1); //Pop the copy of object.
cbl.push_back(D);
D->dead = false;
D->addr = addr;
D->type = type;
D->lua_fn = L.topointer(lfn);
lua::state* LL = &L.get_master();
void* D2 = &D->type;
if(type != DEBUG_TRACE)
D->h = debug_add_callback(addr, type, [LL, D2](uint64_t addr, uint64_t value) {
LL->pushlightuserdata(D2);
LL->rawget(LUA_REGISTRYINDEX);
LL->pushnumber(addr);
LL->pushnumber(value);
do_lua_error(*LL, LL->pcall(2, 0, 0));
}, [LL, D]() {
LL->pushlightuserdata(&D->addr);
LL->pushnil();
LL->rawset(LUA_REGISTRYINDEX);
D->_dtor(LL->handle());
});
else
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str) {
LL->pushlightuserdata(D2);
LL->rawget(LUA_REGISTRYINDEX);
LL->pushnumber(proc);
LL->pushstring(str);
do_lua_error(*LL, LL->pcall(2, 0, 0));
}, [LL, D]() {
LL->pushlightuserdata(&D->addr);
LL->pushnil();
LL->rawset(LUA_REGISTRYINDEX);
D->_dtor(LL->handle());
});
L.pushlightuserdata(D2);
L.pushvalue(lfn);
//Put the context in userdata so it can be gc'd when Lua context is terminated.
lua_debug_callback* D = (lua_debug_callback*)L.newuserdata(sizeof(lua_debug_callback));
L.newtable();
L.pushstring("__gc");
L.pushcclosure(&lua_debug_callback::dtor, 0);
L.rawset(-3);
L.setmetatable(-2);
L.pushlightuserdata(&D->addr);
L.pushvalue(-2);
L.rawset(LUA_REGISTRYINDEX);
L.pop(1); //Pop the copy of object.
cbl.push_back(D);
D->dead = false;
D->addr = addr;
D->type = type;
D->lua_fn = L.topointer(lfn);
lua::state* LL = &L.get_master();
void* D2 = &D->type;
if(type != DEBUG_TRACE)
D->h = debug_add_callback(addr, type, [LL, D2](uint64_t addr, uint64_t value) {
LL->pushlightuserdata(D2);
LL->rawget(LUA_REGISTRYINDEX);
LL->pushnumber(addr);
LL->pushnumber(value);
do_lua_error(*LL, LL->pcall(2, 0, 0));
}, [LL, D]() {
LL->pushlightuserdata(&D->addr);
LL->pushnil();
LL->rawset(LUA_REGISTRYINDEX);
D->_dtor(LL->handle());
});
else
D->h = debug_add_trace_callback(addr, [LL, D2](uint64_t proc, const char* str) {
LL->pushlightuserdata(D2);
LL->rawget(LUA_REGISTRYINDEX);
LL->pushnumber(proc);
LL->pushstring(str);
do_lua_error(*LL, LL->pcall(2, 0, 0));
}, [LL, D]() {
LL->pushlightuserdata(&D->addr);
LL->pushnil();
LL->rawset(LUA_REGISTRYINDEX);
D->_dtor(LL->handle());
});
L.pushlightuserdata(D2);
L.pushvalue(lfn);
L.rawset(LUA_REGISTRYINDEX);
}
template<debug_type type>
void handle_unregisterX(lua::state& L, uint64_t addr, int lfn)
{
if(!cbs.count(addr))
return;
auto& cbl = cbs[addr];
for(auto i = cbl.begin(); i != cbl.end(); i++) {
if((*i)->type != type) continue;
if(L.topointer(lfn) != (*i)->lua_fn) continue;
L.pushlightuserdata(&(*i)->type);
L.pushnil();
L.rawset(LUA_REGISTRYINDEX);
(*i)->_dtor(L.handle());
//Lua will GC the object.
break;
}
}
template<debug_type type, bool reg>
void handle_unregisterX(lua::state& L, uint64_t addr, int lfn)
{
if(!cbs.count(addr))
return;
auto& cbl = cbs[addr];
for(auto i = cbl.begin(); i != cbl.end(); i++) {
if((*i)->type != type) continue;
if(L.topointer(lfn) != (*i)->lua_fn) continue;
L.pushlightuserdata(&(*i)->type);
L.pushnil();
L.rawset(LUA_REGISTRYINDEX);
(*i)->_dtor(L.handle());
//Lua will GC the object.
break;
}
}
namespace
{
template<debug_type type, bool reg>
int lua_registerX(lua::state& L, lua::parameters& P)
{
@ -334,11 +337,11 @@ namespace
P(P.function(lfn));
if(reg) {
handle_registerX<type, reg>(L, addr, lfn);
handle_registerX<type>(L, addr, lfn);
L.pushvalue(lfn);
return 1;
} else {
handle_unregisterX<type, reg>(L, addr, lfn);
handle_unregisterX<type>(L, addr, lfn);
return 0;
}
}

View file

@ -1,10 +1,12 @@
#include "lua/internal.hpp"
#include "lua/debug.hpp"
#include "core/memorymanip.hpp"
#include "core/memorywatch.hpp"
#include "core/moviedata.hpp"
#include "core/moviefile.hpp"
#include "core/rom.hpp"
#include "library/sha256.hpp"
#include "library/skein.hpp"
#include "library/string.hpp"
#include "library/serialization.hpp"
#include "library/minmax.hpp"
@ -53,6 +55,12 @@ namespace
int info(lua::state& L, lua::parameters& P);
template<class T, bool _bswap> int rw(lua::state& L, lua::parameters& P);
template<bool write, bool sign> int scattergather(lua::state& L, lua::parameters& P);
template<class T> int hash(lua::state& L, lua::parameters& P);
template<bool cmp> int storecmp(lua::state& L, lua::parameters& P);
int readregion(lua::state& L, lua::parameters& P);
int writeregion(lua::state& L, lua::parameters& P);
int cheat(lua::state& L, lua::parameters& P);
template<debug_type type, bool reg> int registerX(lua::state& L, lua::parameters& P);
std::string print()
{
return vma;
@ -78,6 +86,40 @@ namespace
}
};
struct l_sha256_h
{
static sha256 create()
{
return sha256();
}
static void write(sha256& h, void* b, size_t s)
{
h.write(reinterpret_cast<uint8_t*>(b), s);
}
static std::string read(sha256& h)
{
return h.read();
}
};
struct l_skein_h
{
static skein::hash create()
{
return skein::hash(skein::hash::PIPE_512, 256);
}
static void write(skein::hash& h, void* b, size_t s)
{
h.write(reinterpret_cast<uint8_t*>(b), s);
}
static std::string read(skein::hash& h)
{
uint8_t buf[32];
h.read(buf);
return hex::b_to(buf, 32);
}
};
lua::_class<lua_vma> class_vma(lua_class_memory, "VMA", {}, {
{"info", &lua_vma::info},
{"read", &lua_vma::scattergather<false, false>},
@ -107,6 +149,19 @@ namespace
{"iqword", &lua_vma::rw<uint64_t, true>},
{"ifloat", &lua_vma::rw<float, true>},
{"idouble", &lua_vma::rw<double, true>},
{"cheat", &lua_vma::cheat},
{"sha256", &lua_vma::hash<l_sha256_h>},
{"skein", &lua_vma::hash<l_skein_h>},
{"store", &lua_vma::storecmp<false>},
{"storecmp", &lua_vma::storecmp<true>},
{"readregion", &lua_vma::readregion},
{"writeregion", &lua_vma::writeregion},
{"registerread", &lua_vma::registerX<DEBUG_READ, true>},
{"unregisterread", &lua_vma::registerX<DEBUG_READ, false>},
{"registerwrite", &lua_vma::registerX<DEBUG_WRITE, true>},
{"unregisterwrite", &lua_vma::registerX<DEBUG_WRITE, false>},
{"registerexec", &lua_vma::registerX<DEBUG_EXEC, true>},
{"unregisterexec", &lua_vma::registerX<DEBUG_EXEC, false>},
}, &lua_vma::print);
lua::_class<lua_vma_list> class_vmalist(lua_class_memory, "VMALIST", {
@ -192,6 +247,208 @@ namespace
return write ? 0 : 1;
}
template<class T> int lua_vma::hash(lua::state& L, lua::parameters& P)
{
uint64_t addr, size, rows, stride = 0;
bool equals = true;
P(P.skipped(), addr, size, P.optional(rows, 1));
if(rows > 1) P(stride);
//First verify that all reads are to the region.
uint64_t tmp = addr;
if(size > vmasize && rows)
throw std::runtime_error("Region out of range");
for(uint64_t i = 0; i < rows; i++) {
if(tmp >= vmasize || tmp + size > vmasize)
throw std::runtime_error("Region out of range");
tmp += stride;
}
auto hstate = T::create();
//Try to map the VMA.
char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
if(vmabuf) {
for(uint64_t i = 0; i < rows; i++) {
T::write(hstate, vmabuf + addr, size);
addr += stride;
}
} else {
uint8_t buf[512]; //Must be power of 2.
unsigned bf = 0;
for(uint64_t i = 0; i < rows; i++) {
for(uint64_t j = 0; j < size; j++) {
buf[bf] = lsnes_memory.read<uint8_t>(vmabase + addr + j);
bf = (bf + 1) & (sizeof(buf) - 1);
if(!bf)
T::write(hstate, buf, sizeof(buf));
}
addr += stride;
}
if(bf)
T::write(hstate, buf, bf);
}
L.pushlstring(T::read(hstate));
return 1;
}
int lua_vma::readregion(lua::state& L, lua::parameters& P)
{
uint64_t addr, size;
P(P.skipped(), addr, size);
if(addr >= vmasize || size > vmasize || addr + size > vmasize)
throw std::runtime_error("Read out of range");
L.newtable();
char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
if(vmabuf) {
uint64_t ctr = 1;
for(size_t i = 0; i < size; i++) {
L.pushnumber(ctr++);
L.pushnumber(static_cast<unsigned char>(vmabuf[addr + i]));
L.settable(-3);
}
} else {
uint64_t ctr = 1;
for(size_t i = 0; i < size; i++) {
L.pushnumber(ctr++);
L.pushnumber(lsnes_memory.read<uint8_t>(addr + i));
L.settable(-3);
}
}
return 1;
}
int lua_vma::writeregion(lua::state& L, lua::parameters& P)
{
uint64_t addr;
int ltbl;
P(P.skipped(), addr, P.table(ltbl));
auto g = lsnes_memory.lookup(vmabase);
if(!g.first || g.first->readonly)
throw std::runtime_error("Memory address is read-only");
if(addr >= vmasize)
throw std::runtime_error("Write out of range");
uint64_t ctr = 1;
char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
if(vmabuf) {
for(size_t i = 0;; i++) {
L.pushnumber(ctr++);
L.gettable(ltbl);
if(L.type(-1) == LUA_TNIL)
break;
if(addr + i >= vmasize)
throw std::runtime_error("Write out of range");
vmabuf[addr + i] = L.tointeger(-1);
L.pop(1);
}
} else {
for(size_t i = 0;; i++) {
L.pushnumber(ctr++);
L.gettable(ltbl);
if(L.type(-1) == LUA_TNIL)
break;
if(addr + i >= vmasize)
throw std::runtime_error("Write out of range");
lsnes_memory.write<uint8_t>(vmabase + addr + i, L.tointeger(-1));
L.pop(1);
}
}
}
template<bool cmp> int lua_vma::storecmp(lua::state& L, lua::parameters& P)
{
uint64_t addr, daddr, size, rows, stride = 0;
bool equals = true;
P(P.skipped(), addr, daddr, size, P.optional(rows, 1));
if(rows > 1) P(stride);
//First verify that all reads are to the region.
uint64_t tmp = addr;
if(size > vmasize && rows)
throw std::runtime_error("Source out of range");
for(uint64_t i = 0; i < rows; i++) {
if(tmp >= vmasize || tmp + size > vmasize)
throw std::runtime_error("Source out of range");
tmp += stride;
}
//Calculate new size of target.
auto& h = movb.get_mfile().host_memory;
size_t rsize = size * rows;
if(size && rsize / size != rows)
throw std::runtime_error("Copy size out of range");
if((size_t)daddr + rsize < rsize)
throw std::runtime_error("Target out of range");
if(daddr + rsize > h.size()) {
h.resize(daddr + rsize);
equals = false;
}
//Try to map the VMA.
char* vmabuf = lsnes_memory.get_physical_mapping(vmabase, vmasize);
if(vmabuf) {
for(uint64_t i = 0; i < rows; i++) {
bool eq = (cmp && !memcmp(&h[daddr], vmabuf + addr, size));
if(!eq)
memcpy(&h[daddr], vmabuf + addr, size);
equals &= eq;
addr += stride;
daddr += size;
}
} else {
for(uint64_t i = 0; i < rows; i++) {
for(uint64_t j = 0; j < size; j++) {
uint8_t byte = lsnes_memory.read<uint8_t>(vmabase + addr + j);
bool eq = (cmp && ((uint8_t)h[daddr + j] == byte));
h[daddr + j] = byte;
equals &= eq;
}
addr += stride;
daddr += size;
}
}
if(cmp) L.pushboolean(equals);
return cmp ? 1 : 0;
}
template<debug_type type, bool reg> int lua_vma::registerX(lua::state& L, lua::parameters& P)
{
uint64_t addr;
int lfn;
P(P.skipped(), addr, P.function(lfn));
if(reg) {
handle_registerX<type>(L, vmabase + addr, lfn);
L.pushvalue(lfn);
return 1;
} else {
handle_unregisterX<type>(L, vmabase + addr, lfn);
return 0;
}
}
int lua_vma::cheat(lua::state& L, lua::parameters& P)
{
uint64_t addr, value;
P(P.skipped(), addr);
if(addr >= vmasize)
throw std::runtime_error("Address out of range");
if(P.is_novalue()) {
debug_clear_cheat(vmabase + addr);
} else {
P(value);
debug_set_cheat(vmabase + addr, value);
}
return 0;
}
lua_vma_list::lua_vma_list(lua::state& L)
{
}