mirror of
https://github.com/pound-emu/pound.git
synced 2025-12-11 07:36:57 +00:00
This new architecture decomposes the project into several distict static libraries: common, host, kvm, and frontend. By using static libraries, changes within one module will only require that library to be re-linked, rather than recompiling and re-linking the entire executable. The third party library ImGui is now built as a static library target. Signed-off-by: Ronald Caesar <github43132@proton.me>
459 lines
16 KiB
C++
459 lines
16 KiB
C++
#ifndef POUND_KVM_GUEST_H
|
|
#define POUND_KVM_GUEST_H
|
|
|
|
#include <cassert>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "endian.h"
|
|
|
|
#include "host/memory/arena.h"
|
|
|
|
namespace pound::kvm::memory
|
|
{
|
|
|
|
/*
|
|
* guest_memory_t - A non-owning descriptor for a block of guest physical RAM.
|
|
* @base: Pointer to the start of the host-allocated memory block.
|
|
* @size: The size of the memory block in bytes.
|
|
*
|
|
*
|
|
* This structure describes a contiguous block of guest physical memory. It acts
|
|
* as a handle or a "view" into a region of host memory, but it does not manage
|
|
* the lifetime of that memory itself.
|
|
*
|
|
* --- Ownership ---
|
|
* The guest_memory_t struct does NOT own the memory block pointed to by @base.
|
|
* Ownership of the underlying memory buffer is retained by the host memory
|
|
* arena from which it was allocated. The party responsible for creating the
|
|
* arena is also responsible for ultimately freeing it. This struct is merely a
|
|
* descriptor and can be safely passed by value or pointer without transferring
|
|
* ownership.
|
|
*
|
|
* --- Lifetime ---
|
|
* An instance of this struct should be considered valid only for as long as the
|
|
* backing memory arena is valid. Typically, this means it is created once
|
|
- * during virtual machine initialization and lives for the entire duration of
|
|
* the emulation session. Its lifetime is tied to the lifetime of the parent
|
|
* KVM instance.
|
|
*
|
|
* --- Invariants ---
|
|
* Both fields of this struct are declared `const`. This establishes the
|
|
* invariant that once a guest_memory_t descriptor is created and initialized
|
|
* by guest_memory_create(), its size and base address are immutable for the
|
|
* lifetime of the object. This prevents accidental resizing or repointing of
|
|
* the guest's physical RAM.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint8_t* const base;
|
|
const uint64_t size;
|
|
} guest_memory_t;
|
|
|
|
/*
|
|
* guest_memory_create() - Allocates and initializes a guest memory region from
|
|
* an arena.
|
|
* @arena: A pointer to a host memory arena that will be the source for all
|
|
* allocations.
|
|
*
|
|
* This function sets up the primary guest RAM block. It uses a provided host
|
|
* memory arena as the backing store for both the guest_memory_t descriptor
|
|
* struct and the guest RAM itself.
|
|
*
|
|
* The function first allocates a small chunk from the arena for the guest_memory_t
|
|
* struct. It then dedicates the *entire remaining capacity* of the arena to be
|
|
* the main guest RAM block.
|
|
*
|
|
* Preconditions:
|
|
* - @arena must be a valid, non-NULL pointer to an initialized host arena.
|
|
* - @arena->data must point to a valid, host-allocated memory buffer.
|
|
* - The arena provided should be dedicated solely to this guest memory block;
|
|
* its entire remaining capacity will be consumed.
|
|
*
|
|
* Return: A pointer to a fully initialized guest_memory_t struct. The `base`
|
|
* pointer will point to the start of the guest RAM block within the arena,
|
|
* and `size` will reflect the size of that block.
|
|
*/
|
|
guest_memory_t* guest_memory_create(pound::host::memory::arena_t* arena);
|
|
|
|
/*
|
|
* guest_mem_access_result_t - Defines the set of possible outcomes for a guest
|
|
* memory access operation.
|
|
* @GUEST_MEM_ACCESS_OK: The memory operation completed
|
|
* successfully.
|
|
* @GUEST_MEM_FAULT_UNALIGNED: The access was unaligned, and the
|
|
* emulated CPU requires an Alignment
|
|
* Fault to be raised. The operation was
|
|
* NOT completed. The host must inject a
|
|
* data abort into the guest.
|
|
* @GUEST_MEM_ACCESS_FAULT_BOUNDARY: An access fell outside the bounds of
|
|
* the defined memory region. The
|
|
* operation was NOT completed, The host
|
|
* must inject a Data Abort for a
|
|
* translation/permission fault into the
|
|
* guest.
|
|
* @GUEST_MEM_ACCESS_ERROR_INTERNAL: An unrecoverable internal error occured
|
|
* within the memory subsystem. This
|
|
* indicates a fatal host bug, not a guest
|
|
* induced fault.
|
|
*/
|
|
typedef enum
|
|
{
|
|
GUEST_MEM_ACCESS_OK = 0,
|
|
GUEST_MEM_ACCESS_FAULT_UNALIGNED,
|
|
GUEST_MEM_ACCESS_FAULT_BOUNDARY,
|
|
GUEST_MEM_ACCESS_ERROR_INTERNAL,
|
|
} guest_mem_access_result_t;
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Guest Memory Read Functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
/*
|
|
* guest_mem_readb() - Read one byte from guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to read from.
|
|
* @out_val: A pointer to a uint8_t where the result will be stored.
|
|
*
|
|
* This function safely reads a single 8-bit value from the guest's physical
|
|
* RAM. It performs a bounds check to ensure the access is within the allocated
|
|
* memory region.
|
|
*
|
|
* Preconditions:
|
|
* - @memory and @out_val must be valid, non-NULL pointers.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY if the @gpa is outside the valid memory
|
|
* range.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_readb(guest_memory_t* memory, uint64_t gpa, uint8_t* out_val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
assert(nullptr != out_val);
|
|
|
|
if (gpa >= memory->size)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
*out_val = *hva;
|
|
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_readw() - Read a 16-bit word from guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to read from.
|
|
* @out_val: A pointer to a uint16_t where the result will be stored.
|
|
*
|
|
* This function safely reads a 16-bit little-endian value from guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory and @out_val must be valid, non-NULL pointers.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 2-byte aligned.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 2-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_readw(guest_memory_t* memory, uint64_t gpa, uint16_t* out_val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
assert(nullptr != out_val);
|
|
|
|
if (gpa > (memory->size - sizeof(uint16_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 1) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
memcpy(out_val, hva, sizeof(uint16_t));
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
*out_val = bswap_16(*out_val);
|
|
#endif
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_readl() - Read a 32-bit long-word from guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to read from.
|
|
* @out_val: A pointer to a uint32_t where the result will be stored.
|
|
*
|
|
* This function safely reads a 32-bit little-endian value from guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory and @out_val must be valid, non-NULL pointers.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 4-byte aligned.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 4-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_readl(guest_memory_t* memory, uint64_t gpa, uint32_t* out_val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
assert(nullptr != out_val);
|
|
|
|
if (gpa > (memory->size - sizeof(uint32_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 3) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
memcpy(out_val, hva, sizeof(uint32_t));
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
*out_val = bswap_32(*out_val);
|
|
#endif
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_readq() - Read a 64-bit quad-word from guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to read from.
|
|
* @out_val: A pointer to a uint64_t where the result will be stored.
|
|
*
|
|
* This function safely reads a 64-bit little-endian value from guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory and @out_val must be valid, non-NULL pointers.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 8-byte aligned.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 8-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_readq(guest_memory_t* memory, uint64_t gpa, uint64_t* out_val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
assert(nullptr != out_val);
|
|
|
|
if (gpa > (memory->size - sizeof(uint64_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 7) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
memcpy(out_val, hva, sizeof(uint64_t));
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
*out_val = bswap_64(*out_val);
|
|
#endif
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Guest Memory Write Functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
/*
|
|
* guest_mem_writeb() - Write one byte to guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to write to.
|
|
* @val: The 8-bit value to write.
|
|
*
|
|
* This function safely writes a single 8-bit value to the guest's physical
|
|
* RAM. It performs a bounds check to ensure the access is within the allocated
|
|
* memory region before performing the write.
|
|
*
|
|
* Preconditions:
|
|
* - @memory must be a valid, non-NULL pointer.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY if the @gpa is outside the valid memory
|
|
* range.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_writeb(guest_memory_t* memory, uint64_t gpa, uint8_t val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
|
|
if (gpa >= memory->size)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
*hva = val;
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_writew() - Write a 16-bit word to guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to write to.
|
|
* @val: The 16-bit value to write.
|
|
*
|
|
* This function safely writes a 16-bit little-endian value to guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory must be a valid, non-NULL pointer.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 2-byte aligned.
|
|
*
|
|
* Return: %GUEST_MEM_ACCESS_OK on success. Returns
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 2-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_writew(guest_memory_t* memory, uint64_t gpa, uint16_t val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
|
|
if (gpa > (memory->size - sizeof(uint16_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 1) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
val = bswap_16(val);
|
|
#endif
|
|
|
|
memcpy(hva, &val, sizeof(uint16_t));
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_writel() - Write a 32-bit long-word to guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to write to.
|
|
* @val: The 32-bit value to write.
|
|
*
|
|
* This function safely writes a 32-bit little-endian value to guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory must be a valid, non-NULL pointer.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 4-byte aligned.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 4-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_writel(guest_memory_t* memory, uint64_t gpa, uint32_t val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
|
|
if (gpa > (memory->size - sizeof(uint32_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 3) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
val = bswap_32(val);
|
|
#endif
|
|
|
|
memcpy(hva, &val, sizeof(uint32_t));
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
|
|
/*
|
|
* guest_mem_writeq() - Write a 64-bit quad-word to guest physical memory.
|
|
* @memory: A pointer to the guest memory region.
|
|
* @gpa: The guest physical address to write to.
|
|
* @val: The 64-bit value to write.
|
|
*
|
|
* This function safely writes a 64-bit little-endian value to guest RAM.
|
|
* It performs both boundary and alignment checks before the access.
|
|
* It will also perform a byte swap if the host system is not little-endian.
|
|
*
|
|
* Preconditions:
|
|
* - @memory must be a valid, non-NULL pointer.
|
|
* - @memory->base must point to a valid, host-allocated memory buffer.
|
|
* - The guest address @gpa must be 8-byte aligned.
|
|
*
|
|
* Return:
|
|
* %GUEST_MEM_ACCESS_OK on success.
|
|
* %GUEST_MEM_ACCESS_FAULT_BOUNDARY on an out-of-bounds access or
|
|
* %GUEST_MEM_ACCESS_FAULT_UNALIGNED if @gpa is not 8-byte aligned.
|
|
*/
|
|
inline guest_mem_access_result_t guest_mem_writeq(guest_memory_t* memory, uint64_t gpa, uint64_t val)
|
|
{
|
|
assert(nullptr != memory);
|
|
assert(nullptr != memory->base);
|
|
|
|
if (gpa > (memory->size - sizeof(uint64_t)))
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_BOUNDARY;
|
|
}
|
|
|
|
if ((gpa & 7) != 0)
|
|
{
|
|
return GUEST_MEM_ACCESS_FAULT_UNALIGNED;
|
|
}
|
|
|
|
uint8_t* hva = memory->base + gpa;
|
|
|
|
#if HOST_IS_LITTLE_ENDIAN != GUEST_IS_LITTLE_ENDIAN
|
|
val = bswap_64(val);
|
|
#endif
|
|
|
|
memcpy(hva, &val, sizeof(uint64_t));
|
|
return GUEST_MEM_ACCESS_OK;
|
|
}
|
|
} // namespace pound::kvm::memory
|
|
#endif // POUND_KVM_GUEST_H
|