port: Add x64 FreeBSD

This commit is contained in:
lizzie 2026-01-15 12:27:34 +00:00
parent 2334981be8
commit b2be5785d5
16 changed files with 129 additions and 46 deletions

View File

@ -245,7 +245,10 @@ find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
find_package(ZLIB 1.3 MODULE)
# Outdated zydis from ports
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
find_package(Zydis 5.0.0 CONFIG)
endif()
find_package(pugixml 1.14 CONFIG)
if (APPLE)
find_package(date 3.0.1 CONFIG)

View File

@ -3,7 +3,7 @@
#pragma once
#ifdef __linux__
#if __unix__
#include <pthread.h>
#endif

View File

@ -7,6 +7,9 @@
#ifdef _WIN32
#include <windows.h>
#elif defined(__FreeBSD__)
#include <sys/ucontext.h>
#include <machine/npx.h>
#else
#include <sys/ucontext.h>
#endif
@ -22,6 +25,16 @@ void* GetXmmPointer(void* ctx, u8 index) {
#define CASE(index) \
case index: \
return (void*)(&((ucontext_t*)ctx)->uc_mcontext->__fs.__fpu_xmm##index);
#elif defined(__FreeBSD__)
// In mc_fpstate
// See <machine/npx.h> for the internals of mc_fpstate[].
#define CASE(index) \
case index: { \
auto& mctx = ((ucontext_t*)ctx)->uc_mcontext; \
ASSERT(mctx.mc_fpformat == _MC_FPFMT_XMM); \
auto* s_fpu = (struct savefpu*)(&mctx.mc_fpstate[0]); \
return (void*)(&(s_fpu->sv_xmm[0])); \
}
#else
#define CASE(index) \
case index: \
@ -57,6 +70,8 @@ void* GetRip(void* ctx) {
return (void*)((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip;
#elif defined(__APPLE__)
return (void*)((ucontext_t*)ctx)->uc_mcontext->__ss.__rip;
#elif defined(__FreeBSD__)
return (void*)((ucontext_t*)ctx)->uc_mcontext.mc_rip;
#else
return (void*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP];
#endif
@ -67,6 +82,8 @@ void IncrementRip(void* ctx, u64 length) {
((EXCEPTION_POINTERS*)ctx)->ContextRecord->Rip += length;
#elif defined(__APPLE__)
((ucontext_t*)ctx)->uc_mcontext->__ss.__rip += length;
#elif defined(__FreeBSD__)
((ucontext_t*)ctx)->uc_mcontext.mc_rip += length;
#else
((ucontext_t*)ctx)->uc_mcontext.gregs[REG_RIP] += length;
#endif
@ -75,18 +92,16 @@ void IncrementRip(void* ctx, u64 length) {
bool IsWriteError(void* ctx) {
#if defined(_WIN32)
return ((EXCEPTION_POINTERS*)ctx)->ExceptionRecord->ExceptionInformation[0] == 1;
#elif defined(__APPLE__)
#if defined(ARCH_X86_64)
#elif defined(__APPLE__) && defined(ARCH_X86_64)
return ((ucontext_t*)ctx)->uc_mcontext->__es.__err & 0x2;
#elif defined(ARCH_ARM64)
#elif defined(__APPLE__) && defined(ARCH_ARM64)
return ((ucontext_t*)ctx)->uc_mcontext->__es.__esr & 0x40;
#endif
#else
#if defined(ARCH_X86_64)
#elif defined(__FreeBSD__) && defined(ARCH_X86_64)
return ((ucontext_t*)ctx)->uc_mcontext.mc_err & 0x2;
#elif defined(ARCH_X86_64)
return ((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ERR] & 0x2;
#else
#error "Unsupported architecture"
#endif
#endif
}
} // namespace Common
} // namespace Common

View File

@ -613,7 +613,11 @@ struct AddressSpace::Impl {
user_size = UserSize;
constexpr int protection_flags = PROT_READ | PROT_WRITE;
constexpr int map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED;
int map_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; //compiler knows its constexpr
#if !defined(__FreeBSD__)
map_flags |= MAP_NORESERVE;
#endif
#if defined(__APPLE__) && defined(ARCH_X86_64)
// On ARM64 Macs, we run into limitations due to the commpage from 0xFC0000000 - 0xFFFFFFFFF
// and the GPU carveout region from 0x1000000000 - 0x6FFFFFFFFF. Because this creates gaps
@ -675,6 +679,12 @@ struct AddressSpace::Impl {
throw std::bad_alloc{};
}
shm_unlink(shm_path.c_str());
#elif defined(__FreeBSD__)
backing_fd = memfd_create("BackingDmem", 0);
if (backing_fd < 0) {
LOG_CRITICAL(Kernel_Vmm, "memfd_create failed: {}", strerror(errno));
throw std::bad_alloc{};
}
#else
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);

View File

@ -12,7 +12,7 @@
#elif defined(__linux__)
#include <filesystem>
#include <fstream>
#elif defined(__APPLE__)
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <errno.h>
#include <signal.h>
#include <sys/sysctl.h>
@ -48,6 +48,8 @@ bool Core::Debugger::IsDebuggerAttached() {
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
return false;
#elif defined(__FreeBSD__)
return false;
#else
#error "Unsupported platform"
#endif
@ -66,7 +68,7 @@ void Core::Debugger::WaitForDebuggerAttach() {
int Core::Debugger::GetCurrentPid() {
#if defined(_WIN32)
return GetCurrentProcessId();
#elif defined(__APPLE__) || defined(__linux__)
#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__)
return getpid();
#else
#error "Unsupported platform"
@ -88,7 +90,7 @@ void Core::Debugger::WaitForPid(int pid) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
}
#elif defined(__APPLE__)
#elif defined(__APPLE__) || defined(__FreeBSD__)
while (kill(pid, 0) == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;

View File

@ -215,6 +215,28 @@ void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context
ctx.uc_mcontext.mc_gs = regs.__gs;
ctx.uc_mcontext.mc_rip = regs.__rip;
ctx.uc_mcontext.mc_addr = reinterpret_cast<uint64_t>(inf->si_addr);
#elif defined(__FreeBSD__)
const auto& regs = raw_context->uc_mcontext;
ctx.uc_mcontext.mc_r8 = regs.mc_r8;
ctx.uc_mcontext.mc_r9 = regs.mc_r9;
ctx.uc_mcontext.mc_r10 = regs.mc_r10;
ctx.uc_mcontext.mc_r11 = regs.mc_r11;
ctx.uc_mcontext.mc_r12 = regs.mc_r12;
ctx.uc_mcontext.mc_r13 = regs.mc_r13;
ctx.uc_mcontext.mc_r14 = regs.mc_r14;
ctx.uc_mcontext.mc_r15 = regs.mc_r15;
ctx.uc_mcontext.mc_rdi = regs.mc_rdi;
ctx.uc_mcontext.mc_rsi = regs.mc_rsi;
ctx.uc_mcontext.mc_rbp = regs.mc_rbp;
ctx.uc_mcontext.mc_rbx = regs.mc_rbx;
ctx.uc_mcontext.mc_rdx = regs.mc_rdx;
ctx.uc_mcontext.mc_rax = regs.mc_rax;
ctx.uc_mcontext.mc_rcx = regs.mc_rcx;
ctx.uc_mcontext.mc_rsp = regs.mc_rsp;
ctx.uc_mcontext.mc_fs = regs.mc_fs;
ctx.uc_mcontext.mc_gs = regs.mc_gs;
ctx.uc_mcontext.mc_rip = regs.mc_rip;
ctx.uc_mcontext.mc_addr = uint64_t(regs.mc_addr);
#else
const auto& regs = raw_context->uc_mcontext.gregs;
ctx.uc_mcontext.mc_r8 = regs[REG_R8];

View File

@ -500,8 +500,10 @@ s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
if (dst_sec != nullptr) {
*dst_sec = res == TIME_ZONE_ID_DAYLIGHT ? -_dstbias : 0;
}
#elif defined(__FreeBSD__)
// todo
#else
#ifdef __APPLE__
#if defined(__APPLE__)
// std::chrono::current_zone() not available yet.
const auto* time_zone = date::current_zone();
#else

View File

@ -663,10 +663,12 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
return ORBIS_NET_ERROR_EBADF;
}
#ifndef __FreeBSD__
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
.data = {.fd = id}};
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_ADD, *native_handle, &native_event) == 0);
epoll->events.emplace_back(id, *event);
#endif
break;
}
case Core::FileSys::FileType::Resolver: {
@ -703,6 +705,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
switch (file->type) {
case Core::FileSys::FileType::Socket: {
<<<<<<< HEAD
auto native_handle = file->socket->Native();
if (!native_handle) {
// P2P socket, cannot be modified in epoll
@ -711,10 +714,14 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
return ORBIS_NET_ERROR_EBADF;
}
=======
#ifndef __FreeBSD__
>>>>>>> fe75987 (port: Add x64 FreeBSD)
epoll_event native_event = {.events = ConvertEpollEventsIn(event->events),
.data = {.fd = id}};
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_MOD, *native_handle, &native_event) == 0);
*it = {id, *event};
#endif
break;
}
default:
@ -745,6 +752,7 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
switch (file->type) {
case Core::FileSys::FileType::Socket: {
<<<<<<< HEAD
auto native_handle = file->socket->Native();
if (!native_handle) {
// P2P socket, cannot be removed from epoll
@ -754,7 +762,13 @@ int PS4_SYSV_ABI sceNetEpollControl(OrbisNetId epollid, OrbisNetEpollFlag op, Or
}
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *native_handle, nullptr) == 0);
=======
#ifndef __FreeBSD__
ASSERT(epoll_ctl(epoll->epoll_fd, EPOLL_CTL_DEL, *file->socket->Native(), nullptr) ==
0);
>>>>>>> fe75987 (port: Add x64 FreeBSD)
epoll->events.erase(it);
#endif
break;
}
case Core::FileSys::FileType::Resolver: {
@ -810,6 +824,9 @@ int PS4_SYSV_ABI sceNetEpollDestroy(OrbisNetId epollid) {
int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events, int maxevents,
int timeout) {
#ifdef __FreeBSD__
return 0;
#else
auto file = FDTable::Instance()->GetEpoll(epollid);
if (!file) {
*sceNetErrnoLoc() = ORBIS_NET_EBADF;
@ -836,7 +853,6 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events,
}
int i = 0;
if (result < 0) {
LOG_ERROR(Lib_Net, "epoll_wait failed with {}", Common::GetLastErrorMsg());
switch (errno) {
@ -905,8 +921,8 @@ int PS4_SYSV_ABI sceNetEpollWait(OrbisNetId epollid, OrbisNetEpollEvent* events,
++i;
}
}
return i;
#endif
}
int* PS4_SYSV_ABI sceNetErrnoLoc() {

View File

@ -10,12 +10,14 @@ namespace Libraries::Net {
u32 ConvertEpollEventsIn(u32 orbis_events) {
u32 ret = 0;
#ifndef __FreeBSD__
if ((orbis_events & ORBIS_NET_EPOLLIN) != 0) {
ret |= EPOLLIN;
}
if ((orbis_events & ORBIS_NET_EPOLLOUT) != 0) {
ret |= EPOLLOUT;
}
#endif
return ret;
}
@ -23,6 +25,7 @@ u32 ConvertEpollEventsIn(u32 orbis_events) {
u32 ConvertEpollEventsOut(u32 epoll_events) {
u32 ret = 0;
#ifndef __FreeBSD__
if ((epoll_events & EPOLLIN) != 0) {
ret |= ORBIS_NET_EPOLLIN;
}
@ -35,6 +38,7 @@ u32 ConvertEpollEventsOut(u32 epoll_events) {
if ((epoll_events & EPOLLHUP) != 0) {
ret |= ORBIS_NET_EPOLLHUP;
}
#endif
return ret;
}

View File

@ -14,9 +14,13 @@
#include <wepoll.h>
#endif
#if defined(__linux__) || defined(__APPLE__)
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
// ADD libepoll-shim if using freebsd!
#include <sys/epoll.h>
#endif
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
int epoll_create1(int fd) { return 0; }
#endif
namespace Libraries::Net {
@ -82,4 +86,4 @@ private:
std::mutex m_mutex;
};
} // namespace Libraries::Net
} // namespace Libraries::Net

View File

@ -25,7 +25,7 @@ typedef int net_socket;
#include <net/if_dl.h>
#include <net/route.h>
#endif
#if __linux__
#if defined(__linux__) || defined(__FreeBSD__)
#include <fstream>
#include <iostream>
#include <sstream>
@ -81,6 +81,8 @@ bool NetUtilInternal::RetrieveEthernetAddr() {
}
freeifaddrs(ifap);
}
#elif defined(__FreeBSD__)
// todo
#else
ifreq ifr;
ifconf ifc;
@ -226,7 +228,8 @@ bool NetUtilInternal::RetrieveDefaultGateway() {
inet_ntop(AF_INET, gateAddr, str, sizeof(str));
this->default_gateway = str;
return true;
#elif defined(__FreeBSD__)
return true;
#else
std::ifstream route{"/proc/net/route"};
std::string line;
@ -398,4 +401,4 @@ int NetUtilInternal::ResolveHostname(const char* hostname, Libraries::Net::Orbis
return ret;
}
} // namespace NetUtil
} // namespace NetUtil

View File

@ -155,9 +155,9 @@ Tcb* GetTcbBase() {
return tcb;
}
#elif defined(ARCH_X86_64)
#elif defined(ARCH_X86_64) && !defined(__FreeBSD__)
// Other POSIX x86_64
// Linux x86_64
void SetTcbBase(void* image_address) {
const int ret = syscall(SYS_arch_prctl, ARCH_SET_GS, (unsigned long)image_address);

View File

@ -535,7 +535,7 @@ void Emulator::Restart(std::filesystem::path eboot_path,
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
#elif defined(__APPLE__) || defined(__linux__)
#elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__)
std::vector<char*> argv;
// Emulator executable

View File

@ -7,7 +7,7 @@
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#ifdef __linux__
#ifdef __unix__
#include "common/adaptive_mutex.h"
#else
#include "common/spin_lock.h"

View File

@ -36,8 +36,8 @@
namespace VideoCore {
constexpr size_t PAGE_SIZE = 4_KB;
constexpr size_t PAGE_BITS = 12;
constexpr size_t PM_PAGE_SIZE = 4_KB;
constexpr size_t PM_PAGE_BITS = 12;
struct PageManager::Impl {
struct PageState {
@ -85,7 +85,7 @@ struct PageManager::Impl {
};
static constexpr size_t ADDRESS_BITS = 40;
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PAGE_BITS);
static constexpr size_t NUM_ADDRESS_PAGES = 1ULL << (40 - PM_PAGE_BITS);
static constexpr size_t NUM_ADDRESS_LOCKS = NUM_ADDRESS_PAGES / PAGES_PER_LOCK;
inline static Vulkan::Rasterizer* rasterizer;
#ifdef ENABLE_USERFAULTFD
@ -222,8 +222,8 @@ struct PageManager::Impl {
void UpdatePageWatchers(VAddr addr, u64 size) {
RENDERER_TRACE;
size_t page = addr >> PAGE_BITS;
const u64 page_end = Common::DivCeil(addr + size, PAGE_SIZE);
size_t page = addr >> PM_PAGE_BITS;
const u64 page_end = Common::DivCeil(addr + size, PM_PAGE_SIZE);
// Acquire locks for the range of pages
const auto lock_start = locks.begin() + (page / PAGES_PER_LOCK);
@ -239,15 +239,15 @@ struct PageManager::Impl {
if (range_bytes > 0) {
RENDERER_TRACE;
// Perform pending (un)protect action
Protect(range_begin << PAGE_BITS, range_bytes, perms);
Protect(range_begin << PM_PAGE_BITS, range_bytes, perms);
range_bytes = 0;
potential_range_bytes = 0;
}
};
// Iterate requested pages
const u64 aligned_addr = page << PAGE_BITS;
const u64 aligned_end = page_end << PAGE_BITS;
const u64 aligned_addr = page << PM_PAGE_BITS;
const u64 aligned_end = page_end << PM_PAGE_BITS;
if (!rasterizer->IsMapped(aligned_addr, aligned_end - aligned_addr)) {
LOG_WARNING(Render,
"Tracking memory region {:#x} - {:#x} which is not fully GPU mapped.",
@ -266,7 +266,7 @@ struct PageManager::Impl {
perms = new_perms;
} else if (range_bytes != 0) {
// If the protection did not change, extend the potential range
potential_range_bytes += PAGE_SIZE;
potential_range_bytes += PM_PAGE_SIZE;
}
// Only start a new range if the page must be (un)protected
@ -274,7 +274,7 @@ struct PageManager::Impl {
if (range_bytes == 0) {
// Start a new potential range
range_begin = page;
potential_range_bytes = PAGE_SIZE;
potential_range_bytes = PM_PAGE_SIZE;
}
// Extend current range up to potential range
range_bytes = potential_range_bytes;
@ -293,12 +293,12 @@ struct PageManager::Impl {
if (start_range.second == end_range.second) {
// if all pages are contiguous, use the regular UpdatePageWatchers
const VAddr start_addr = base_addr + (start_range.first << PAGE_BITS);
const u64 size = (start_range.second - start_range.first) << PAGE_BITS;
const VAddr start_addr = base_addr + (start_range.first << PM_PAGE_BITS);
const u64 size = (start_range.second - start_range.first) << PM_PAGE_BITS;
return UpdatePageWatchers<track, is_read>(start_addr, size);
}
size_t base_page = (base_addr >> PAGE_BITS);
size_t base_page = (base_addr >> PM_PAGE_BITS);
ASSERT(base_page % PAGES_PER_LOCK == 0);
std::scoped_lock lk(locks[base_page / PAGES_PER_LOCK]);
auto perms = cached_pages[base_page + start_range.first].Perms();
@ -310,7 +310,7 @@ struct PageManager::Impl {
if (range_bytes > 0) {
RENDERER_TRACE;
// Perform pending (un)protect action
Protect((range_begin << PAGE_BITS), range_bytes, perms);
Protect((range_begin << PM_PAGE_BITS), range_bytes, perms);
range_bytes = 0;
potential_range_bytes = 0;
}
@ -331,7 +331,7 @@ struct PageManager::Impl {
perms = new_perms;
} else if (range_bytes != 0) {
// If the protection did not change, extend the potential range
potential_range_bytes += PAGE_SIZE;
potential_range_bytes += PM_PAGE_SIZE;
}
// If the page is not being updated, skip it
@ -344,7 +344,7 @@ struct PageManager::Impl {
if (range_bytes == 0) {
// Start a new potential range
range_begin = base_page + page;
potential_range_bytes = PAGE_SIZE;
potential_range_bytes = PM_PAGE_SIZE;
}
// Extend current rango up to potential range
range_bytes = potential_range_bytes;
@ -356,7 +356,7 @@ struct PageManager::Impl {
}
std::array<PageState, NUM_ADDRESS_PAGES> cached_pages{};
#ifdef __linux__
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
using LockType = Common::AdaptiveMutex;
#else
using LockType = Common::SpinLock;

View File

@ -3,6 +3,7 @@
#pragma once
#include <cstddef>
#include <memory>
#include "common/alignment.h"
#include "common/types.h"
@ -15,9 +16,10 @@ class Rasterizer;
namespace VideoCore {
class PageManager {
// PAGE_SIZE and PAGE_BITS conflicts with machine/param.h definitions on freebsd!
// Use the same page size as the tracker.
static constexpr size_t PAGE_BITS = TRACKER_PAGE_BITS;
static constexpr size_t PAGE_SIZE = TRACKER_BYTES_PER_PAGE;
static constexpr size_t PM_PAGE_BITS = TRACKER_PAGE_BITS;
static constexpr size_t PM_PAGE_SIZE = TRACKER_BYTES_PER_PAGE;
// Keep the lock granularity the same as region granularity. (since each regions has
// itself a lock)
@ -43,12 +45,12 @@ public:
/// Returns page aligned address.
static constexpr VAddr GetPageAddr(VAddr addr) {
return Common::AlignDown(addr, PAGE_SIZE);
return Common::AlignDown(addr, PM_PAGE_SIZE);
}
/// Returns address of the next page.
static constexpr VAddr GetNextPageAddr(VAddr addr) {
return Common::AlignUp(addr + 1, PAGE_SIZE);
return Common::AlignUp(addr + 1, PM_PAGE_SIZE);
}
private: