Removed silly aligned page allocation calls. Fixed contiguous memory. Added basic MMIO. Added initial GPU class.

This commit is contained in:
Cody Brocious 2016-05-19 19:23:32 -06:00
parent 2aeb66c4ab
commit 21f002ac5e
15 changed files with 236 additions and 54 deletions

View file

@ -12,13 +12,12 @@ Box::Box() {
hypercall = new Hypercall();
hm = new HandleManager();
pm = new PageManager();
pm->add_region(0, 64 * 1024 * 1024);
tm = new ThreadManager();
io = new IOManager();
debugger = new Debugger();
gpu = new Gpu();
}

View file

@ -7,6 +7,7 @@ public:
Box();
Cpu *cpu;
Gpu *gpu;
Hypercall *hypercall;
HandleManager *hm;
PageManager *pm;

77
Cpu.cpp
View file

@ -53,17 +53,35 @@ Cpu::Cpu(uint8_t *ram, uint8_t *kram) {
hv->reg(CR4, 0x2000);
}
void Cpu::map_pages(uint32_t virt, uint32_t phys, uint32_t count) {
void Cpu::map_pages(uint32_t virt, uint32_t phys, uint32_t count, bool present) {
auto dir = (uint32_t *) (mem + 64*1024*1024);
for(auto i = 0; i < count; ++i) {
auto table = (uint32_t *) (mem + (dir[virt >> 22] & ~0xFFF));
table[(virt >> 12) & 0x3ff] = phys | 0x7;
table[(virt >> 12) & 0x3ff] = phys | 0x6 | (present ? 1 : 0);
virt += 4096;
phys += 4096;
}
hv->invalidate_tlb(); // Do we really need to do this all the time?
}
void Cpu::map_io(uint32_t base, uint32_t pages, MMIOReceiver *recv) {
auto memblock = new uint8_t[pages * 4096];
hv->map_phys(memblock, base, pages * 4096);
for(auto i = 0; i < pages; ++i) {
mmio[base] = recv;
recv->buffers[base] = memblock;
map_pages(base, base, 1, false); // Pages are not marked present
base += 4096;
memblock += 4096;
}
}
void Cpu::flip_page(uint32_t base, bool val) {
auto dir = (uint32_t *) (mem + 64*1024*1024);
auto table = (uint32_t *) (mem + (dir[base >> 22] & ~0xFFF));
table[(base >> 12) & 0x3ff] = (table[(base >> 12) & 0x3ff] & ~1) | (val ? 1 : 0);
}
uint32_t Cpu::virt2phys(uint32_t addr) {
auto cr3 = hv->reg(CR3);
if(cr3 == 0)
@ -150,6 +168,7 @@ void Cpu::run(uint32_t eip) {
auto last_time = systime();
auto swap = true;
uint32_t in_mmio;
do {
if(break_in) {
box->debugger->enter();
@ -173,12 +192,36 @@ void Cpu::run(uint32_t eip) {
case 1: { // Single step
auto flags = hv->reg(EFLAGS);
hv->reg(EFLAGS, flags & ~(1 << 8));
if(single_step == 2) { // Requested
single_step = 0;
box->debugger->enter();
switch(single_step) {
case 1: { // Debugger requested
box->debugger->reenable();
break;
}
case 2: { // User requested
single_step = 0;
box->debugger->enter();
break;
}
case 3: { // MMIO Read
auto page = in_mmio & ~0xFFF;
flip_page(page, false);
single_step = 0;
in_mmio = 0;
break;
}
case 4: { // MMIO Write
auto page = in_mmio & ~0xFFF;
flip_page(page, false);
auto dev = mmio[page];
auto buf = dev->buffers[page];
auto off = in_mmio & 0xFFF;
volatile auto val = *((uint32_t *) ((uint8_t *) buf + off));
dev->write(in_mmio, val);
single_step = 0;
in_mmio = 0;
break;
}
}
else
box->debugger->reenable();
swap = true;
break;
}
@ -188,8 +231,24 @@ void Cpu::run(uint32_t eip) {
break;
}
case 14: {
cout << format("Page fault reading %08x") % exit.address << endl;
stop = true;
auto page = exit.address & ~0xFFF;
if(IN(page, mmio)) {
auto off = exit.address & 0xFFF;
auto dev = mmio[page];
auto buf = dev->buffers[page];
auto write = FLAG(exit.error_code, 2);
in_mmio = exit.address;
flip_page(page, true);
if(write) {
single_step = 4;
} else {
*((uint32_t *) ((uint8_t *) buf + off)) = dev->read(exit.address);
single_step = 3;
}
} else {
cout << format("Page fault reading %08x") % exit.address << endl;
stop = true;
}
break;
}
default:

14
Cpu.hpp
View file

@ -2,12 +2,22 @@
#include "Zookeeper.hpp"
class MMIOReceiver {
public:
virtual uint32_t read(uint32_t addr) = 0;
virtual void write(uint32_t addr, uint32_t value) = 0;
map<uint32_t, void *> buffers;
};
class Cpu {
public:
Cpu(uint8_t *ram, uint8_t *kram);
~Cpu();
void run(uint32_t eip);
void map_pages(uint32_t virt, uint32_t phys, uint32_t count);
void map_pages(uint32_t virt, uint32_t phys, uint32_t count, bool present=true);
void map_io(uint32_t base, uint32_t pages, MMIOReceiver *recv);
void flip_page(uint32_t base, bool val);
uint32_t virt2phys(uint32_t addr);
bool is_mapped(uint32_t addr);
@ -32,4 +42,6 @@ public:
int single_step = 0;
bool stop = false;
bool break_in = false;
map<uint32_t, MMIOReceiver *> mmio;
};

16
Gpu.cpp Normal file
View file

@ -0,0 +1,16 @@
#include "Zookeeper.hpp"
Gpu::Gpu() {
box->cpu->map_io(0xFD000000, 16, (MMIOReceiver *) this);
box->cpu->map_io(0xFD100000, 1, (MMIOReceiver *) this);
box->cpu->map_io(0xFD600000, 1, (MMIOReceiver *) this);
}
uint32_t Gpu::read(uint32_t addr) {
cout << format("Gpu::read(0x%08x)") % addr << endl;
return 0;
}
void Gpu::write(uint32_t addr, uint32_t value) {
cout << format("Gpu::write(0x%08x, 0x%08x)") % addr % value << endl;
}

10
Gpu.hpp Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include "Zookeeper.hpp"
class Gpu : public MMIOReceiver {
public:
Gpu();
uint32_t read(uint32_t addr);
void write(uint32_t addr, uint32_t value);
};

1
HV.hpp
View file

@ -29,6 +29,7 @@ enum HVExitReason {
typedef struct exit {
HVExitReason reason;
uint32_t error_code;
uint32_t instruction_length;
uint32_t interrupt_no;
uint32_t address; // Used for page faults

View file

@ -20,8 +20,14 @@ void Hypercall::log_(uint32_t message) {
uint32_t Hypercall::map(uint32_t virt_base, uint32_t count) {
return box->pm->map(virt_base, count);
}
uint32_t Hypercall::map_aligned(uint32_t virt_base, uint32_t count) {
return box->pm->map(virt_base, count, true);
uint32_t Hypercall::map_contiguous(uint32_t virt_base, uint32_t phys_low, uint32_t phys_high, uint32_t count) {
return box->pm->map_contiguous(virt_base, phys_low, phys_high, count);
}
uint32_t Hypercall::query_map_size(uint32_t base) {
auto count = 0;
while(box->cpu->is_mapped(base + count * 4096))
count++;
return count * 4096;
}
void Hypercall::unmap(uint32_t virt_base, uint32_t count) {
box->pm->unmap(virt_base, count);

View file

@ -165,6 +165,7 @@ exit_t HVMac::enter() {
switch(exit_reason) {
case VMX_REASON_EXC_NMI: {
auto vec_val = rvmcs(VMCS_RO_VMEXIT_IRQ_INFO) & 0xFFFF;
_exit.error_code = rvmcs(VMCS_RO_VMEXIT_IRQ_ERROR);
switch((vec_val >> 8) & 7) {
case 6: {
_exit.reason = Interrupt;

View file

@ -8,8 +8,17 @@ void NTAPI kernel_MmPersistContiguousMemory(
//log("Ignore MmPersistContiguousMemory");
}
PVOID NTAPI kernel_MmAllocateContiguousMemory(IN ULONG NumberOfBytes) {
return (PVOID) new uint8_t[NumberOfBytes];
void * NTAPI kernel_MmAllocateContiguousMemory(uint32_t NumberOfBytes) {
return kernel_MmAllocateContiguousMemoryEx(NumberOfBytes, 0, 0xFFFFFFFF, 0, 0);
}
void * NTAPI kernel_MmAllocateContiguousMemoryEx(
uint32_t NumberOfBytes,
uint32_t low, uint32_t high,
uint32_t unk, uint32_t flags
) {
NumberOfBytes = pagepad(NumberOfBytes);
return map_contiguous(0, low, high, NumberOfBytes / 4096);
}
NTSTATUS NTAPI kernel_NtAllocateVirtualMemory(
@ -22,12 +31,12 @@ NTSTATUS NTAPI kernel_NtAllocateVirtualMemory(
*BaseAddress = (void *) (((uint32_t) *BaseAddress) & ~0xFFF);
*RegionSize = pagepad(*RegionSize);
if((AllocationType & MEM_COMMIT) == MEM_COMMIT) {
*BaseAddress = map_aligned(*BaseAddress, *RegionSize / 4096);
*BaseAddress = map(*BaseAddress, *RegionSize / 4096);
} else if((AllocationType & MEM_RESERVE) == MEM_RESERVE) {
// We should just be reserving memory, but it doesn't matter.
// Commit will trash this region and we're probably leaking some.
// Future coders will deal with this problem.
*BaseAddress = map_aligned(*BaseAddress, *RegionSize / 4096);
*BaseAddress = map(*BaseAddress, *RegionSize / 4096);
} else {
bailout("Unsupported allocation type %x", AllocationType);
}
@ -47,3 +56,15 @@ NTSTATUS NTAPI kernel_NtFreeVirtualMemory(
return STATUS_SUCCESS;
}
uint32_t NTAPI kernel_MmQueryAllocationSize(void *base) {
// XXX: This won't return the size of a given allocation,
// but how many pages are mapped after that point.
// This might blow up sometime.
return query_map_size(base);
}
uint32_t NTAPI kernel_MmQueryAddressProtect(void *base) {
// XXX: Implement
return 0xFFFFFFFF;
}

View file

@ -6,7 +6,12 @@ void NTAPI kernel_MmPersistContiguousMemory(
IN ULONG NumberOfBytes,
IN BOOLEAN Persist
);
PVOID NTAPI kernel_MmAllocateContiguousMemory(IN ULONG NumberOfBytes);
void * NTAPI kernel_MmAllocateContiguousMemory(uint32_t NumberOfBytes);
void * NTAPI kernel_MmAllocateContiguousMemoryEx(
uint32_t NumberOfBytes,
uint32_t low, uint32_t high,
uint32_t unk, uint32_t flags
);
NTSTATUS NTAPI kernel_NtAllocateVirtualMemory(
void **BaseAddress,
@ -21,3 +26,6 @@ NTSTATUS NTAPI kernel_NtFreeVirtualMemory(
uint32_t *FreeSize,
uint32_t FreeType
);
uint32_t NTAPI kernel_MmQueryAllocationSize(void *base);
uint32_t NTAPI kernel_MmQueryAddressProtect(void *base);

View file

@ -16,9 +16,9 @@ void PageManager::add_region(uint32_t base, uint32_t size) {
freePhysPages.push_back(addr);
}
uint32_t PageManager::map(uint32_t base, uint32_t count, bool aligned) {
uint32_t PageManager::map(uint32_t base, uint32_t count) {
if(base == 0)
base = box->pm->alloc_virt(count, aligned);
base = box->pm->alloc_virt(count);
for(auto i = 0; i < count; ++i) {
auto virt = base + i * 4096;
@ -29,6 +29,16 @@ uint32_t PageManager::map(uint32_t base, uint32_t count, bool aligned) {
return base;
}
uint32_t PageManager::map_contiguous(uint32_t base, uint32_t phys_low, uint32_t phys_high, uint32_t count) {
if(base == 0)
base = box->pm->alloc_virt(count, 0x80000000); // D3D wants a higher half address
auto phys_base = box->pm->alloc_phys(count, phys_low, phys_high);
box->cpu->map_pages(base, phys_base, count);
return base;
}
void PageManager::unmap(uint32_t base, uint32_t count) {
auto addr = base;
for(auto i = 0; i < count; ++i) {
@ -38,49 +48,78 @@ void PageManager::unmap(uint32_t base, uint32_t count) {
box->pm->free_virt(base, count);
}
uint32_t PageManager::alloc_phys() {
bailout(freePhysPages.size() == 0);
uint32_t PageManager::alloc_phys(uint32_t count, uint32_t phys_low, uint32_t phys_high) {
bailout(freePhysPages.size() < count);
auto page = freePhysPages.front();
freePhysPages.pop_front();
return page;
if(count == 1) {
for(auto iter = freePhysPages.begin(); iter != freePhysPages.end(); ++iter) {
if(phys_low < *iter && phys_high >= (*iter + 4096)) {
auto page = *iter;
freePhysPages.erase(iter);
return page;
}
}
bailout(true); // Could not find page in range
} else {
for(auto base : freePhysPages) {
if(!(phys_low < base && (base + count * 4096) <= phys_high))
continue;
// This is the least efficient search ever.
auto found = true;
for(auto i = 1; i < count; ++i) {
if(find(freePhysPages.begin(), freePhysPages.end(), base + i * 4096) == freePhysPages.end()) {
found = false;
break;
}
}
if(found) {
for(auto i = 0; i < count; ++i) {
auto iter = find(freePhysPages.begin(), freePhysPages.end(), base + i * 4096);
freePhysPages.erase(iter);
}
return base;
}
}
bailout(true); // Could not find enough contiguous pages in range
}
}
void PageManager::free_phys(uint32_t page) {
freePhysPages.push_back(page);
}
uint32_t PageManager::alloc_virt(uint32_t count, bool aligned) {
uint32_t PageManager::alloc_virt(uint32_t count, uint32_t low) {
for(auto iter = virtGroups.begin(); iter != virtGroups.end(); ++iter) {
if(iter->count >= count) {
auto start = iter->start;
if(aligned && (start & 0xFFF) != 0) {
auto pad = start & 0xFFF;
if(iter->count == count + pad) {
iter->count = pad;
iter->end = iter->start + pad;
start += pad;
} else if(iter->count > count + pad) {
auto next = iter->count - (count + pad);
start += pad;
iter->count = pad;
iter->end = iter->start + pad;
virtgroup_t group;
group.count = next;
group.start = start + count;
group.end = start + count + next;
virtGroups.insert(++iter, group); // Add this group after the current one
} else
continue; // Too small to fix the padding.
} else {
if(iter->count >= count && iter->end > low + count) {
if(iter->start >= low) {
auto start = iter->start;
if(iter->count == count)
virtGroups.erase(iter);
else {
iter->count -= count;
iter->start += count * 4096;
}
return start;
} else {
auto off = low - iter->start;
auto coff = off / 4096;
auto start = low;
if(iter->count == coff + count) {
iter->count -= count;
iter->end -= count * 4096;
} else {
auto oend = iter->end;
auto ocount = iter->count;
iter->count = coff;
iter->end = low;
virtgroup_t group;
group.count = ocount - count - coff;
group.start = low + count * 4096;
group.end = oend;
virtGroups.insert(++iter, group);
}
return start;
}
return start;
}
}

View file

@ -11,13 +11,14 @@ public:
PageManager();
void add_region(uint32_t base, uint32_t size);
uint32_t map(uint32_t base, uint32_t count, bool aligned=false);
uint32_t map(uint32_t base, uint32_t count);
uint32_t map_contiguous(uint32_t base, uint32_t phys_low, uint32_t phys_high, uint32_t count);
void unmap(uint32_t base, uint32_t count);
uint32_t alloc_phys();
uint32_t alloc_phys(uint32_t count=1, uint32_t phys_low=0, uint32_t phys_high=0xFFFFFFFF);
void free_phys(uint32_t page);
uint32_t alloc_virt(uint32_t count, bool aligned=false);
uint32_t alloc_virt(uint32_t count, uint32_t low=0);
void free_virt(uint32_t start, uint32_t count);
void debug();

View file

@ -19,6 +19,8 @@ using namespace boost::algorithm;
#define pagepad(expr) (((expr) & 0xFFF) ? ((expr) & ~0xFFF) + 4096 : (expr))
#define IN(a, b) (((b).find(a)) != (b).end())
#define RAM_SIZE 128*1024*1024
#define KRAM_SIZE 128*1024*1024
@ -32,6 +34,7 @@ using namespace boost::algorithm;
#include "xbetypes.hpp"
#include "Xbe.hpp"
#include "Cpu.hpp"
#include "Gpu.hpp"
#include "HandleManager.hpp"
#include "PageManager.hpp"
#include "ThreadManager.hpp"

View file

@ -4,10 +4,15 @@ map:
- return: void *
- virt_base: void *
- count: uint32_t
map_aligned:
map_contiguous:
- return: void *
- virt_base: void *
- phys_low: uint32_t
- phys_high: uint32_t
- count: uint32_t
query_map_size:
- return: uint32_t
- base: void *
unmap:
- virt_base: void *
- count: uint32_t