Lua: memory.hash_region(), memory.store(), memory.storecmp()

This commit is contained in:
Ilari Liusvaara 2014-02-10 20:01:36 +02:00
parent 783d20f690
commit 0e0ca079fa
5 changed files with 224 additions and 17 deletions

View file

@ -179,6 +179,14 @@ public:
* Returns: True on success, false on failure.
*/
template<typename T> bool write(uint64_t address, T value);
/**
* Get physical mapping of region of memory.
*
* Parameter base: The base address.
* Parameter size: The size of region.
* Returns: Pointer to base on success, NULL on failure (range isn't backed by memory)
*/
char* get_physical_mapping(uint64_t base, uint64_t size);
/**
* Read a byte range (not across regions).
*

62
lua.lyx
View file

@ -6447,10 +6447,70 @@ Syntax: string memory.hash_region([string vma, ]number base, number size)
\end_layout
\begin_layout Standard
Hash specified number of bytes starting from specified address and return
Hash <size> bytes starting from address <base> (relative to <vma>) and return
the SHA-256.
\end_layout
\begin_layout Subsection
memory.hash_region2: Hash region of memory
\end_layout
\begin_layout Itemize
Syntax: string memory.hash_region2([string vma, ]number base, number size[,
number rows, number stride])
\end_layout
\begin_layout Standard
Hash <rows> blocks of <size> bytes starting from address <base> (relative
to <vma>).
The blocks are offset by <stride> from one another and return the SHA-256.
\end_layout
\begin_layout Subsection
memory.hash_region_skein: Hash region of memory
\end_layout
\begin_layout Itemize
Syntax: string memory.hash_region_skein([string vma, ]number base, number
size[, number rows, number stride])
\end_layout
\begin_layout Standard
Same as memory.hash_region2, but uses Skein-512-256 (v1.3; one of the SHA-3
finalists) as hash function.
\end_layout
\begin_layout Subsection
memory.store: Store region of memory
\end_layout
\begin_layout Itemize
Syntax: none memory.store([string vma, ]number addr, number daddr[, number
rows, number stride]
\end_layout
\begin_layout Standard
Copy memory starting from <addr> in memory area <vma> (each row being of
size <size>, there being <rows> rows, and rows being separated by <stride>
in memory) into savestate-saved memory area, starting from <daadr> (all
rows are written back to back).
\end_layout
\begin_layout Subsection
memory.storecmp: Compare and store region of memory
\end_layout
\begin_layout Itemize
Syntax: bool memory.storecmp([string vma, ]number addr, number daddr[, number
rows, number stride]
\end_layout
\begin_layout Standard
Like memory.store, but returns true if target of copy already held the value
that would be copied before the copy happened.
Otherwise returns false (if target and source differ before copy).
\end_layout
\begin_layout Subsection
memory.hash_state: Hash system state
\end_layout

BIN
lua.pdf

Binary file not shown.

View file

@ -261,6 +261,21 @@ std::list<memory_region*> memory_space::get_regions()
return r;
}
char* memory_space::get_physical_mapping(uint64_t base, uint64_t size)
{
uint64_t last = base + size - 1;
if(last < base)
return NULL; //Warps around.
auto g1 = lookup(base);
auto g2 = lookup(last);
if(g1.first != g2.first)
return NULL; //Not the same VMA.
if(!g1.first || !g1.first->direct_map)
return NULL; //Not mapped.
//OK.
return reinterpret_cast<char*>(g1.first->direct_map + g1.second);
}
void memory_space::set_regions(const std::list<memory_region*>& regions)
{
umutex_class m(mutex);

View file

@ -8,6 +8,7 @@
#include "core/rom.hpp"
#include "library/sha256.hpp"
#include "library/string.hpp"
#include "library/skein.hpp"
#include "library/minmax.hpp"
#include "library/hex.hpp"
#include "library/int24.hpp"
@ -499,31 +500,150 @@ namespace
return 1;
}
int hash_region(lua::state& L, lua::parameters& P)
template<typename H, void(*update)(H& state, const char* mem, size_t memsize),
std::string(*read)(H& state), bool extra>
int hash_core(H& state, lua::state& L, lua::parameters& P)
{
std::string hash;
uint64_t addr, size;
uint64_t stride = 0, rows = 1;
bool mappable = true;
char buffer[BLOCKSIZE];
addr = get_read_address(P);
P(size);
char buffer[BLOCKSIZE];
sha256 h;
while(size > BLOCKSIZE) {
for(size_t i = 0; i < BLOCKSIZE; i++)
buffer[i] = lsnes_memory.read<uint8_t>(addr + i);
h.write(buffer, BLOCKSIZE);
addr += BLOCKSIZE;
size -= BLOCKSIZE;
if(extra) {
P(P.optional(rows, 1));
if(rows > 1)
P(stride);
}
for(size_t i = 0; i < size; i++)
buffer[i] = lsnes_memory.read<uint8_t>(addr + i);
h.write(buffer, size);
hash = h.read();
L.pushlstring(hash.c_str(), 64);
uint64_t psize = rows ? ((rows - 1) * stride + size) : 0;
//Don't use mapping if range used warps around.
if(rows && stride && (psize - size) / stride != (rows - 1))
mappable = false;
char* pbuffer = mappable ? lsnes_memory.get_physical_mapping(addr, psize) : NULL;
if(!size && !rows) {
} else if(pbuffer) {
uint64_t offset = 0;
for(uint64_t i = 0; i < rows; i++) {
update(state, pbuffer + offset, size);
offset += stride;
}
} else {
uint64_t offset = 0;
for(uint64_t i = 0; i < rows; i++) {
size_t sz = size;
while(sz > 0) {
size_t ssz = min(sz, static_cast<size_t>(BLOCKSIZE));
for(size_t i = 0; i < ssz; i++)
buffer[i] = lsnes_memory.read<uint8_t>(addr + offset + i);
offset += ssz;
sz -= ssz;
update(state, buffer, ssz);
}
offset += (stride - size);
}
}
hash = read(state);
L.pushlstring(hash);
return 1;
}
void lua_sha256_update(sha256& s, const char* ptr, size_t size)
{
s.write(ptr, size);
}
std::string lua_sha256_read(sha256& s)
{
return s.read();
}
void lua_skein_update(skein::hash& s, const char* ptr, size_t size)
{
s.write(reinterpret_cast<const uint8_t*>(ptr), size);
}
std::string lua_skein_read(skein::hash& s)
{
uint8_t buf[32];
s.read(buf);
return hex::b_to(buf, 32, false);
}
template<bool extended>
int hash_region(lua::state& L, lua::parameters& P)
{
sha256 h;
return hash_core<sha256, lua_sha256_update, lua_sha256_read, extended>(h, L, P);
}
int hash_region_skein(lua::state& L, lua::parameters& P)
{
skein::hash h(skein::hash::PIPE_512, 256);
return hash_core<skein::hash, lua_skein_update, lua_skein_read, true>(h, L, P);
}
template<bool cmp>
int copy_to_host(lua::state& L, lua::parameters& P)
{
uint64_t addr, daddr, size;
uint64_t stride = 0, rows = 1;
bool equals = true, mappable = true;
addr = get_read_address(P);
P(daddr, size, P.optional(rows, 1));
if(rows > 1)
P(stride);
if(rows && size * rows / rows != size)
throw std::runtime_error("Size to copy too large");
if(daddr + rows * size < daddr)
throw std::runtime_error("Size to copy too large");
uint64_t psize = rows ? ((rows - 1) * stride + size) : 0;
//Don't use mapping if range used warps around.
if(rows && stride && (psize - size) / stride != (rows - 1))
mappable = false;
auto& h = movb.get_mfile().host_memory;
if(daddr + rows * size > h.size()) {
equals = false;
h.resize(daddr + rows * size);
}
char* pbuffer = mappable ? lsnes_memory.get_physical_mapping(addr, psize) : NULL;
if(!size && !rows) {
} else if(pbuffer) {
//Mapable.
uint64_t offset = 0;
for(uint64_t i = 0; i < rows; i++) {
bool eq = (cmp && !memcmp(&h[i * size], pbuffer + offset, size));
if(!eq)
memcpy(&h[i * size], pbuffer + offset, size);
equals &= eq;
offset += stride;
}
} else {
//Not mapable.
for(uint64_t i = 0; i < rows; i++) {
uint64_t addr1 = addr + i * stride;
uint64_t addr2 = daddr + i * size;
for(uint64_t j = 0; j < size; j++) {
uint8_t byte = lsnes_memory.read<uint8_t>(addr1 + j);
bool eq = (cmp && h[addr2 + j] == byte);
if(!eq)
h[addr2 + j] = byte;
equals &= eq;
}
}
}
if(cmp)
L.pushboolean(equals);
return cmp ? 1 : 0;
}
int readregion(lua::state& L, lua::parameters& P)
{
uint64_t addr, size;
@ -580,7 +700,11 @@ namespace
{"read_vma", read_vma},
{"find_vma", find_vma},
{"hash_state", hash_state},
{"hash_region", hash_region},
{"hash_region", hash_region<false>},
{"hash_region2", hash_region<true>},
{"hash_region_skein", hash_region_skein},
{"store", copy_to_host<false>},
{"storecmp", copy_to_host<true>},
{"readregion", readregion},
{"writeregion", readregion},
{"read_sg", memory_scattergather<false, false>},