mirror of
https://github.com/wiiu-env/MemoryMappingModule.git
synced 2024-11-22 01:39:20 +01:00
frist commit
This commit is contained in:
commit
5d38198e41
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.cbp
|
||||
*.elf
|
||||
*.layout
|
||||
*.rpx
|
||||
build/
|
||||
*.save-failed
|
||||
.idea/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.wms
|
142
Makefile
Normal file
142
Makefile
Normal file
@ -0,0 +1,142 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
include $(DEVKITPRO)/wums/share/wums_rules
|
||||
|
||||
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||
WUT_ROOT := $(DEVKITPRO)/wut
|
||||
#-------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := MemoryMappingModule
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := source
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -g -Wall -O0 -ffunction-sections\
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=c++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(WUMSSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lwums -lwut -lkernel
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#-------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#-------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).rpx $(TARGET).elf
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#-------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).wms
|
||||
|
||||
$(OUTPUT).wms : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%.o: %.s
|
||||
@echo $(notdir $<)
|
||||
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
138
source/CThread.h
Normal file
138
source/CThread.h
Normal file
@ -0,0 +1,138 @@
|
||||
/****************************************************************************
|
||||
* Copyright (C) 2015 Dimok
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdint>
|
||||
#include <coreinit/thread.h>
|
||||
|
||||
class CThread {
|
||||
public:
|
||||
typedef void (* Callback)(CThread *thread, void *arg);
|
||||
|
||||
//! constructor
|
||||
CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = NULL, void *callbackArg = NULL)
|
||||
: pThread(NULL)
|
||||
, pThreadStack(NULL)
|
||||
, pCallback(callback)
|
||||
, pCallbackArg(callbackArg) {
|
||||
//! save attribute assignment
|
||||
iAttributes = iAttr;
|
||||
//! allocate the thread
|
||||
pThread = (OSThread*) memalign(8, 0x1000);
|
||||
//! allocate the stack
|
||||
pThreadStack = (uint8_t *) memalign(0x20, iStackSize);
|
||||
//! create the thread
|
||||
if(pThread && pThreadStack) {
|
||||
OSCreateThread(pThread, (int (*)(int, const char**)) &CThread::threadCallback, 1, (char*) this, (void*) (pThreadStack+iStackSize), iStackSize, iPriority, iAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
//! destructor
|
||||
virtual ~CThread() {
|
||||
shutdownThread();
|
||||
}
|
||||
|
||||
static CThread *create(CThread::Callback callback, void *callbackArg, int32_t iAttr = eAttributeNone, int32_t iPriority = 16, int32_t iStackSize = 0x8000) {
|
||||
return ( new CThread(iAttr, iPriority, iStackSize, callback, callbackArg) );
|
||||
}
|
||||
|
||||
//! Get thread ID
|
||||
virtual void* getThread() const {
|
||||
return pThread;
|
||||
}
|
||||
//! Thread entry function
|
||||
virtual void executeThread(void) {
|
||||
if(pCallback)
|
||||
pCallback(this, pCallbackArg);
|
||||
}
|
||||
//! Suspend thread
|
||||
virtual void suspendThread(void) {
|
||||
if(isThreadSuspended()) return;
|
||||
if(pThread) OSSuspendThread(pThread);
|
||||
}
|
||||
//! Resume thread
|
||||
virtual void resumeThread(void) {
|
||||
if(!isThreadSuspended()) return;
|
||||
if(pThread) OSResumeThread(pThread);
|
||||
}
|
||||
//! Set thread priority
|
||||
virtual void setThreadPriority(int32_t prio) {
|
||||
if(pThread) OSSetThreadPriority(pThread, prio);
|
||||
}
|
||||
//! Check if thread is suspended
|
||||
virtual bool isThreadSuspended(void) const {
|
||||
if(pThread) return OSIsThreadSuspended(pThread);
|
||||
return false;
|
||||
}
|
||||
//! Check if thread is terminated
|
||||
virtual bool isThreadTerminated(void) const {
|
||||
if(pThread) return OSIsThreadTerminated(pThread);
|
||||
return false;
|
||||
}
|
||||
//! Check if thread is running
|
||||
virtual bool isThreadRunning(void) const {
|
||||
return !isThreadSuspended() && !isThreadRunning();
|
||||
}
|
||||
|
||||
//! Gets the thread affinity.
|
||||
virtual uint16_t getThreadAffinity(void) const {
|
||||
if(pThread) return OSGetThreadAffinity(pThread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Shutdown thread
|
||||
virtual void shutdownThread(void) {
|
||||
//! wait for thread to finish
|
||||
if(pThread && !(iAttributes & eAttributeDetach)) {
|
||||
while(isThreadSuspended()) {
|
||||
resumeThread();
|
||||
}
|
||||
OSJoinThread(pThread, NULL);
|
||||
}
|
||||
//! free the thread stack buffer
|
||||
if(pThreadStack) {
|
||||
free(pThreadStack);
|
||||
}
|
||||
if(pThread) {
|
||||
free(pThread);
|
||||
}
|
||||
pThread = NULL;
|
||||
pThreadStack = NULL;
|
||||
}
|
||||
//! Thread attributes
|
||||
enum eCThreadAttributes {
|
||||
eAttributeNone = 0x07,
|
||||
eAttributeAffCore0 = 0x01,
|
||||
eAttributeAffCore1 = 0x02,
|
||||
eAttributeAffCore2 = 0x04,
|
||||
eAttributeDetach = 0x08,
|
||||
eAttributePinnedAff = 0x10
|
||||
};
|
||||
private:
|
||||
static int32_t threadCallback(int32_t argc, void *arg) {
|
||||
//! After call to start() continue with the internal function
|
||||
((CThread *) arg)->executeThread();
|
||||
return 0;
|
||||
}
|
||||
int32_t iAttributes;
|
||||
OSThread *pThread;
|
||||
uint8_t *pThreadStack;
|
||||
Callback pCallback;
|
||||
void *pCallbackArg;
|
||||
};
|
27
source/logger.h
Normal file
27
source/logger.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
|
||||
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
|
||||
OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||
} while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
|
||||
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||
} while (0);
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...)do { \
|
||||
WHBLogWritef("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
|
||||
} while (0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
45
source/main.cpp
Normal file
45
source/main.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <coreinit/debug.h>
|
||||
#include <cstddef>
|
||||
#include <malloc.h>
|
||||
#include <wums.h>
|
||||
#include <whb/log.h>
|
||||
#include <whb/log_udp.h>
|
||||
#include "memory_mapping.h"
|
||||
#include "logger.h"
|
||||
|
||||
WUMS_MODULE_EXPORT_NAME("homebrew_memorymapping");
|
||||
|
||||
WUMS_INITIALIZE(){
|
||||
WHBLogUdpInit();
|
||||
DEBUG_FUNCTION_LINE("Setting up memory mapping!");
|
||||
static uint8_t ucSetupRequired = 1;
|
||||
if (!ucSetupRequired){
|
||||
return;
|
||||
}
|
||||
ucSetupRequired = 0;
|
||||
|
||||
MemoryMapping::setupMemoryMapping();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MemoryMappingFree(void* ptr){
|
||||
MemoryMapping::free(ptr);
|
||||
}
|
||||
void* MemoryMappingAlloc(uint32_t size, uint32_t align){
|
||||
return MemoryMapping::alloc(size, align);
|
||||
}
|
||||
|
||||
uint32_t MemoryMappingEffectiveToPhysical(uint32_t address){
|
||||
return MemoryMapping::EffectiveToPhysical(address);
|
||||
}
|
||||
uint32_t MemoryMappingPhysicalToEffective(uint32_t address){
|
||||
return MemoryMapping::PhysicalToEffective(address);
|
||||
}
|
||||
|
||||
WUMS_EXPORT_FUNCTION(MemoryMappingFree);
|
||||
WUMS_EXPORT_FUNCTION(MemoryMappingAlloc);
|
||||
WUMS_EXPORT_FUNCTION(MemoryMappingEffectiveToPhysical);
|
||||
WUMS_EXPORT_FUNCTION(MemoryMappingPhysicalToEffective);
|
795
source/memory_mapping.cpp
Normal file
795
source/memory_mapping.cpp
Normal file
@ -0,0 +1,795 @@
|
||||
#include "memory_mapping.h"
|
||||
#include <coreinit/memorymap.h>
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <coreinit/thread.h>
|
||||
|
||||
#include <vector>
|
||||
#include "memory.h"
|
||||
#include "logger.h"
|
||||
#include "CThread.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
// #define DEBUG_FUNCTION_LINE(x,...)
|
||||
|
||||
void runOnAllCores(CThread::Callback callback, void *callbackArg, int32_t iAttr = 0, int32_t iPriority = 16, int32_t iStackSize = 0x8000) {
|
||||
int32_t aff[] = {CThread::eAttributeAffCore2,CThread::eAttributeAffCore1,CThread::eAttributeAffCore0};
|
||||
|
||||
for(uint32_t i = 0; i <(sizeof(aff)/sizeof(aff[0])); i++) {
|
||||
CThread * thread = CThread::create(callback, callbackArg, iAttr | aff[i],iPriority,iStackSize);
|
||||
thread->resumeThread();
|
||||
delete thread;
|
||||
}
|
||||
}
|
||||
|
||||
void writeSegmentRegister(CThread *thread, void *arg) {
|
||||
sr_table_t * table = (sr_table_t *) arg;
|
||||
uint16_t core = OSGetThreadAffinity(OSGetCurrentThread());
|
||||
DEBUG_FUNCTION_LINE("Writing segment register to core %d\n",core);
|
||||
|
||||
DCFlushRange(table,sizeof(sr_table_t));
|
||||
KernelWriteSRs(table);
|
||||
}
|
||||
|
||||
void readAndPrintSegmentRegister(CThread *thread, void *arg) {
|
||||
uint16_t core = OSGetThreadAffinity(OSGetCurrentThread());
|
||||
DEBUG_FUNCTION_LINE("Reading segment register and page table from core %d",core);
|
||||
sr_table_t srTable;
|
||||
memset(&srTable,0,sizeof(srTable));
|
||||
|
||||
KernelReadSRs(&srTable);
|
||||
DCFlushRange(&srTable,sizeof(srTable));
|
||||
|
||||
for(int32_t i = 0; i < 16; i++) {
|
||||
DEBUG_FUNCTION_LINE("[%d] SR[%d]=%08X",core,i,srTable.value[i]);
|
||||
}
|
||||
|
||||
uint32_t pageTable[0x8000];
|
||||
|
||||
memset(pageTable,0,sizeof(pageTable));
|
||||
DEBUG_FUNCTION_LINE("Reading pageTable now.");
|
||||
KernelReadPTE((uint32_t) pageTable,sizeof(pageTable));
|
||||
DCFlushRange(pageTable,sizeof(pageTable));
|
||||
DEBUG_FUNCTION_LINE("Reading pageTable done");
|
||||
|
||||
MemoryMapping::printPageTableTranslation(srTable,pageTable);
|
||||
|
||||
DEBUG_FUNCTION_LINE("-----------------------------");
|
||||
}
|
||||
|
||||
bool MemoryMapping::isMemoryMapped() {
|
||||
sr_table_t srTable;
|
||||
memset(&srTable,0,sizeof(srTable));
|
||||
|
||||
KernelReadSRs(&srTable);
|
||||
if((srTable.value[MEMORY_START_BASE >> 28] & 0x00FFFFFF) == SEGMENT_UNIQUE_ID) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryMapping::searchEmptyMemoryRegions() {
|
||||
DEBUG_FUNCTION_LINE("Searching for empty memory.");
|
||||
|
||||
for(int32_t i = 0;; i++) {
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
break;
|
||||
}
|
||||
uint32_t ea_start_address = mem_mapping[i].effective_start_address;
|
||||
|
||||
const memory_values_t * mem_vals = mem_mapping[i].physical_addresses;
|
||||
|
||||
uint32_t ea_size = 0;
|
||||
for(uint32_t j = 0;; j++) {
|
||||
uint32_t pa_start_address = mem_vals[j].start_address;
|
||||
uint32_t pa_end_address = mem_vals[j].end_address;
|
||||
if(pa_end_address == 0 && pa_start_address == 0) {
|
||||
break;
|
||||
}
|
||||
ea_size += pa_end_address - pa_start_address;
|
||||
}
|
||||
|
||||
uint32_t* flush_start = (uint32_t*)ea_start_address;
|
||||
uint32_t flush_size = ea_size;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Flushing %08X (%d kB) at %08X.",flush_size,flush_size/1024, flush_start);
|
||||
DCFlushRange(flush_start,flush_size);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Searching in memory region %d. 0x%08X - 0x%08X. Size 0x%08X (%d KBytes).",i+1,ea_start_address,ea_start_address+ea_size,ea_size,ea_size/1024);
|
||||
bool success = true;
|
||||
uint32_t * memory_ptr = (uint32_t*) ea_start_address;
|
||||
bool inFailRange = false;
|
||||
uint32_t startFailing = 0;
|
||||
uint32_t startGood = ea_start_address;
|
||||
for(uint32_t j=0; j < ea_size/4; j++) {
|
||||
if(memory_ptr[j] != 0) {
|
||||
success = false;
|
||||
if(!success && !inFailRange) {
|
||||
if((((uint32_t)&memory_ptr[j])-(uint32_t)startGood)/1024 > 512) {
|
||||
uint32_t start_addr = startGood & 0xFFFE0000;
|
||||
if(start_addr != startGood) {
|
||||
start_addr += 0x20000;
|
||||
}
|
||||
uint32_t end_addr = ((uint32_t)&memory_ptr[j]) - MEMORY_START_BASE;
|
||||
end_addr = (end_addr & 0xFFFE0000);
|
||||
DEBUG_FUNCTION_LINE("+ Free between 0x%08X and 0x%08X size: %u kB",start_addr - MEMORY_START_BASE,end_addr,(((uint32_t)end_addr)-((uint32_t)startGood - MEMORY_START_BASE))/1024);
|
||||
}
|
||||
startFailing = (uint32_t)&memory_ptr[j];
|
||||
inFailRange = true;
|
||||
startGood = 0;
|
||||
j = ((j & 0xFFFF8000) + 0x00008000)-1;
|
||||
}
|
||||
//break;
|
||||
} else {
|
||||
if(inFailRange) {
|
||||
//DEBUG_FUNCTION_LINE("- Error between 0x%08X and 0x%08X size: %u kB",startFailing,&memory_ptr[j],(((uint32_t)&memory_ptr[j])-(uint32_t)startFailing)/1024);
|
||||
startFailing = 0;
|
||||
startGood = (uint32_t) &memory_ptr[j];
|
||||
inFailRange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(startGood != 0 && (startGood != ea_start_address + ea_size)) {
|
||||
DEBUG_FUNCTION_LINE("+ Good between 0x%08X and 0x%08X size: %u kB",startGood - MEMORY_START_BASE,((uint32_t)(ea_start_address + ea_size) - (uint32_t)MEMORY_START_BASE),((uint32_t)(ea_start_address + ea_size) - (uint32_t)startGood)/1024);
|
||||
} else if(inFailRange) {
|
||||
DEBUG_FUNCTION_LINE("- Used between 0x%08X and 0x%08X size: %u kB",startFailing,ea_start_address + ea_size,((uint32_t)(ea_start_address + ea_size) - (uint32_t)startFailing)/1024);
|
||||
}
|
||||
if(success) {
|
||||
DEBUG_FUNCTION_LINE("Test %d was successful!",i+1);
|
||||
}
|
||||
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("All tests done.");
|
||||
}
|
||||
|
||||
void MemoryMapping::writeTestValuesToMemory() {
|
||||
//don't smash the stack.
|
||||
uint32_t chunk_size = 0x1000;
|
||||
uint32_t testBuffer[chunk_size];
|
||||
|
||||
for(int32_t i = 0;; i++) {
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
break;
|
||||
}
|
||||
uint32_t cur_ea_start_address = mem_mapping[i].effective_start_address;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Preparing memory test for region %d. Region start at effective address %08X.",i+1,cur_ea_start_address);
|
||||
|
||||
const memory_values_t * mem_vals = mem_mapping[i].physical_addresses;
|
||||
uint32_t counter = 0;
|
||||
for(uint32_t j = 0;; j++) {
|
||||
uint32_t pa_start_address = mem_vals[j].start_address;
|
||||
uint32_t pa_end_address = mem_vals[j].end_address;
|
||||
if(pa_end_address == 0 && pa_start_address == 0) {
|
||||
break;
|
||||
}
|
||||
uint32_t pa_size = pa_end_address - pa_start_address;
|
||||
DEBUG_FUNCTION_LINE("Writing region %d of mapping %d. From %08X to %08X Size: %d KBytes...",j+1,i+1,pa_start_address,pa_end_address,pa_size/1024);
|
||||
for(uint32_t k=0; k<=pa_size/4; k++) {
|
||||
if(k > 0 && (k % chunk_size) == 0) {
|
||||
DCFlushRange(&testBuffer,sizeof(testBuffer));
|
||||
DCInvalidateRange(&testBuffer,sizeof(testBuffer));
|
||||
uint32_t destination = pa_start_address + ((k*4) - sizeof(testBuffer));
|
||||
KernelCopyData(destination,(uint32_t)OSEffectiveToPhysical((uint32_t)testBuffer),sizeof(testBuffer));
|
||||
//DEBUG_FUNCTION_LINE("Copy testBuffer into %08X",destination);
|
||||
}
|
||||
if(k != pa_size/4) {
|
||||
testBuffer[k % chunk_size] = counter++;
|
||||
}
|
||||
//DEBUG_FUNCTION_LINE("testBuffer[%d] = %d",i % chunk_size,i);
|
||||
}
|
||||
uint32_t* flush_start = (uint32_t*)cur_ea_start_address;
|
||||
uint32_t flush_size = pa_size;
|
||||
|
||||
cur_ea_start_address += pa_size;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Flushing %08X (%d kB) at %08X to map memory.",flush_size,flush_size/1024, flush_start);
|
||||
DCFlushRange(flush_start,flush_size);
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Done writing region %d",i+1);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMapping::readTestValuesFromMemory() {
|
||||
DEBUG_FUNCTION_LINE("Testing reading the written values.");
|
||||
|
||||
for(int32_t i = 0;; i++) {
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
break;
|
||||
}
|
||||
uint32_t ea_start_address = mem_mapping[i].effective_start_address;
|
||||
|
||||
const memory_values_t * mem_vals = mem_mapping[i].physical_addresses;
|
||||
//uint32_t counter = 0;
|
||||
uint32_t ea_size = 0;
|
||||
for(uint32_t j = 0;; j++) {
|
||||
uint32_t pa_start_address = mem_vals[j].start_address;
|
||||
uint32_t pa_end_address = mem_vals[j].end_address;
|
||||
if(pa_end_address == 0 && pa_start_address == 0) {
|
||||
break;
|
||||
}
|
||||
ea_size += pa_end_address - pa_start_address;
|
||||
}
|
||||
|
||||
uint32_t* flush_start = (uint32_t*)ea_start_address;
|
||||
uint32_t flush_size = ea_size;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Flushing %08X (%d kB) at %08X to map memory.",flush_size,flush_size/1024, flush_start);
|
||||
DCFlushRange(flush_start,flush_size);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Testing memory region %d. 0x%08X - 0x%08X. Size 0x%08X (%d KBytes).",i+1,ea_start_address,ea_start_address+ea_size,ea_size,ea_size/1024);
|
||||
bool success = true;
|
||||
uint32_t * memory_ptr = (uint32_t*) ea_start_address;
|
||||
bool inFailRange = false;
|
||||
uint32_t startFailing = 0;
|
||||
uint32_t startGood = ea_start_address;
|
||||
for(uint32_t j=0; j < ea_size/4; j++) {
|
||||
if(memory_ptr[j] != j) {
|
||||
success = false;
|
||||
if(!success && !inFailRange) {
|
||||
DEBUG_FUNCTION_LINE("+ Good between 0x%08X and 0x%08X size: %u kB",startGood,&memory_ptr[j],(((uint32_t)&memory_ptr[j])-(uint32_t)startGood)/1024);
|
||||
startFailing = (uint32_t)&memory_ptr[j];
|
||||
inFailRange = true;
|
||||
startGood = 0;
|
||||
j = ((j & 0xFFFF8000) + 0x00008000)-1;
|
||||
}
|
||||
//break;
|
||||
} else {
|
||||
if(inFailRange) {
|
||||
DEBUG_FUNCTION_LINE("- Error between 0x%08X and 0x%08X size: %u kB",startFailing,&memory_ptr[j],(((uint32_t)&memory_ptr[j])-(uint32_t)startFailing)/1024);
|
||||
startFailing = 0;
|
||||
startGood = (uint32_t) &memory_ptr[j];
|
||||
inFailRange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(startGood != 0 && (startGood != ea_start_address + ea_size)) {
|
||||
DEBUG_FUNCTION_LINE("+ Good between 0x%08X and 0x%08X size: %u kB",startGood,ea_start_address + ea_size,((uint32_t)(ea_start_address + ea_size) - (uint32_t)startGood)/1024);
|
||||
} else if(inFailRange) {
|
||||
DEBUG_FUNCTION_LINE("- Error between 0x%08X and 0x%08X size: %u kB",startFailing,ea_start_address + ea_size,((uint32_t)(ea_start_address + ea_size) - (uint32_t)startFailing)/1024);
|
||||
}
|
||||
if(success) {
|
||||
DEBUG_FUNCTION_LINE("Test %d was successful!",i+1);
|
||||
}
|
||||
|
||||
}
|
||||
DEBUG_FUNCTION_LINE("All tests done.");
|
||||
}
|
||||
|
||||
void MemoryMapping::memoryMappingForRegions(const memory_mapping_t * memory_mapping, sr_table_t SRTable, uint32_t * translation_table) {
|
||||
for(int32_t i = 0; /* waiting for a break */; i++) {
|
||||
DEBUG_FUNCTION_LINE("In loop %d",i);
|
||||
if(memory_mapping[i].physical_addresses == NULL) {
|
||||
DEBUG_FUNCTION_LINE("break %d",i);
|
||||
break;
|
||||
}
|
||||
uint32_t cur_ea_start_address = memory_mapping[i].effective_start_address;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Mapping area %d. effective address %08X...",i+1,cur_ea_start_address);
|
||||
const memory_values_t * mem_vals = memory_mapping[i].physical_addresses;
|
||||
|
||||
for(uint32_t j = 0;; j++) {
|
||||
DEBUG_FUNCTION_LINE("In inner loop %d",j);
|
||||
uint32_t pa_start_address = mem_vals[j].start_address;
|
||||
uint32_t pa_end_address = mem_vals[j].end_address;
|
||||
if(pa_end_address == 0 && pa_start_address == 0) {
|
||||
DEBUG_FUNCTION_LINE("inner break %d",j);
|
||||
// Break if entry was empty.
|
||||
break;
|
||||
}
|
||||
uint32_t pa_size = pa_end_address - pa_start_address;
|
||||
DEBUG_FUNCTION_LINE("Adding page table entry %d for mapping area %d. %08X-%08X => %08X-%08X...",j+1,i+1,cur_ea_start_address,memory_mapping[i].effective_start_address+pa_size,pa_start_address,pa_end_address);
|
||||
if(!mapMemory(pa_start_address,pa_end_address,cur_ea_start_address,SRTable,translation_table)) {
|
||||
//log_print("error =(");
|
||||
DEBUG_FUNCTION_LINE("Failed to map memory.");
|
||||
//OSFatal("Failed to map memory.");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
cur_ea_start_address += pa_size;
|
||||
//log_print("done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMapping::setupMemoryMapping() {
|
||||
// Override all writes to SR8 with nops.
|
||||
KernelNOPAtPhysicalAddress(0xFFF1D754);
|
||||
KernelNOPAtPhysicalAddress(0xFFF1D64C);
|
||||
KernelNOPAtPhysicalAddress(0xFFE00638);
|
||||
|
||||
//runOnAllCores(readAndPrintSegmentRegister,NULL,0,16,0x80000);
|
||||
|
||||
sr_table_t srTableCpy;
|
||||
uint32_t pageTableCpy[0x8000];
|
||||
|
||||
KernelReadSRs(&srTableCpy);
|
||||
KernelReadPTE((uint32_t) pageTableCpy,sizeof(pageTableCpy));
|
||||
|
||||
DCFlushRange(&srTableCpy,sizeof(srTableCpy));
|
||||
DCFlushRange(pageTableCpy,sizeof(pageTableCpy));
|
||||
|
||||
for(int32_t i = 0; i < 16; i++) {
|
||||
DEBUG_FUNCTION_LINE("SR[%d]=%08X",i,srTableCpy.value[i]);
|
||||
}
|
||||
|
||||
//printPageTableTranslation(srTableCpy,pageTableCpy);
|
||||
|
||||
// According to
|
||||
// http://wiiubrew.org/wiki/Cafe_OS#Virtual_Memory_Map 0x80000000
|
||||
// is currently unmapped.
|
||||
// This is nice because it leads to SR[8] which also seems to be unused (was set to 0x30FFFFFF)
|
||||
// The content of the segment was chosen randomly.
|
||||
uint32_t segment_index = MEMORY_START_BASE >> 28;
|
||||
uint32_t segment_content = 0x00000000 | SEGMENT_UNIQUE_ID;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Setting SR[%d] to %08X",segment_index,segment_content);
|
||||
srTableCpy.value[segment_index] = segment_content;
|
||||
DCFlushRange(&srTableCpy,sizeof(srTableCpy));
|
||||
|
||||
DEBUG_FUNCTION_LINE("Writing segment registers...",segment_index,segment_content);
|
||||
// Writing the segment registers to ALL cores.
|
||||
//
|
||||
//writeSegmentRegister(NULL, &srTableCpy);
|
||||
|
||||
runOnAllCores(writeSegmentRegister,&srTableCpy);
|
||||
|
||||
memoryMappingForRegions(mem_mapping,srTableCpy,pageTableCpy);
|
||||
|
||||
//printPageTableTranslation(srTableCpy,pageTableCpy);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Writing PageTable... ");
|
||||
DCFlushRange(pageTableCpy,sizeof(pageTableCpy));
|
||||
KernelWritePTE((uint32_t) pageTableCpy,sizeof(pageTableCpy));
|
||||
DCFlushRange(pageTableCpy,sizeof(pageTableCpy));
|
||||
DEBUG_FUNCTION_LINE("done");
|
||||
|
||||
for(int32_t i = 0; /* waiting for a break */; i++) {
|
||||
DEBUG_FUNCTION_LINE("In loop %d",i);
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
DEBUG_FUNCTION_LINE("break %d",i);
|
||||
break;
|
||||
}
|
||||
MEMCreateExpHeapEx((void *) (mem_mapping[i].effective_start_address), mem_mapping[i].effective_end_address - mem_mapping[i].effective_start_address, 0);
|
||||
}
|
||||
|
||||
//printPageTableTranslation(srTableCpy,pageTableCpy);
|
||||
|
||||
//runOnAllCores(readAndPrintSegmentRegister,NULL,0,16,0x80000);
|
||||
|
||||
//searchEmptyMemoryRegions();
|
||||
|
||||
//writeTestValuesToMemory();
|
||||
//readTestValuesFromMemory();
|
||||
|
||||
//runOnAllCores(writeSegmentRegister,&srTableCpy);
|
||||
}
|
||||
|
||||
void* MemoryMapping::alloc(uint32_t size, uint32_t align){
|
||||
void* res = NULL;
|
||||
for(int32_t i = 0; /* waiting for a break */; i++) {
|
||||
DEBUG_FUNCTION_LINE("In loop %d",i);
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
DEBUG_FUNCTION_LINE("break %d",i);
|
||||
break;
|
||||
}
|
||||
res = MEMAllocFromExpHeapEx((MEMHeapHandle) mem_mapping->effective_start_address, size, align);
|
||||
if (res != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void MemoryMapping::free(void* ptr){
|
||||
if(ptr == NULL){
|
||||
return;
|
||||
}
|
||||
uint32_t ptr_val = (uint32_t) ptr;
|
||||
for(int32_t i = 0; /* waiting for a break */; i++) {
|
||||
DEBUG_FUNCTION_LINE("In loop %d",i);
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
DEBUG_FUNCTION_LINE("break %d",i);
|
||||
break;
|
||||
}
|
||||
if(ptr_val > mem_mapping->effective_start_address && ptr_val < mem_mapping->effective_end_address){
|
||||
MEMFreeToExpHeap((MEMHeapHandle) mem_mapping->effective_start_address, ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t MemoryMapping::getAreaSizeFromPageTable(uint32_t start, uint32_t maxSize) {
|
||||
sr_table_t srTable;
|
||||
uint32_t pageTable[0x8000];
|
||||
|
||||
KernelReadSRs(&srTable);
|
||||
KernelReadPTE((uint32_t) pageTable,sizeof(pageTable));
|
||||
|
||||
uint32_t sr_start = start >> 28;
|
||||
uint32_t sr_end = (start + maxSize) >> 28;
|
||||
|
||||
if(sr_end < sr_start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t cur_address = start;
|
||||
uint32_t end_address = start + maxSize;
|
||||
|
||||
uint32_t memSize = 0;
|
||||
|
||||
for(uint32_t segment = sr_start; segment <= sr_end ; segment++) {
|
||||
uint32_t sr = srTable.value[segment];
|
||||
if(sr >> 31) {
|
||||
DEBUG_FUNCTION_LINE("Direct access not supported");
|
||||
} else {
|
||||
uint32_t vsid = sr & 0xFFFFFF;
|
||||
|
||||
uint32_t pageSize = 1 << PAGE_INDEX_SHIFT;
|
||||
uint32_t cur_end_addr = 0;
|
||||
if(segment == sr_end) {
|
||||
cur_end_addr = end_address;
|
||||
} else {
|
||||
cur_end_addr = (segment + 1) * 0x10000000;
|
||||
}
|
||||
if(segment != sr_start) {
|
||||
cur_address = (segment) * 0x10000000;
|
||||
}
|
||||
bool success = true;
|
||||
for(uint32_t addr = cur_address; addr < cur_end_addr; addr += pageSize) {
|
||||
uint32_t PTEH = 0;
|
||||
uint32_t PTEL = 0;
|
||||
if(getPageEntryForAddress(srTable.sdr1, addr, vsid, pageTable, &PTEH, &PTEL, false)) {
|
||||
memSize += pageSize;
|
||||
} else {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return memSize;
|
||||
}
|
||||
|
||||
bool MemoryMapping::getPageEntryForAddress(uint32_t SDR1,uint32_t addr, uint32_t vsid, uint32_t * translation_table,uint32_t* oPTEH, uint32_t* oPTEL, bool checkSecondHash) {
|
||||
uint32_t pageMask = SDR1 & 0x1FF;
|
||||
uint32_t pageIndex = (addr >> PAGE_INDEX_SHIFT) & PAGE_INDEX_MASK;
|
||||
uint32_t primaryHash = (vsid & 0x7FFFF) ^ pageIndex;
|
||||
|
||||
if(getPageEntryForAddressEx(SDR1, addr, vsid, primaryHash, translation_table, oPTEH, oPTEL, 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(checkSecondHash) {
|
||||
if(getPageEntryForAddressEx(pageMask, addr, vsid, ~primaryHash, translation_table, oPTEH, oPTEL, 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MemoryMapping::getPageEntryForAddressEx(uint32_t pageMask, uint32_t addr, uint32_t vsid, uint32_t primaryHash, uint32_t * translation_table,uint32_t* oPTEH, uint32_t* oPTEL,uint32_t H) {
|
||||
uint32_t maskedHash = primaryHash & ((pageMask << 10) | 0x3FF);
|
||||
uint32_t api = (addr >> 22) & 0x3F;
|
||||
|
||||
uint32_t pteAddrOffset = (maskedHash << 6);
|
||||
|
||||
for (int32_t j = 0; j < 8; j++, pteAddrOffset += 8) {
|
||||
uint32_t PTEH = 0;
|
||||
uint32_t PTEL = 0;
|
||||
|
||||
uint32_t pteh_index = pteAddrOffset / 4;
|
||||
uint32_t ptel_index = pteh_index + 1;
|
||||
|
||||
PTEH = translation_table[pteh_index];
|
||||
PTEL = translation_table[ptel_index];
|
||||
|
||||
//Check validity
|
||||
if (!(PTEH >> 31)) {
|
||||
//printf("PTE is not valid ");
|
||||
continue;
|
||||
}
|
||||
//DEBUG_FUNCTION_LINE("in");
|
||||
// the H bit indicated if the PTE was found using the second hash.
|
||||
if (((PTEH >> 6) & 1) != H) {
|
||||
//DEBUG_FUNCTION_LINE("Secondary hash is used",((PTEH >> 6) & 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the VSID matches, otherwise this is a PTE for another SR
|
||||
// This is the place where collision could happen.
|
||||
// Hopefully no collision happen and only the PTEs of the SR will match.
|
||||
if (((PTEH >> 7) & 0xFFFFFF) != vsid) {
|
||||
//DEBUG_FUNCTION_LINE("VSID mismatch");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the API (Abbreviated Page Index)
|
||||
if ((PTEH & 0x3F) != api) {
|
||||
//DEBUG_FUNCTION_LINE("API mismatch");
|
||||
continue;
|
||||
}
|
||||
*oPTEH = PTEH;
|
||||
*oPTEL = PTEL;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryMapping::printPageTableTranslation(sr_table_t srTable, uint32_t * translation_table) {
|
||||
uint32_t SDR1 = srTable.sdr1;
|
||||
|
||||
pageInformation current;
|
||||
memset(¤t,0,sizeof(current));
|
||||
|
||||
std::vector<pageInformation> pageInfos;
|
||||
|
||||
for(uint32_t segment = 0; segment < 16 ; segment++) {
|
||||
uint32_t sr = srTable.value[segment];
|
||||
if(sr >> 31) {
|
||||
DEBUG_FUNCTION_LINE("Direct access not supported");
|
||||
} else {
|
||||
uint32_t ks = (sr >> 30) & 1;
|
||||
uint32_t kp = (sr >> 29) & 1;
|
||||
uint32_t nx = (sr >> 28) & 1;
|
||||
uint32_t vsid = sr & 0xFFFFFF;
|
||||
|
||||
DEBUG_FUNCTION_LINE("ks %08X kp %08X nx %08X vsid %08X",ks,kp,nx,vsid);
|
||||
uint32_t pageSize = 1 << PAGE_INDEX_SHIFT;
|
||||
for(uint32_t addr = segment * 0x10000000; addr < (segment + 1) * 0x10000000; addr += pageSize) {
|
||||
uint32_t PTEH = 0;
|
||||
uint32_t PTEL = 0;
|
||||
if(getPageEntryForAddress(SDR1, addr, vsid, translation_table, &PTEH, &PTEL, false)) {
|
||||
uint32_t pp = PTEL & 3;
|
||||
uint32_t phys = PTEL & 0xFFFFF000;
|
||||
|
||||
//DEBUG_FUNCTION_LINE("current.phys == phys - current.size ( %08X %08X)",current.phys, phys - current.size);
|
||||
|
||||
if( current.ks == ks &&
|
||||
current.kp == kp &&
|
||||
current.nx == nx &&
|
||||
current.pp == pp &&
|
||||
current.phys == phys - current.size
|
||||
) {
|
||||
current.size += pageSize;
|
||||
//DEBUG_FUNCTION_LINE("New size of %08X is %08X",current.addr,current.size);
|
||||
} else {
|
||||
if(current.addr != 0 && current.size != 0) {
|
||||
/*DEBUG_FUNCTION_LINE("Saving old block from %08X",current.addr);
|
||||
DEBUG_FUNCTION_LINE("ks %08X new %08X",current.ks,ks);
|
||||
DEBUG_FUNCTION_LINE("kp %08X new %08X",current.kp,kp);
|
||||
DEBUG_FUNCTION_LINE("nx %08X new %08X",current.nx,nx);
|
||||
DEBUG_FUNCTION_LINE("pp %08X new %08X",current.pp,pp);*/
|
||||
pageInfos.push_back(current);
|
||||
memset(¤t,0,sizeof(current));
|
||||
}
|
||||
//DEBUG_FUNCTION_LINE("Found new block at %08X",addr);
|
||||
current.addr = addr;
|
||||
current.size = pageSize;
|
||||
current.kp = kp;
|
||||
current.ks = ks;
|
||||
current.nx = nx;
|
||||
current.pp = pp;
|
||||
current.phys = phys;
|
||||
}
|
||||
} else {
|
||||
if(current.addr != 0 && current.size != 0) {
|
||||
pageInfos.push_back(current);
|
||||
memset(¤t,0,sizeof(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *access1[] = {"read/write", "read/write", "read/write", "read only"};
|
||||
const char *access2[] = {"no access", "read only", "read/write", "read only"};
|
||||
|
||||
for(std::vector<pageInformation>::iterator it = pageInfos.begin(); it != pageInfos.end(); ++it) {
|
||||
pageInformation cur = *it;
|
||||
DEBUG_FUNCTION_LINE("%08X %08X -> %08X %08X. user access %s. supervisor access %s. %s",cur.addr,cur.addr+cur.size,cur.phys,cur.phys+cur.size,cur.kp ? access2[cur.pp] : access1[cur.pp],
|
||||
cur.ks ? access2[cur.pp] : access1[cur.pp],cur.nx? "not executable" : "executable");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MemoryMapping::mapMemory(uint32_t pa_start_address,uint32_t pa_end_address,uint32_t ea_start_address, sr_table_t SRTable, uint32_t * translation_table) {
|
||||
// Based on code from dimok. Thanks!
|
||||
|
||||
//uint32_t byteOffsetMask = (1 << PAGE_INDEX_SHIFT) - 1;
|
||||
//uint32_t apiShift = 22 - PAGE_INDEX_SHIFT;
|
||||
|
||||
// Information on page 5.
|
||||
// https://www.nxp.com/docs/en/application-note/AN2794.pdf
|
||||
uint32_t HTABORG = SRTable.sdr1 >> 16;
|
||||
uint32_t HTABMASK = SRTable.sdr1 & 0x1FF;
|
||||
|
||||
// Iterate to all possible pages. Each page is 1<<(PAGE_INDEX_SHIFT) big.
|
||||
|
||||
uint32_t pageSize = 1<<(PAGE_INDEX_SHIFT);
|
||||
for(uint32_t i = 0; i < pa_end_address - pa_start_address; i += pageSize) {
|
||||
// Calculate the current effective address.
|
||||
uint32_t ea_addr = ea_start_address + i;
|
||||
// Calculate the segement.
|
||||
uint32_t segment = SRTable.value[ea_addr >> 28];
|
||||
|
||||
// Unique ID from the segment which is the input for the hash function.
|
||||
// Change it to prevent collisions.
|
||||
uint32_t VSID = segment & 0x00FFFFFF;
|
||||
uint32_t V = 1;
|
||||
|
||||
//Indicated if second hash is used.
|
||||
uint32_t H = 0;
|
||||
|
||||
// Abbreviated Page Index
|
||||
|
||||
// Real page number
|
||||
uint32_t RPN = (pa_start_address + i) >> 12;
|
||||
uint32_t RC = 3;
|
||||
uint32_t WIMG = 0x02;
|
||||
uint32_t PP = 0x02;
|
||||
|
||||
uint32_t page_index = (ea_addr >> PAGE_INDEX_SHIFT) & PAGE_INDEX_MASK;
|
||||
uint32_t API = (ea_addr >> 22) & 0x3F;
|
||||
|
||||
uint32_t PTEH = (V << 31) | (VSID << 7) | (H << 6) | API;
|
||||
uint32_t PTEL = (RPN << 12) | (RC << 7) | (WIMG << 3) | PP;
|
||||
|
||||
//unsigned long long virtual_address = ((unsigned long long)VSID << 28UL) | (page_index << PAGE_INDEX_SHIFT) | (ea_addr & 0xFFF);
|
||||
|
||||
uint32_t primary_hash = (VSID & 0x7FFFF);
|
||||
|
||||
uint32_t hashvalue1 = primary_hash ^ page_index;
|
||||
|
||||
// hashvalue 2 is the complement of the first hash.
|
||||
uint32_t hashvalue2 = ~hashvalue1;
|
||||
|
||||
//uint32_t pageMask = SRTable.sdr1 & 0x1FF;
|
||||
|
||||
// calculate the address of the PTE groups.
|
||||
// PTEs are saved in a group of 8 PTEs
|
||||
// When PTEGaddr1 is full (all 8 PTEs set), PTEGaddr2 is used.
|
||||
// Then H in PTEH needs to be set to 1.
|
||||
uint32_t PTEGaddr1 = (HTABORG << 16) | (((hashvalue1 >> 10) & HTABMASK) << 16) | ((hashvalue1 & 0x3FF) << 6);
|
||||
uint32_t PTEGaddr2 = (HTABORG << 16) | (((hashvalue2 >> 10) & HTABMASK) << 16) | ((hashvalue2 & 0x3FF) << 6);
|
||||
|
||||
//offset of the group inside the PTE Table.
|
||||
uint32_t PTEGoffset = PTEGaddr1 - (HTABORG << 16);
|
||||
|
||||
bool setSuccessfully = false;
|
||||
PTEGoffset += 7*8;
|
||||
// Lets iterate through the PTE group where out PTE should be saved.
|
||||
for(int32_t j = 7; j>0; PTEGoffset -= 8) {
|
||||
int32_t index = (PTEGoffset/4);
|
||||
|
||||
uint32_t pteh = translation_table[index];
|
||||
// Check if it's already taken. The first bit indicates if the PTE-slot inside
|
||||
// this group is already taken.
|
||||
if ((pteh == 0)) {
|
||||
// If we found a free slot, set the PTEH and PTEL value.
|
||||
DEBUG_FUNCTION_LINE("Used slot %d. PTEGaddr1 %08X addr %08X",j+1,PTEGaddr1 - (HTABORG << 16),PTEGoffset);
|
||||
translation_table[index] = PTEH;
|
||||
translation_table[index+1] = PTEL;
|
||||
setSuccessfully = true;
|
||||
break;
|
||||
} else {
|
||||
//printf("PTEGoffset %08X was taken",PTEGoffset);
|
||||
}
|
||||
j--;
|
||||
}
|
||||
// Check if we already found a slot.
|
||||
if(!setSuccessfully) {
|
||||
DEBUG_FUNCTION_LINE("-------------- Using second slot -----------------------");
|
||||
// We still have a chance to find a slot in the PTEGaddr2 using the complement of the hash.
|
||||
// We need to set the H flag in PTEH and use PTEGaddr2.
|
||||
// (Not well tested)
|
||||
H = 1;
|
||||
PTEH = (V << 31) | (VSID << 7) | (H << 6) | API;
|
||||
PTEGoffset = PTEGaddr2 - (HTABORG << 16);
|
||||
PTEGoffset += 7*8;
|
||||
// Same as before.
|
||||
for(int32_t j = 7; j>0; PTEGoffset -= 8) {
|
||||
int32_t index = (PTEGoffset/4);
|
||||
uint32_t pteh = translation_table[index];
|
||||
//Check if it's already taken.
|
||||
if ((pteh == 0)) {
|
||||
translation_table[index] = PTEH;
|
||||
translation_table[index+1] = PTEL;
|
||||
setSuccessfully = true;
|
||||
break;
|
||||
} else {
|
||||
//printf("PTEGoffset %08X was taken",PTEGoffset);
|
||||
}
|
||||
j--;
|
||||
}
|
||||
|
||||
if(!setSuccessfully) {
|
||||
// Fail if we couldn't find a free slot.
|
||||
DEBUG_FUNCTION_LINE("-------------- No more free PTE -----------------------");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MemoryMapping::PhysicalToEffective(uint32_t phyiscalAddress) {
|
||||
if(phyiscalAddress >= 0x30800000 && phyiscalAddress < 0x31000000) {
|
||||
return phyiscalAddress - (0x30800000 - 0x00800000);
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
const memory_values_t * curMemValues = NULL;
|
||||
int32_t curOffset = 0;
|
||||
//iterate through all own mapped memory regions
|
||||
for(int32_t i = 0; true; i++) {
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
curMemValues = mem_mapping[i].physical_addresses;
|
||||
uint32_t curOffsetInEA = 0;
|
||||
// iterate through all memory values of this region
|
||||
for(int32_t j= 0; true; j++) {
|
||||
if(curMemValues[j].end_address == 0) {
|
||||
break;
|
||||
}
|
||||
if(phyiscalAddress >= curMemValues[j].start_address && phyiscalAddress < curMemValues[j].end_address) {
|
||||
// calculate the EA
|
||||
result = (phyiscalAddress - curMemValues[j].start_address) + (mem_mapping[i].effective_start_address + curOffsetInEA);
|
||||
return result;
|
||||
}
|
||||
curOffsetInEA += curMemValues[j].end_address - curMemValues[j].start_address;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t MemoryMapping::EffectiveToPhysical(uint32_t effectiveAddress) {
|
||||
if(effectiveAddress >= 0x00800000 && effectiveAddress < 0x01000000) {
|
||||
return effectiveAddress + (0x30800000 - 0x00800000);
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
// CAUTION: The data may be fragmented between multiple areas in PA.
|
||||
const memory_values_t * curMemValues = NULL;
|
||||
uint32_t curOffset = 0;
|
||||
|
||||
for(int32_t i = 0; true; i++) {
|
||||
if(mem_mapping[i].physical_addresses == NULL) {
|
||||
break;
|
||||
}
|
||||
if(effectiveAddress >= mem_mapping[i].effective_start_address && effectiveAddress < mem_mapping[i].effective_end_address) {
|
||||
curMemValues = mem_mapping[i].physical_addresses;
|
||||
curOffset = mem_mapping[i].effective_start_address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(curMemValues == NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for(int32_t i= 0; true; i++) {
|
||||
if(curMemValues[i].end_address == 0) {
|
||||
break;
|
||||
}
|
||||
uint32_t curChunkSize = curMemValues[i].end_address - curMemValues[i].start_address;
|
||||
if(effectiveAddress < (curOffset + curChunkSize)) {
|
||||
result = (effectiveAddress - curOffset) + curMemValues[i].start_address;
|
||||
break;
|
||||
}
|
||||
curOffset += curChunkSize;
|
||||
}
|
||||
return result;
|
||||
}
|
203
source/memory_mapping.h
Normal file
203
source/memory_mapping.h
Normal file
@ -0,0 +1,203 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <kernel/kernel.h>
|
||||
#include <kernel/kernel_defs.h>
|
||||
|
||||
typedef struct pageInformation_ {
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
uint32_t ks;
|
||||
uint32_t kp;
|
||||
uint32_t nx;
|
||||
uint32_t pp;
|
||||
uint32_t phys;
|
||||
} pageInformation;
|
||||
|
||||
typedef struct _memory_values_t {
|
||||
uint32_t start_address;
|
||||
uint32_t end_address;
|
||||
} memory_values_t;
|
||||
|
||||
typedef struct _memory_mapping_t {
|
||||
uint32_t effective_start_address;
|
||||
uint32_t effective_end_address;
|
||||
const memory_values_t *physical_addresses;
|
||||
} memory_mapping_t;
|
||||
|
||||
#define SEGMENT_UNIQUE_ID 0x00AABBCC // Unique ID. Chosen arbitrary.
|
||||
#define PAGE_INDEX_SHIFT (32-15)
|
||||
#define PAGE_INDEX_MASK ((1 << (28 - PAGE_INDEX_SHIFT)) - 1)
|
||||
|
||||
#define MEMORY_START_BASE 0x80000000
|
||||
|
||||
const memory_values_t mem_vals_heap_1[] = {
|
||||
{0x28000000 + 0x06620000, 0x28000000 + 0x07F80000}, // size: 25984 kB
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#define MEMORY_HEAP0_SIZE 0x1960000
|
||||
#define MEMORY_HEAP0 MEMORY_START_BASE
|
||||
|
||||
const memory_values_t mem_vals_heap_2[] = {
|
||||
{0x28000000 + 0x09000000, 0x28000000 + 0x09E20000}, // size: 14464 kB
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#define MEMORY_HEAP1_SIZE 0xE20000
|
||||
#define MEMORY_HEAP1 MEMORY_HEAP0 + MEMORY_HEAP0_SIZE
|
||||
|
||||
const memory_values_t mem_vals_heap_3[] = {
|
||||
{0x28000000 + 0x058E0000, 0x28000000 + 0x06000000}, // size: 7296 kB
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#define MEMORY_HEAP2_SIZE 0x720000
|
||||
#define MEMORY_HEAP2 MEMORY_HEAP1 + MEMORY_HEAP1_SIZE
|
||||
|
||||
const memory_values_t mem_vals_heap_4[] = {
|
||||
{0x28000000 + 0x053C0000, 0x28000000 + 0x05880000}, // size: 4864 kB
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
#define MEMORY_HEAP3_SIZE 0x4C0000
|
||||
#define MEMORY_HEAP3 MEMORY_HEAP2 + MEMORY_HEAP2_SIZE
|
||||
|
||||
#define MEMORY_HEAP4 MEMORY_HEAP3 + MEMORY_HEAP3_SIZE
|
||||
|
||||
const memory_mapping_t mem_mapping[] = {
|
||||
{MEMORY_HEAP0, MEMORY_HEAP1, mem_vals_heap_1},
|
||||
{MEMORY_HEAP1, MEMORY_HEAP2, mem_vals_heap_2},
|
||||
{MEMORY_HEAP2, MEMORY_HEAP3, mem_vals_heap_3},
|
||||
{MEMORY_HEAP3, MEMORY_HEAP4, mem_vals_heap_4},
|
||||
{0, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
const memory_values_t mem_vals_video[] = {
|
||||
// The GPU doesn't have access to the 0x28000000 - 0x32000000 area, so we need memory from somewhere else.
|
||||
// From the SharedReadHeap of the loader.
|
||||
//
|
||||
// #define TinyHeap_Alloc ((int32_t (*)(void* heap, int32_t size, int32_t align,void ** outPtr))0x0101235C)
|
||||
// #define TinyHeap_Free ((void (*)(void* heap, void * ptr))0x01012814)
|
||||
// uint32_t SharedReadHeapTrackingAddr = 0xFA000000 + 0x18 + 0x830 // see https://github.com/decaf-emu/decaf-emu/blob/master/src/libdecaf/src/cafe/loader/cafe_loader_shared.cpp#L490
|
||||
//
|
||||
// Map the area of the heap to somewhere in the user space and test allocation with
|
||||
// void * heap = (void*) SharedReadHeapTrackingAddr - [delta due mapping e.g (0xF8000000 + 0x80000000)];
|
||||
// int size = 0x20000; // value have to be a multiple of 0x20000;
|
||||
// while(true){
|
||||
// void * outPtr = NULL;
|
||||
// if(TinyHeap_Alloc(heap,size, 0x20000,&outPtr) == 0){ // value have to be a multiple of 0x20000;
|
||||
// DEBUG_FUNCTION_LINE("Allocated %d kb on heap %08X (PA %08X)\n",size/1024,(uint32_t)outPtr, OSEffectiveToPhysical(outPtr));
|
||||
// TinyHeap_Free(heap, outPtr);
|
||||
// }else{
|
||||
// DEBUG_FUNCTION_LINE("Failed %08X\n",(uint32_t)outPtr);
|
||||
// break;
|
||||
// }
|
||||
// size += 0x20000; // value have to be a multiple of 0x20000;
|
||||
// }
|
||||
//
|
||||
{0x1A020000, 0x1A020000 + 0xE60000}, // size: 14720 kB
|
||||
// The following chunk were empty while early tests and are maybe promising. However we can get 15mb from
|
||||
// a loader heap. Which should be enough for now.
|
||||
//{0x14000000 + 0x02E00000 , 0x14000000 +0x034E0000}, // size: 7040 kB
|
||||
//{0x14000000 + 0x02820000 , 0x14000000 +0x02C20000}, // size: 4096 kB
|
||||
//{0x14000000 + 0x05AE0000 , 0x14000000 +0x06000000}, // size: 5248 kB
|
||||
//{0x14000000 + 0x08040000 , 0x14000000 +0x08400000}, // size: 3840 kB
|
||||
//{0x18000000 , 0x18000000 +0x3000000}, // size: 3840 kB
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// Values needs to be aligned to 0x20000 and size needs to be a multiple of 0x20000
|
||||
const memory_values_t mem_vals_heap[] = {
|
||||
// 5.5.2 EUR
|
||||
{0x28000000 + 0x09000000, 0x28000000 + 0x09E20000}, // size: 14464 kB
|
||||
{0x28000000 + 0x058E0000, 0x28000000 + 0x06000000}, // size: 7296 kB
|
||||
{0x28000000 + 0x053C0000, 0x28000000 + 0x05880000}, // size: 4864 kB
|
||||
//{0x28000000 + 0x08C20000, 0x28000000 + 0x08F20000}, // size: 3072 kB is part of BAT mapping.
|
||||
{0x28000000 + 0x00900000, 0x28000000 + 0x00B00000}, // size: 2048 kB
|
||||
|
||||
{0x28000000 + 0x02060000, 0x28000000 + 0x021A0000}, // size: 1280 kB
|
||||
{0x28000000 + 0x083C0000, 0x28000000 + 0x084C0000}, // size: 1024 kB
|
||||
{0x28000000 + 0x003C0000, 0x28000000 + 0x004C0000}, // size: 1024 kB
|
||||
{0x28000000 + 0x02BC0000, 0x28000000 + 0x02CA0000}, // size: 896 kB
|
||||
{0x28000000 + 0x080E0000, 0x28000000 + 0x08180000}, // size: 640 kB
|
||||
{0x28000000 + 0x000E0000, 0x28000000 + 0x00160000}, // size: 512 kB
|
||||
{0x28000000 + 0x00E40000, 0x28000000 + 0x00EC0000}, // size: 512 kB
|
||||
{0x28000000 + 0x00EE0000, 0x28000000 + 0x00F60000}, // size: 512 kB
|
||||
{0x28000000 + 0x00FA0000, 0x28000000 + 0x01020000}, // size: 512 kB
|
||||
{0x28000000 + 0x086E0000, 0x28000000 + 0x08760000}, // size: 512 kB
|
||||
{0x28000000 + 0x04B60000, 0x28000000 + 0x04B80000}, // size: 128 kB
|
||||
// This chunk was reduced several times, it _might_ be dangerous to use, let's put it right to the end.
|
||||
{0x28000000 + 0x01040000, 0x28000000 + 0x01340000}, // size: 3072 kB
|
||||
|
||||
// Not usable on 5.5.2
|
||||
//
|
||||
// Used in notifications {0x28000000 + 0x01720000, 0x28000000 + 0x018A0000}, // size: 1536 kB
|
||||
// {0x28000000 + 0x03820000, 0x28000000 + 0x038C0000}, // size: 640 kB
|
||||
// {0x28000000 + 0x03920000, 0x28000000 + 0x039A0000}, // size: 512 kB
|
||||
// Used in notifications {0x28000000 + 0x04B80000, 0x28000000 + 0x051E0000}, // size: 6528 kB
|
||||
// {0x28000000 + 0x08F20000, 0x28000000 + 0x09000000}, // size: 896 kB
|
||||
|
||||
// {0x28000000 + 0x013A0000, 0x28000000 + 0x013C0000}, // size: 128 kB
|
||||
|
||||
// Porting to other/newer firmware:
|
||||
// Map this to check for free regions.
|
||||
// Use MemoryMapper::testFreeMemory() to see regions with are 0x00000000;
|
||||
// Then map the promising regions, and do the write/read check.
|
||||
// Writing numbers into the area, open the home menu and all background apps an check if anything was
|
||||
// overridden.
|
||||
// {0x28000000 + 0x00000000, 0x28000000 + 0x0A000000}, //
|
||||
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
class MemoryMapping {
|
||||
|
||||
public:
|
||||
static bool isMemoryMapped();
|
||||
|
||||
static void setupMemoryMapping();
|
||||
|
||||
static void printPageTableTranslation(sr_table_t srTable, uint32_t *translation_table);
|
||||
|
||||
static void writeTestValuesToMemory();
|
||||
|
||||
static void readTestValuesFromMemory();
|
||||
|
||||
static void searchEmptyMemoryRegions();
|
||||
|
||||
static void * alloc(uint32_t size, uint32_t align);
|
||||
|
||||
static void free(void * ptr);
|
||||
|
||||
static uint32_t getAreaSizeFromPageTable(uint32_t start, uint32_t maxSize);
|
||||
|
||||
// Caution when using the result. A chunk of memory in effective address may be split up
|
||||
// into several small chunks inside physical space.
|
||||
static uint32_t PhysicalToEffective(uint32_t phyiscalAddress);
|
||||
|
||||
// Caution when using the result. A chunk of memory in effective address may be split up
|
||||
// into several small chunks inside physical space.
|
||||
static uint32_t EffectiveToPhysical(uint32_t effectiveAddress);
|
||||
|
||||
private:
|
||||
static void memoryMappingForRegions(const memory_mapping_t *memory_mapping, sr_table_t SRTable, uint32_t *translation_table);
|
||||
|
||||
static bool mapMemory(uint32_t pa_start_address, uint32_t pa_end_address, uint32_t ea_start_address, sr_table_t SRTable, uint32_t *translation_table);
|
||||
|
||||
static bool getPageEntryForAddress(uint32_t SDR1, uint32_t addr, uint32_t vsid, uint32_t *translation_table, uint32_t *oPTEH, uint32_t *oPTEL, bool checkSecondHash);
|
||||
|
||||
static bool getPageEntryForAddressEx(uint32_t pageMask, uint32_t addr, uint32_t vsid, uint32_t primaryHash, uint32_t *translation_table, uint32_t *oPTEH, uint32_t *oPTEL, uint32_t H);
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user