TLS for ARMv7 threads

This commit is contained in:
Nekotekina 2015-01-31 19:44:26 +03:00
parent 12d1f8202d
commit 61a5459ccb
9 changed files with 115 additions and 64 deletions

View file

@ -111,6 +111,8 @@ struct ARMv7Context
} ITSTATE; } ITSTATE;
u32 TLS;
u32 R_ADDR; u32 R_ADDR;
u64 R_DATA; u64 R_DATA;

View file

@ -336,15 +336,17 @@ void ARMv7_instrs::MRC_(ARMv7Context& context, const ARMv7Code code, const ARMv7
if (ConditionPassed(context, cond)) if (ConditionPassed(context, cond))
{ {
if (cp == 15 && opc1 == 0 && cn == 13 && cm == 0 && opc2 == 3) // APSR flags are written if t = 15
{
LOG_ERROR(ARMv7, "TODO: TLS requested");
if (t < 15) if (t < 15 && cp == 15 && opc1 == 0 && cn == 13 && cm == 0 && opc2 == 3)
{ {
context.GPR[t] = 0; if (!context.TLS)
return; {
throw "TLS not initialized";
} }
context.GPR[t] = context.TLS;
return;
} }
throw fmt::format("Bad instruction: mrc p%d,%d,r%d,c%d,c%d,%d", cp, opc1, t, cn, cm, opc2); throw fmt::format("Bad instruction: mrc p%d,%d,r%d,c%d,c%d,%d", cp, opc1, t, cn, cm, opc2);

View file

@ -30,6 +30,69 @@ void ARMv7Context::fast_call(u32 addr)
return thread.FastCall(addr); return thread.FastCall(addr);
} }
#define TLS_MAX 128
u32 g_armv7_tls_start;
std::array<std::atomic<u32>, TLS_MAX> g_armv7_tls_owners;
void armv7_init_tls()
{
g_armv7_tls_start = Emu.GetTLSMemsz() ? vm::cast(Memory.PSV.RAM.AllocAlign(Emu.GetTLSMemsz() * TLS_MAX, 4096)) : 0;
for (auto& v : g_armv7_tls_owners)
{
v.store(0, std::memory_order_relaxed);
}
}
u32 armv7_get_tls(u32 thread)
{
if (!Emu.GetTLSMemsz())
{
return 0;
}
for (u32 i = 0; i < TLS_MAX; i++)
{
if (g_armv7_tls_owners[i] == thread)
{
return g_armv7_tls_start + i * Emu.GetTLSMemsz(); // if already initialized, return TLS address
}
}
for (u32 i = 0; i < TLS_MAX; i++)
{
u32 old = 0;
if (g_armv7_tls_owners[i].compare_exchange_strong(old, thread))
{
const u32 addr = g_armv7_tls_start + i * Emu.GetTLSMemsz(); // get TLS address
memset(vm::get_ptr(addr), 0, Emu.GetTLSMemsz()); // fill TLS area with zeros
memcpy(vm::get_ptr(addr), vm::get_ptr(Emu.GetTLSAddr()), Emu.GetTLSFilesz()); // initialize from TLS image
return addr;
}
}
throw "Out of TLS memory";
}
void armv7_free_tls(u32 thread)
{
if (!Emu.GetTLSMemsz())
{
return;
}
for (auto& v : g_armv7_tls_owners)
{
u32 old = thread;
if (v.compare_exchange_strong(old, 0))
{
return;
}
}
}
ARMv7Thread::ARMv7Thread() ARMv7Thread::ARMv7Thread()
: CPUThread(CPU_THREAD_ARMv7) : CPUThread(CPU_THREAD_ARMv7)
, context(*this) , context(*this)
@ -39,6 +102,11 @@ ARMv7Thread::ARMv7Thread()
{ {
} }
ARMv7Thread::~ARMv7Thread()
{
armv7_free_tls(GetId());
}
void ARMv7Thread::InitRegs() void ARMv7Thread::InitRegs()
{ {
memset(context.GPR, 0, sizeof(context.GPR[0]) * 15); memset(context.GPR, 0, sizeof(context.GPR[0]) * 15);
@ -47,6 +115,7 @@ void ARMv7Thread::InitRegs()
context.ISET = Thumb; context.ISET = Thumb;
context.ITSTATE.IT = 0; context.ITSTATE.IT = 0;
context.SP = m_stack_addr + m_stack_size; context.SP = m_stack_addr + m_stack_size;
context.TLS = armv7_get_tls(GetId());
} }
void ARMv7Thread::InitStack() void ARMv7Thread::InitStack()

View file

@ -12,6 +12,7 @@ public:
//const char* m_last_instr_name; //const char* m_last_instr_name;
ARMv7Thread(); ARMv7Thread();
~ARMv7Thread();
//void update_code(const u32 address) //void update_code(const u32 address)
//{ //{

View file

@ -65,33 +65,8 @@ void PPUThread::InitRegs()
const u32 pc = entry ? vm::read32(entry) : 0; const u32 pc = entry ? vm::read32(entry) : 0;
const u32 rtoc = entry ? vm::read32(entry + 4) : 0; const u32 rtoc = entry ? vm::read32(entry + 4) : 0;
//ConLog.Write("entry = 0x%x", entry);
//ConLog.Write("rtoc = 0x%x", rtoc);
SetPc(pc); SetPc(pc);
/*
const s32 thread_num = Emu.GetCPU().GetThreadNumById(GetType(), GetId());
if(thread_num < 0)
{
LOG_ERROR(PPU, "GetThreadNumById failed.");
Emu.Pause();
return;
}
*/
/*
const s32 tls_size = Emu.GetTLSFilesz() * thread_num;
if(tls_size >= Emu.GetTLSMemsz())
{
LOG_ERROR(PPU, "Out of TLS memory.");
Emu.Pause();
return;
}
*/
GPR[1] = align(m_stack_addr + m_stack_size, 0x200) - 0x200; GPR[1] = align(m_stack_addr + m_stack_size, 0x200) - 0x200;
GPR[2] = rtoc; GPR[2] = rtoc;
//GPR[11] = entry; //GPR[11] = entry;

View file

@ -23,12 +23,10 @@
Module *sysPrxForUser = nullptr; Module *sysPrxForUser = nullptr;
u32 g_tls_size; // size of every thread's storage #define TLS_MAX 128
u32 g_tls_start; // start of TLS memory area
u32 g_tls_image_addr; // address of TLS initialization area u32 g_tls_start; // start of TLS memory area
u32 g_tls_image_size; // size of TLS initialization area
const u32 TLS_MAX = 256;
std::array<std::atomic<u32>, TLS_MAX> g_tls_owners; std::array<std::atomic<u32>, TLS_MAX> g_tls_owners;
void sys_initialize_tls() void sys_initialize_tls()
@ -40,19 +38,16 @@ u32 ppu_get_tls(u32 thread)
{ {
if (!g_tls_start) if (!g_tls_start)
{ {
g_tls_size = vm::cast(Emu.GetTLSMemsz(), "Emu.GetTLSMemsz"); // (not an address for vm::cast, but fine) g_tls_start = vm::cast(Memory.MainMem.AllocAlign(Emu.GetTLSMemsz() * TLS_MAX, 4096)); // memory for up to TLS_MAX threads
g_tls_start = vm::cast(Memory.Alloc(g_tls_size * TLS_MAX, 4096)); // memory for up to TLS_MAX threads sysPrxForUser->Notice("Thread Local Storage initialized (g_tls_start=0x%x, size = 0x%x)\n*** TLS segment addr: 0x%08x\n*** TLS segment size: 0x%08x",
g_tls_image_addr = vm::cast(Emu.GetTLSAddr(), "Emu.GetTLSAddr"); g_tls_start, Emu.GetTLSMemsz(), Emu.GetTLSAddr(), Emu.GetTLSFilesz());
g_tls_image_size = vm::cast(Emu.GetTLSFilesz(), "Emu.GetTLSFilesz");
sysPrxForUser->Warning("TLS initialized (g_tls_size=0x%x, g_tls_start=0x%x, g_tls_image_addr=0x%x, g_tls_image_size=0x%x)", g_tls_size, g_tls_start, g_tls_image_addr, g_tls_image_size);
} }
for (u32 i = 0; i < TLS_MAX; i++) for (u32 i = 0; i < TLS_MAX; i++)
{ {
if (g_tls_owners[i] == thread) if (g_tls_owners[i] == thread)
{ {
return g_tls_start + i * g_tls_size; // if already initialized, return TLS address return g_tls_start + i * Emu.GetTLSMemsz(); // if already initialized, return TLS address
} }
} }
@ -61,9 +56,9 @@ u32 ppu_get_tls(u32 thread)
u32 old = 0; u32 old = 0;
if (g_tls_owners[i].compare_exchange_strong(old, thread)) if (g_tls_owners[i].compare_exchange_strong(old, thread))
{ {
const u32 addr = g_tls_start + i * g_tls_size; // get TLS address const u32 addr = g_tls_start + i * Emu.GetTLSMemsz(); // get TLS address
memset(vm::get_ptr(addr), 0, g_tls_size); // fill TLS area with zeros memset(vm::get_ptr(addr), 0, Emu.GetTLSMemsz()); // fill TLS area with zeros
memcpy(vm::get_ptr(addr), vm::get_ptr(g_tls_image_addr), g_tls_image_size); // initialize from TLS image memcpy(vm::get_ptr(addr), vm::get_ptr(Emu.GetTLSAddr()), Emu.GetTLSFilesz()); // initialize from TLS image
return addr; return addr;
} }
} }
@ -420,10 +415,7 @@ void sysPrxForUser_init(Module *pxThis)
{ {
sysPrxForUser = pxThis; sysPrxForUser = pxThis;
g_tls_size = 0;
g_tls_start = 0; g_tls_start = 0;
g_tls_image_addr = 0;
g_tls_image_size = 0;
for (auto& v : g_tls_owners) for (auto& v : g_tls_owners)
{ {
v.store(0, std::memory_order_relaxed); v.store(0, std::memory_order_relaxed);

View file

@ -28,9 +28,9 @@ struct VFS;
struct EmuInfo struct EmuInfo
{ {
private: private:
u64 tls_addr; u32 tls_addr;
u64 tls_filesz; u32 tls_filesz;
u64 tls_memsz; u32 tls_memsz;
sys_process_param_info proc_param; sys_process_param_info proc_param;
@ -50,16 +50,16 @@ public:
proc_param.primary_prio = be_t<s32>::make(0x50); proc_param.primary_prio = be_t<s32>::make(0x50);
} }
void SetTLSData(const u64 addr, const u64 filesz, const u64 memsz) void SetTLSData(u32 addr, u32 filesz, u32 memsz)
{ {
tls_addr = addr; tls_addr = addr;
tls_filesz = filesz; tls_filesz = filesz;
tls_memsz = memsz; tls_memsz = memsz;
} }
u64 GetTLSAddr() const { return tls_addr; } u32 GetTLSAddr() const { return tls_addr; }
u64 GetTLSFilesz() const { return tls_filesz; } u32 GetTLSFilesz() const { return tls_filesz; }
u64 GetTLSMemsz() const { return tls_memsz; } u32 GetTLSMemsz() const { return tls_memsz; }
}; };
class ModuleInitializer class ModuleInitializer
@ -173,7 +173,7 @@ public:
m_modules_init.push_back(std::move(m)); m_modules_init.push_back(std::move(m));
} }
void SetTLSData(const u64 addr, const u64 filesz, const u64 memsz) void SetTLSData(u32 addr, u32 filesz, u32 memsz)
{ {
m_info.SetTLSData(addr, filesz, memsz); m_info.SetTLSData(addr, filesz, memsz);
} }
@ -195,9 +195,9 @@ public:
EmuInfo& GetInfo() { return m_info; } EmuInfo& GetInfo() { return m_info; }
u64 GetTLSAddr() const { return m_info.GetTLSAddr(); } u32 GetTLSAddr() const { return m_info.GetTLSAddr(); }
u64 GetTLSFilesz() const { return m_info.GetTLSFilesz(); } u32 GetTLSFilesz() const { return m_info.GetTLSFilesz(); }
u64 GetTLSMemsz() const { return m_info.GetTLSMemsz(); } u32 GetTLSMemsz() const { return m_info.GetTLSMemsz(); }
u32 GetMallocPageSize() { return m_info.GetProcParam().malloc_pagesize; } u32 GetMallocPageSize() { return m_info.GetProcParam().malloc_pagesize; }

View file

@ -11,6 +11,8 @@
#include "Emu/ARMv7/PSVFuncList.h" #include "Emu/ARMv7/PSVFuncList.h"
#include "Emu/System.h" #include "Emu/System.h"
extern void armv7_init_tls();
namespace loader namespace loader
{ {
namespace handlers namespace handlers
@ -235,9 +237,13 @@ namespace loader
} }
else if (!strcmp(name.c_str(), ".tbss")) else if (!strcmp(name.c_str(), ".tbss"))
{ {
LOG_NOTICE(LOADER, ".tbss analysis"); LOG_NOTICE(LOADER, ".tbss analysis...");
const u32 img_addr = shdr.data_le.sh_addr; // start address of TLS initialization image
const u32 img_size = (&shdr)[1].data_le.sh_addr - img_addr; // calculate its size as the difference between sections
const u32 tls_size = shdr.data_le.sh_size; // full size of TLS
LOG_ERROR(LOADER, "TLS: size=0x%08x", shdr.data_le.sh_size); LOG_WARNING(LOADER, "TLS: img_addr=0x%08x, img_size=0x%x, tls_size=0x%x", img_addr, img_size, tls_size);
Emu.SetTLSData(img_addr, img_size, tls_size);
} }
else if (!strcmp(name.c_str(), ".sceRefs.rodata")) else if (!strcmp(name.c_str(), ".sceRefs.rodata"))
{ {
@ -314,6 +320,7 @@ namespace loader
} }
} }
armv7_init_tls();
armv7_decoder_initialize(code_start, code_end); armv7_decoder_initialize(code_start, code_end);
arm7_thread(entry & ~1 /* TODO: Thumb/ARM encoding selection */, "main_thread").args({ Emu.GetPath()/*, "-emu"*/ }).run(); arm7_thread(entry & ~1 /* TODO: Thumb/ARM encoding selection */, "main_thread").args({ Emu.GetPath()/*, "-emu"*/ }).run();

View file

@ -422,7 +422,10 @@ namespace loader
break; break;
case 0x00000007: //TLS case 0x00000007: //TLS
Emu.SetTLSData(phdr.p_vaddr.addr(), phdr.p_filesz.value(), phdr.p_memsz.value()); Emu.SetTLSData(
vm::cast(phdr.p_vaddr.addr(), "TLS: phdr.p_vaddr"),
vm::cast(phdr.p_filesz.value(), "TLS: phdr.p_filesz"),
vm::cast(phdr.p_memsz.value(), "TLS: phdr.p_memsz"));
break; break;
case 0x60000001: //LOOS+1 case 0x60000001: //LOOS+1