Files
flycast/core/linux/common.cpp
T
Flyinghead b9df7d3469 network digital outputs: check config option when game starts
Avoid the need to restart when enabling or disabling this option
Send game name to newly connected sockets
ignore SIGPIPE (telnet)
Log network errors
2025-02-04 17:46:02 +01:00

196 lines
4.9 KiB
C++

#include "types.h"
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
#if defined(__APPLE__)
#define _XOPEN_SOURCE 1
#define __USE_GNU 1
#include <TargetConditionals.h>
#endif
#include <csignal>
#include <sys/time.h>
#if defined(__linux__) && !defined(__ANDROID__)
#include <sys/personality.h>
#endif
#include <unistd.h>
#ifdef __linux__
#include <pthread.h>
#endif
#include "oslib/host_context.h"
#include "hw/sh4/dyna/ngen.h"
#include "rend/TexCache.h"
#include "hw/mem/addrspace.h"
#include "hw/mem/mem_watch.h"
#include "emulator.h"
#ifdef __SWITCH__
#include <ucontext.h>
extern "C" char __start__;
#define siginfo_t switch_siginfo_t
#endif // __SWITCH__
void context_from_segfault(host_context_t* hctx, void* segfault_ctx);
void context_to_segfault(host_context_t* hctx, void* segfault_ctx);
#ifndef __SWITCH__
static struct sigaction next_segv_handler;
#endif
#if defined(__APPLE__)
static struct sigaction next_bus_handler;
#endif
void fault_handler(int sn, siginfo_t * si, void *segfault_ctx)
{
// Ram watcher for net rollback
if (memwatch::writeAccess(si->si_addr))
return;
// code protection in RAM
if (bm_RamWriteAccess(si->si_addr))
return;
// texture protection in VRAM
if (VramLockedWrite((u8*)si->si_addr))
return;
#if FEAT_SHREC == DYNAREC_JIT
// FPCB jump table protection
if (addrspace::bm_lockedWrite((u8*)si->si_addr))
return;
// fast mem access rewriting
host_context_t ctx;
context_from_segfault(&ctx, segfault_ctx);
if (sh4Dynarec->rewrite(ctx, si->si_addr))
{
context_to_segfault(&ctx, segfault_ctx);
return;
}
ERROR_LOG(COMMON, "SIGSEGV @ %p invalid access to %p", (void *)ctx.pc, si->si_addr);
#endif
#ifdef __SWITCH__
MemoryInfo meminfo;
u32 pageinfo;
svcQueryMemory(&meminfo, &pageinfo, (u64)&__start__);
ERROR_LOG(COMMON, ".text base: %p -> offset: %lx", (void*)meminfo.addr, ctx.pc - meminfo.addr);
#else
if (next_segv_handler.sa_sigaction != nullptr)
next_segv_handler.sa_sigaction(sn, si, segfault_ctx);
else
#endif // !__SWITCH__
die("segfault");
}
#undef HOST_CTX_READY
void os_InstallFaultHandler()
{
#ifndef __SWITCH__
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, nullptr);
act.sa_sigaction = fault_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, &next_segv_handler);
#endif
#if defined(__APPLE__)
//this is broken on osx/ios/mach in general
sigaction(SIGBUS, &act, &next_bus_handler);
#endif
}
void os_UninstallFaultHandler()
{
#ifndef __SWITCH__
sigaction(SIGSEGV, &next_segv_handler, nullptr);
#endif
#if defined(__APPLE__)
sigaction(SIGBUS, &next_bus_handler, nullptr);
#endif
}
#if !defined(__unix__) && !defined(LIBRETRO) && !defined(__SWITCH__)
[[noreturn]] void os_DebugBreak()
{
__builtin_trap();
}
#endif
// RunFast mode is the combination of the following conditions:
// * the VFP11 coprocessor is in flush-to-zero mode
// * the VFP11 coprocessor is in default NaN mode
// * all exception enable bits are cleared.
static void enable_runfast()
{
#if HOST_CPU == CPU_ARM && !defined(ARMCC)
static const unsigned int x = 0x04086060; // reset and disable FP exceptions, flush-to-zero, default NaN mode
static const unsigned int y = 0x03000000; // round to zero
int r;
asm volatile (
"fmrx %0, fpscr \n\t" //r0 = FPSCR
"and %0, %0, %1 \n\t" //r0 = r0 & 0x04086060
"orr %0, %0, %2 \n\t" //r0 = r0 | 0x03000000
"fmxr fpscr, %0 \n\t" //FPSCR = r0
: "=r"(r)
: "r"(x), "r"(y)
);
DEBUG_LOG(BOOT, "ARM VFP-Run Fast (NFP) enabled !");
#endif
}
// Some old CPUs lack the NX (no exec) flag so READ_IMPLIES_EXEC is set by default on these platforms.
// However resetting the flag isn't going to magically change the way the CPU works. So I wonder how useful this is.
// It's not needed on modern 64-bit architectures anyway.
static void linux_fix_personality()
{
#if defined(__linux__) && !defined(__ANDROID__) && (HOST_CPU == CPU_X86 || HOST_CPU == CPU_ARM)
DEBUG_LOG(BOOT, "Personality: %08X", personality(0xFFFFFFFF));
personality(~READ_IMPLIES_EXEC & personality(0xFFFFFFFF));
DEBUG_LOG(BOOT, "Updated personality: %08X", personality(0xFFFFFFFF));
#endif
}
#if defined(__unix__) && !defined(LIBRETRO) && !defined(__ANDROID__)
static void sigintHandler(int)
{
dc_exit();
}
#endif
void common_linux_setup()
{
linux_fix_personality();
enable_runfast();
os_InstallFaultHandler();
#if defined(__unix__) && !defined(LIBRETRO) && !defined(__ANDROID__)
// exit cleanly on ^C
signal(SIGINT, sigintHandler);
#endif
DEBUG_LOG(BOOT, "Linux paging: %ld %08X %08X", sysconf(_SC_PAGESIZE), PAGE_SIZE, PAGE_MASK);
verify(PAGE_MASK==(sysconf(_SC_PAGESIZE)-1));
}
#ifndef __APPLE__
void os_SetThreadName(const char *name)
{
#ifdef __linux__
if (strlen(name) > 16)
{
static char tmp[17];
strncpy(tmp, name, 16);
name = tmp;
}
pthread_setname_np(pthread_self(), name);
#endif
}
#endif
#endif // __unix__ or __APPLE__ or __SWITCH__