waterbox - expose read-only information about the guest memory page table

This commit is contained in:
nattthebear 2020-07-23 16:08:24 -04:00
parent e778e5248d
commit 1c606c1eb6
11 changed files with 153 additions and 30 deletions

Binary file not shown.

Binary file not shown.

View File

@ -437,5 +437,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
_exe.LoadStateBinary(reader);
_core.PostLoadState();
}
public MemoryDomain GetPagesDomain()
{
return _exe.GetPagesDomain();
}
}
}

View File

@ -73,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
MakeMemoryDomain("SGB CARTRAM", LibsnesApi.SNES_MEMORY.SGB_CARTRAM, MemoryDomain.Endian.Little);
}
_memoryDomainList.Add(Api.GetPagesDomain());
_memoryDomains = new MemoryDomainList(_memoryDomainList);
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);

View File

@ -94,6 +94,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
mm.Add(s68Bus);
}
mm.Add(_elf.GetPagesDomain());
_memoryDomains = new MemoryDomainList(mm) { SystemBus = m68Bus };
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);

View File

@ -73,8 +73,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
var primaryDomain = memoryDomains
.Where(md => md.Definition.Flags.HasFlag(LibWaterboxCore.MemoryDomainFlags.Primary))
.Single();
var mdl = new MemoryDomainList(memoryDomains.Cast<MemoryDomain>().ToList());
var mdl = new MemoryDomainList(
memoryDomains.Cast<MemoryDomain>()
.Concat(new[] { _exe.GetPagesDomain() })
.ToList()
);
mdl.MainMemory = primaryDomain;
_serviceProvider.Register<IMemoryDomains>(mdl);

View File

@ -218,11 +218,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
return ms.ToArray();
}
// public class MissingFileResult
// {
// public byte[] data;
// public bool writable;
// }
#if false
public class MissingFileResult
{
public byte[] data;
public bool writable;
}
/// <summary>
/// Can be set by the frontend and will be called if the core attempts to open a missing file.
@ -233,29 +234,65 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// if it was for firmware only.
/// writable == false is equivalent to AddReadonlyFile, writable == true is equivalent to AddTransientFile
/// </summary>
// public Func<string, MissingFileResult> MissingFileCallback
// {
// set
// {
// // TODO
// using (this.EnterExit())
// {
// var mfc_o = value == null ? null : new WaterboxHostNative.MissingFileCallback
// {
// callback = (_unused, name) =>
// {
// var res = value(name);
// }
// };
public Func<string, MissingFileResult> MissingFileCallback
{
set
{
// TODO
using (this.EnterExit())
{
var mfc_o = value == null ? null : new WaterboxHostNative.MissingFileCallback
{
callback = (_unused, name) =>
{
var res = value(name);
}
};
// NativeImpl.wbx_set_missing_file_callback(_activatedNativeHost, value == null
// ? null
// : )
// }
// }
// get => _syscalls.MissingFileCallback;
// set => _syscalls.MissingFileCallback = value;
// }
NativeImpl.wbx_set_missing_file_callback(_activatedNativeHost, value == null
? null
: )
}
}
get => _syscalls.MissingFileCallback;
set => _syscalls.MissingFileCallback = value;
}
#endif
public MemoryDomain GetPagesDomain()
{
return new WaterboxPagesDomain(this);
}
private class WaterboxPagesDomain : MemoryDomain
{
protected readonly WaterboxHost _host;
public WaterboxPagesDomain(WaterboxHost host)
{
_host = host;
var retobj = new ReturnData();
NativeImpl.wbx_get_page_len(_host._nativeHost, retobj);
Name = "Waterbox PageData";
Size = (long)retobj.GetDataOrThrow();
WordSize = 1;
EndianType = Endian.Unknown;
Writable = false;
}
public override byte PeekByte(long addr)
{
var retobj = new ReturnData();
NativeImpl.wbx_get_page_data(_host._nativeHost, Z.SU(addr), retobj);
return (byte)retobj.GetDataOrThrow();
}
public override void PokeByte(long addr, byte val)
{
throw new InvalidOperationException();
}
}
public void SaveStateBinary(BinaryWriter bw)
{

View File

@ -235,5 +235,24 @@ namespace BizHawk.Emulation.Cores.Waterbox
/// </summary>
[BizImport(CallingConvention.Cdecl)]
public abstract void wbx_set_always_evict_blocks(bool val);
/// <summary>
/// Retrieve the number of pages of guest memory that this host is tracking
/// </summary>
[BizImport(CallingConvention.Cdecl)]
public abstract void wbx_get_page_len(IntPtr /*WaterboxHost*/ obj, ReturnData /*UIntPtr*/ ret);
/// <summary>
/// Retrieve basic information for a tracked guest page. Index should be in 0..wbx_get_page_len().
/// 1 - readable, implies allocated
/// 2 - writable
/// 4 - executable
/// 0x10 - stack
/// 0x20 - allocated but not readable (guest-generated "guard")
/// 0x40 - invisible
/// 0x80 - dirty
/// </summary>
[BizImport(CallingConvention.Cdecl)]
public abstract void wbx_get_page_data(IntPtr /*WaterboxHost*/ obj, UIntPtr index, ReturnData /*byte*/ ret);
}
}

View File

@ -354,3 +354,26 @@ pub extern fn wbx_set_always_evict_blocks(_val: bool) {
unsafe { ALWAYS_EVICT_BLOCKS = _val; }
}
}
/// Retrieve the number of pages of guest memory that this host is tracking
#[no_mangle]
pub extern fn wbx_get_page_len(obj: &mut WaterboxHost, ret: &mut Return<usize>) {
ret.put(Ok(obj.page_len()))
}
/// Retrieve basic information for a tracked guest page. Index should be in 0..wbx_get_page_len().
/// 1 - readable, implies allocated
/// 2 - writable
/// 4 - executable
/// 0x10 - stack
/// 0x20 - allocated but not readable (guest-generated "guard")
/// 0x40 - invisible
/// 0x80 - dirty
#[no_mangle]
pub extern fn wbx_get_page_data(obj: &mut WaterboxHost, index: usize, ret: &mut Return<u8>) {
if index >= obj.page_len() {
ret.put(Err(anyhow!("Index out of range")))
} else {
ret.put(Ok(obj.page_info(index)))
}
}

View File

@ -154,6 +154,14 @@ impl WaterboxHost {
pub fn run_guest_simple(&mut self, entry_point: usize) {
context::call_guest_simple(entry_point, &mut self.context);
}
pub fn page_len(&self) -> usize {
self.memory_block.page_len()
}
pub fn page_info(&self, index: usize) -> u8 {
self.memory_block.page_info(index)
}
}
const SAVE_START_MAGIC: &str = "ActivatedWaterboxHost_v1";

View File

@ -492,6 +492,32 @@ impl MemoryBlock {
}
}
}
pub fn page_len(&self) -> usize {
self.pages.len()
}
pub fn page_info(&self, index: usize) -> u8 {
let p = &self.pages[index];
let mut res = match p.status {
PageAllocation::Free => 0,
PageAllocation::Allocated(prot) => match prot {
Protection::None => 0x20,
Protection::R => 1,
Protection::RW => 3,
Protection::RX => 5,
Protection::RWX => 7,
Protection::RWStack => 0x13,
}
};
if p.dirty {
res |= 0x80;
}
if p.invisible {
res |= 0x40;
}
res
}
}
impl Drop for MemoryBlock {