sysutil/audio: Implement shared memory handling (#19467)

This commit is contained in:
Yahfz
2026-09-13 08:39:10 +00:00
committed by GitHub
parent 6ba56a52a4
commit 43cedf366b
15 changed files with 842 additions and 159 deletions
+358 -72
View File
@@ -6,6 +6,13 @@
#include "Emu/Cell/timers.hpp"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/Cell/lv2/sys_mmapper.h"
#include "Emu/Cell/lv2/sys_memory.h"
#include "Emu/Memory/vm_var.h"
#include "Emu/savestate_utils.hpp"
#include "util/asm.hpp"
#include "util/vm.hpp"
#include "sysPrxForUser.h"
#include "cellAudio.h"
#include "util/video_provider.h"
@@ -17,16 +24,6 @@ extern atomic_t<recording_mode> g_recording_mode;
extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr);
vm::gvar<char, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT> g_audio_buffer;
struct alignas(16) aligned_index_t
{
be_t<u64> index;
u8 pad[8];
};
vm::gvar<aligned_index_t, AUDIO_PORT_COUNT> g_audio_indices;
template <>
void fmt_class_string<CellAudioError>::format(std::string& out, u64 arg)
{
@@ -366,10 +363,8 @@ u64 audio_ringbuffer::update(bool emu_is_paused)
return timestamp;
}
void audio_port::tag(s32 offset)
void audio_port::tag(be_t<f32>* port_buf)
{
auto port_buf = get_vm_ptr(offset);
// This tag will be used to make sure that the game has finished writing the audio for the next audio period
// We use -0.0f in case games check if the buffer is empty. -0.0f == 0.0f evaluates to true, but std::signbit can be used to distinguish them
const f32 tag = -0.0f;
@@ -389,10 +384,72 @@ void audio_port::tag(s32 offset)
prev_touched_tag_nr = -1;
}
namespace
{
void save_audio_memory(utils::serial& ar, const shared_ptr<lv2_memory>& memory)
{
ar(static_cast<bool>(memory));
if (!memory)
{
return;
}
const bool registered = memory->exists || memory->key == SYS_MMAPPER_MIO_SHM_KEY;
ar(registered);
if (registered)
{
ar(memory->key);
}
else
{
memory->save_data(ar);
}
}
shared_ptr<lv2_memory> load_audio_memory(utils::serial& ar)
{
if (!ar.pop<bool>())
{
return {};
}
if (ar.pop<bool>())
{
return ensure(g_fxo->get<ipc_manager<lv2_memory, u64>>().get(ar.pop<u64>()));
}
auto memory = make_shared<lv2_memory>(stx::exact_t<utils::serial&>(ar));
ensure(memory->external_refs);
ensure((g_fxo->get<ipc_manager<lv2_memory, u64>>().add(memory->key, [&] { return memory; }).first));
return memory;
}
shared_ptr<lv2_memory> acquire_audio_memory(u32 id)
{
return idm::get<lv2_obj, lv2_memory>(id, [](lv2_memory& memory)
{
memory.external_refs++;
});
}
void release_audio_memory(shared_ptr<lv2_memory>& memory)
{
if (memory)
{
std::lock_guard lock(id_manager::g_mutex);
ensure(memory->external_refs--);
memory->release_memory();
memory.reset();
}
}
}
cell_audio_thread::cell_audio_thread(utils::serial& ar)
: cell_audio_thread()
{
ar(init);
ar(init, shared_area, shared_address, shared_refs, ports);
if (!init)
{
@@ -409,20 +466,26 @@ cell_audio_thread::cell_audio_thread(utils::serial& ar)
k.port = lv2_event_queue::load_ptr(ar, k.port, "audio");
}
ar(ports);
ar(free_port_count, free_ports, free_indices);
shared_memory = load_audio_memory(ar);
for (auto& memory : port_memories)
{
memory = load_audio_memory(ar);
}
}
void cell_audio_thread::save(utils::serial& ar)
{
ar(init);
USING_SERIALIZATION_VERSION(cellAudio);
// closed ports keep their descriptor after audio quits
ar(init, shared_area, shared_address, shared_refs, ports);
if (!init)
{
return;
}
USING_SERIALIZATION_VERSION(cellAudio);
ar(key_count, event_period);
ar(static_cast<u64>(keys.size()));
@@ -432,11 +495,24 @@ void cell_audio_thread::save(utils::serial& ar)
lv2_event_queue::save_ptr(ar, k.port.get());
}
ar(ports);
ar(free_port_count, free_ports, free_indices);
save_audio_memory(ar, shared_memory);
for (const auto& memory : port_memories)
{
save_audio_memory(ar, memory);
}
}
be_t<f32>* cell_audio_thread::get_buffer(const audio_port& port, s32 offset) const
{
// caller holds the audio mutex, port_memories keeps the buffer alive after the game unmaps it
return reinterpret_cast<be_t<f32>*>((*port_memories[port.number]->shm.observe())->get()) + port.position(offset) * port.block_size();
}
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
{
std::lock_guard lock(mutex);
AUDIT(cfg.buffering_enabled);
u32 active = 0;
@@ -451,7 +527,9 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
if (port.state != audio_port_state::started) continue;
active++;
auto port_buf = port.get_vm_ptr();
if (!port.num_channels) continue;
auto port_buf = get_buffer(port);
// Find the last tag that has been touched
const u32 tag_first_pos = port.num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
@@ -568,11 +646,12 @@ void cell_audio_thread::reset_ports(s32 offset)
if (port.state != audio_port_state::started) continue;
memset(port.get_vm_ptr(offset), 0, port.block_size() * sizeof(float));
auto* buffer = get_buffer(port, offset);
memset(buffer, 0, port.block_size() * sizeof(float));
if (cfg.buffering_enabled)
if (cfg.buffering_enabled && port.num_channels)
{
port.tag(offset);
port.tag(buffer);
// One period is about to elapse for this port. count_port_buffer_tags() zeroes this again as
// soon as it sees a tag move, so it counts periods during which none did.
@@ -593,6 +672,8 @@ void cell_audio_thread::advance(u64 timestamp)
// update ports
reset_ports(0);
be_t<u64>* indices = nullptr;
for (audio_port& port : ports)
{
if (port.state != audio_port_state::started) continue;
@@ -602,7 +683,13 @@ void cell_audio_thread::advance(u64 timestamp)
port.timestamp = timestamp;
port.cur_pos = port.position(1);
*port.index = port.cur_pos;
if (!indices)
{
indices = reinterpret_cast<be_t<u64>*>((*shared_memory->shm.observe())->get());
}
indices[port.server_index * 2] = port.cur_pos;
}
if (cfg.buffering_enabled)
@@ -1089,22 +1176,109 @@ void cell_audio_thread::operator()()
audio_port* cell_audio_thread::open_port()
{
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
if (!free_port_count)
{
if (ports[i].state.compare_and_swap_test(audio_port_state::closed, audio_port_state::opened))
{
m_front_only_reported[i] = false;
m_periods_without_tag[i] = 0;
return &ports[i];
}
return nullptr;
}
return nullptr;
const u32 index = --free_port_count;
auto& port = ports[free_ports[index]];
port.server_index = free_indices[index];
port.state = audio_port_state::opened;
m_front_only_reported[port.number] = false;
m_periods_without_tag[port.number] = 0;
return &port;
}
error_code cell_audio_thread::allocate_port(ppu_thread& ppu, audio_port& port)
{
const u32 size = std::max<u32>(0x10000, utils::align(port.size, 0x10000));
auto* ct = g_ps3_process_info.sdk_ver > 0x21ffff ? &g_fxo->get<lv2_memory_container>() : nullptr;
for (u64 key = SYS_MMAPPER_MIO_SHM_KEY + 2;; key++)
{
if (g_fxo->get<ipc_manager<lv2_memory, u64>>().get(key))
{
continue;
}
const auto result = lv2_obj::create<lv2_memory>(SYS_SYNC_PROCESS_SHARED, key, SYS_SYNC_NEWLY_CREATED, [&]()
{
return make_shared<lv2_memory>(size, 0x10000, 0x200, key, true, ct);
});
if (result + 0u == CELL_EEXIST)
{
continue;
}
if (result)
{
close_port(ppu, port);
return CELL_AUDIO_ERROR_SHAREDMEMORY;
}
const u32 memory_id = idm::last_id<lv2_memory>();
const vm::var<u32> address;
auto memory = acquire_audio_memory(memory_id);
if (!memory || sys_mmapper_search_and_map(ppu, shared_area, memory_id, 0x40000, address))
{
sys_mmapper_free_shared_memory(ppu, memory_id);
release_audio_memory(memory);
close_port(ppu, port);
return CELL_AUDIO_ERROR_SHAREDMEMORY;
}
port_memories[port.number] = std::move(memory);
port.addr = vm::cast(*address);
port.mapped = true;
port.index = vm::cast(shared_address + port.server_index * 16);
std::memset(port.addr.get_ptr(), 0, size);
break;
}
return CELL_OK;
}
void cell_audio_thread::close_port(ppu_thread& ppu, audio_port& port)
{
port.state = audio_port_state::closed;
const vm::var<u32> memory_id;
if (port.mapped && !sys_mmapper_unmap_shared_memory(ppu, port.addr.addr(), memory_id))
{
// an extra mapping can keep the handle alive after the port closes
sys_mmapper_free_shared_memory(ppu, *memory_id);
}
port.mapped = false;
release_audio_memory(port_memories[port.number]);
free_ports[free_port_count] = port.number;
free_indices[free_port_count] = port.server_index;
free_port_count++;
}
void cell_audio_thread::release_shared_memory(ppu_thread& ppu)
{
if (--shared_refs || !shared_address)
{
return;
}
const vm::var<u32> memory_id;
if (!sys_mmapper_unmap_shared_memory(ppu, shared_address, memory_id))
{
sys_mmapper_free_shared_memory(ppu, *memory_id);
}
}
template <AudioChannelCnt channels, AudioChannelCnt downmix>
void cell_audio_thread::mix(float* out_buffer, s32 offset)
{
std::lock_guard lock(mutex);
AUDIT(out_buffer != nullptr);
constexpr u32 out_channels = static_cast<u32>(channels);
@@ -1120,7 +1294,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
{
if (port.state != audio_port_state::started) continue;
auto buf = port.get_vm_ptr(offset);
auto buf = get_buffer(port, offset);
static constexpr float minus_3db = 0.707f; // value taken from https://www.dolby.com/us/en/technologies/a-guide-to-dolby-metadata.pdf
float m = master_volume;
@@ -1229,6 +1403,17 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
}
}
}
else if (!port.num_channels)
{
for (u32 out = 0, in = 0; out < out_buffer_sz; out += out_channels, in++)
{
step_volume(port);
const float sample = buf[in] * m;
out_buffer[out + 0] += sample;
out_buffer[out + 1] += sample;
}
}
else
{
fmt::throw_exception("Unknown channel count (port=%u, channel=%d)", port.number, port.num_channels);
@@ -1238,6 +1423,8 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
void cell_audio_thread::finish_port_volume_stepping()
{
std::lock_guard lock(mutex);
// part of cellAudioSetPortLevel functionality
for (audio_port& port : ports)
{
@@ -1249,12 +1436,19 @@ void cell_audio_thread::finish_port_volume_stepping()
}
}
error_code cellAudioInit()
error_code cellAudioInit(ppu_thread& ppu)
{
cellAudio.warning("cellAudioInit()");
auto& g_audio = g_fxo->get<cell_audio>();
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
auto& g_audio = g_fxo->get<cell_audio>();
std::lock_guard lock(g_audio.mutex);
if (g_audio.init)
@@ -1262,28 +1456,96 @@ error_code cellAudioInit()
return CELL_AUDIO_ERROR_ALREADY_INIT;
}
std::memset(g_audio_buffer.get_ptr(), 0, g_audio_buffer.alloc_size);
std::memset(g_audio_indices.get_ptr(), 0, g_audio_indices.alloc_size);
if (!g_audio.shared_refs++)
{
g_audio.shared_address = 0;
const vm::var<u32> memory_id;
const auto result = sys_mmapper_allocate_shared_memory(ppu, SYS_MMAPPER_MIO_SHM_KEY, 0x10000, 0x200, memory_id);
if (result)
{
g_audio.release_shared_memory(ppu);
// mio and audio both release this on an already open handle
if (result + 0u == CELL_EEXIST)
{
g_audio.release_shared_memory(ppu);
return CELL_AUDIO_ERROR_TRANS_EVENT;
}
return result;
}
const vm::var<u32> area;
const vm::var<u32> address;
auto memory = acquire_audio_memory(*memory_id);
if (!memory)
{
g_audio.release_shared_memory(ppu);
return CELL_ESRCH;
}
auto mapping_result = sys_mmapper_get_shared_memory_area(ppu, 0x20full, +area);
if (!mapping_result)
{
mapping_result = sys_mmapper_search_and_map(ppu, *area, *memory_id, 0x40000, address);
}
if (mapping_result)
{
sys_mmapper_free_shared_memory(ppu, *memory_id);
release_audio_memory(memory);
g_audio.release_shared_memory(ppu);
return mapping_result;
}
g_audio.shared_memory = std::move(memory);
g_audio.shared_area = *area;
g_audio.shared_address = *address;
}
else
{
g_audio.release_shared_memory(ppu);
return CELL_AUDIO_ERROR_TRANS_EVENT;
}
const u32 port_count = g_ps3_process_info.sdk_ver <= 0x35ffff ? CELL_AUDIO_MAX_PORT : CELL_AUDIO_MAX_PORT_2;
g_audio.free_port_count = port_count;
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
{
g_audio.ports[i].state = audio_port_state::closed;
g_audio.ports[i].mapped = false;
g_audio.ports[i].number = i;
g_audio.ports[i].addr = g_audio_buffer + AUDIO_PORT_OFFSET * i;
g_audio.ports[i].index = (g_audio_indices + i).ptr(&aligned_index_t::index);
g_audio.ports[i].state = audio_port_state::closed;
// closed ports keep their buffer fields and their slot in the new index mapping
g_audio.ports[i].index = vm::cast(g_audio.shared_address + g_audio.ports[i].server_index * 16);
if (i < port_count)
{
g_audio.free_ports[i] = port_count - i - 1;
g_audio.free_indices[i] = port_count - i;
}
}
g_audio.init = 1;
return CELL_OK;
}
error_code cellAudioQuit()
error_code cellAudioQuit(ppu_thread& ppu)
{
cellAudio.warning("cellAudioQuit()");
auto& g_audio = g_fxo->get<cell_audio>();
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
auto& g_audio = g_fxo->get<cell_audio>();
std::lock_guard lock(g_audio.mutex);
if (!g_audio.init)
@@ -1291,16 +1553,33 @@ error_code cellAudioQuit()
return CELL_AUDIO_ERROR_NOT_INIT;
}
for (auto& port : g_audio.ports)
{
if (port.state != audio_port_state::closed)
{
g_audio.close_port(ppu, port);
}
}
g_audio.release_shared_memory(ppu);
release_audio_memory(g_audio.shared_memory);
// NOTE: Do not clear event queues here. They are handled independently.
g_audio.init = 0;
return CELL_OK;
}
error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32> portNum)
error_code cellAudioPortOpen(ppu_thread& ppu, vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32> portNum)
{
cellAudio.warning("cellAudioPortOpen(audioParam=*0x%x, portNum=*0x%x)", audioParam, portNum);
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
auto& g_audio = g_fxo->get<cell_audio>();
if (!g_audio.init)
@@ -1386,13 +1665,10 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
return CELL_AUDIO_ERROR_PORT_FULL;
}
// TODO: is this necessary in any way? (Based on libaudio.prx)
//const u64 num_channels_non_0 = std::max<u64>(1, num_channels);
port->num_channels = ::narrow<u32>(num_channels);
port->num_blocks = ::narrow<u32>(num_blocks);
port->attr = attr;
port->size = ::narrow<u32>(num_channels * num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(f32));
port->size = ::narrow<u32>(std::max<u64>(1, num_channels) * num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(f32));
port->cur_pos = 0;
port->global_counter = g_audio.m_counter;
port->active_counter = 0;
@@ -1409,9 +1685,9 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
port->level_set.store({ port->level, 0.0f });
if (port->level <= -1.0f)
if (auto result = g_audio.allocate_port(ppu, *port))
{
return CELL_AUDIO_ERROR_PORT_OPEN;
return result;
}
*portNum = port->number;
@@ -1489,19 +1765,32 @@ error_code cellAudioPortStart(u32 portNum)
return CELL_AUDIO_ERROR_NOT_INIT;
}
switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started))
auto& port = g_audio.ports[portNum];
switch (audio_port_state state = port.state)
{
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
case audio_port_state::started: return CELL_AUDIO_ERROR_PORT_ALREADY_RUN;
case audio_port_state::opened: return CELL_OK;
case audio_port_state::opened:
std::memset(port.addr.get_ptr(), 0, port.size);
port.state = audio_port_state::started;
return CELL_OK;
default: fmt::throw_exception("Invalid port state (%d: %d)", portNum, static_cast<u32>(state));
}
}
error_code cellAudioPortClose(u32 portNum)
error_code cellAudioPortClose(ppu_thread& ppu, u32 portNum)
{
cellAudio.warning("cellAudioPortClose(portNum=%d)", portNum);
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
auto& g_audio = g_fxo->get<cell_audio>();
std::lock_guard lock(g_audio.mutex);
@@ -1516,13 +1805,15 @@ error_code cellAudioPortClose(u32 portNum)
return CELL_AUDIO_ERROR_PARAM;
}
switch (audio_port_state state = g_audio.ports[portNum].state.exchange(audio_port_state::closed))
auto& port = g_audio.ports[portNum];
if (port.state == audio_port_state::closed)
{
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
case audio_port_state::started: return CELL_OK;
case audio_port_state::opened: return CELL_OK;
default: fmt::throw_exception("Invalid port state (%d: %d)", portNum, static_cast<u32>(state));
return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
}
g_audio.close_port(ppu, port);
return CELL_OK;
}
error_code cellAudioPortStop(u32 portNum)
@@ -1904,7 +2195,7 @@ error_code cellAudioAddData(u32 portNum, vm::ptr<float> src, u32 samples, float
auto& g_audio = g_fxo->get<cell_audio>();
std::unique_lock lock(g_audio.mutex);
std::lock_guard lock(g_audio.mutex);
if (!g_audio.init)
{
@@ -1918,9 +2209,12 @@ error_code cellAudioAddData(u32 portNum, vm::ptr<float> src, u32 samples, float
const audio_port& port = g_audio.ports[portNum];
const auto dst = port.get_vm_ptr();
if (!port.num_channels)
{
return CELL_OK;
}
lock.unlock();
const auto dst = port.get_vm_ptr();
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
@@ -1938,7 +2232,7 @@ error_code cellAudioAdd2chData(u32 portNum, vm::ptr<float> src, u32 samples, flo
auto& g_audio = g_fxo->get<cell_audio>();
std::unique_lock lock(g_audio.mutex);
std::lock_guard lock(g_audio.mutex);
if (!g_audio.init)
{
@@ -1954,8 +2248,6 @@ error_code cellAudioAdd2chData(u32 portNum, vm::ptr<float> src, u32 samples, flo
const auto dst = port.get_vm_ptr();
lock.unlock();
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
if (port.num_channels == 2)
@@ -1994,7 +2286,7 @@ error_code cellAudioAdd6chData(u32 portNum, vm::ptr<float> src, float volume)
auto& g_audio = g_fxo->get<cell_audio>();
std::unique_lock lock(g_audio.mutex);
std::lock_guard lock(g_audio.mutex);
if (!g_audio.init)
{
@@ -2010,8 +2302,6 @@ error_code cellAudioAdd6chData(u32 portNum, vm::ptr<float> src, float volume)
const auto dst = port.get_vm_ptr();
lock.unlock();
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
if (port.num_channels == 8)
@@ -2131,10 +2421,6 @@ error_code cellAudioUnsetPersonalDevice(s32 iPersonalStream)
DECLARE(ppu_module_manager::cellAudio)("cellAudio", []()
{
// Private variables
REG_VAR(cellAudio, g_audio_buffer).flag(MFF_HIDDEN);
REG_VAR(cellAudio, g_audio_indices).flag(MFF_HIDDEN);
REG_FUNC(cellAudio, cellAudioInit);
REG_FUNC(cellAudio, cellAudioPortClose);
REG_FUNC(cellAudio, cellAudioPortStop);
+22 -9
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Utilities/Thread.h"
#include "Utilities/simple_ringbuf.h"
#include "Emu/Memory/vm.h"
@@ -10,6 +11,8 @@
#include "Emu/system_config_types.h"
struct lv2_event_queue;
struct lv2_memory;
class ppu_thread;
// Error codes
enum CellAudioError : u32
@@ -153,6 +156,8 @@ struct audio_port
atomic_t<audio_port_state> state = audio_port_state::closed;
u32 number = 0;
u32 server_index = 0;
bool mapped = false;
vm::ptr<char> addr{};
vm::ptr<u64> index{};
@@ -176,7 +181,7 @@ struct audio_port
u32 block_size() const
{
return num_channels * AUDIO_BUFFER_SAMPLES;
return std::max(num_channels, 1u) * AUDIO_BUFFER_SAMPLES;
}
u32 buf_size() const
@@ -190,14 +195,10 @@ struct audio_port
return (cur_pos + ofs) % num_blocks;
}
u32 buf_addr(s32 offset = 0) const
be_t<f32>* get_vm_ptr() const
{
return addr.addr() + position(offset) * buf_size();
}
be_t<f32>* get_vm_ptr(s32 offset = 0) const
{
return vm::_ptr<f32>(buf_addr(offset));
const u32 block = static_cast<u16>(*index) & (num_blocks - 1);
return vm::_ptr<f32>(addr.addr() + block * buf_size());
}
@@ -210,7 +211,7 @@ struct audio_port
return tag_nr * PORT_BUFFER_MARK_DELTA_SAMPLE * num_channels + PORT_BUFFER_MARK_CHANNEL;
}
void tag(s32 offset = 0);
void tag(be_t<f32>* port_buf);
audio_port() = default;
@@ -392,6 +393,7 @@ class cell_audio_thread
{
private:
std::unique_ptr<audio_ringbuffer> ringbuffer{};
be_t<f32>* get_buffer(const audio_port& port, s32 offset = 0) const;
void reset_ports(s32 offset = 0);
void advance(u64 timestamp);
@@ -410,6 +412,14 @@ public:
shared_mutex mutex{};
atomic_t<u8> init = 0;
u32 shared_area = 0;
u32 shared_address = 0;
u32 shared_refs = 0;
shared_ptr<lv2_memory> shared_memory;
std::array<shared_ptr<lv2_memory>, AUDIO_PORT_COUNT> port_memories;
u32 free_port_count = 0;
std::array<u32, AUDIO_PORT_COUNT> free_ports{};
std::array<u32, AUDIO_PORT_COUNT> free_indices{};
u32 key_count = 0;
u8 event_period = 0;
@@ -457,6 +467,9 @@ public:
void save(utils::serial& ar);
audio_port* open_port();
error_code allocate_port(ppu_thread& ppu, audio_port& port);
void close_port(ppu_thread& ppu, audio_port& port);
void release_shared_memory(ppu_thread& ppu);
static constexpr auto thread_name = "cellAudio Thread"sv;
};
+111
View File
@@ -8,7 +8,11 @@
#include "Emu/Cell/lv2/sys_game.h"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/Cell/lv2/sys_mmapper.h"
#include "Emu/Cell/lv2/sys_prx.h"
#include "Emu/savestate_utils.hpp"
#include "cellSysutil.h"
#include "sysPrxForUser.h"
#include "Utilities/StrUtil.h"
#include "Utilities/lockless.h"
@@ -18,6 +22,110 @@
LOG_CHANNEL(cellSysutil);
namespace
{
struct sysutil_module
{
be_t<u32> start[2];
be_t<u32> stop[2];
be_t<u32> address;
be_t<u32> ref_count;
};
}
error_code sysutilModuleStart(ppu_thread& ppu, u32 args, vm::ptr<void> argp)
{
cellSysutil.notice("module_start(args=%d, argp=*0x%x)", args, argp);
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
const vm::ptr<sysutil_module> data = vm::cast(ppu.gpr[2]);
if (++data->ref_count != 1u)
{
return CELL_OK;
}
const vm::var<u32> memory_id;
// firmware still returns success if setup fails, a failed map leaves the handle around
if (sys_mmapper_allocate_shared_memory(ppu, SYS_MMAPPER_SYSUTIL_SHM_KEY, 0x10000, 0x200, memory_id))
{
return CELL_OK;
}
const vm::var<u32> area;
if (!sys_mmapper_get_shared_memory_area(ppu, 0x20full, +area))
{
sys_mmapper_search_and_map(ppu, *area, *memory_id, 0x40000, data.ptr(&sysutil_module::address));
}
return CELL_OK;
}
error_code sysutilModuleStop(ppu_thread& ppu)
{
cellSysutil.notice("module_stop()");
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
const vm::ptr<sysutil_module> data = vm::cast(ppu.gpr[2]);
if (--data->ref_count || !data->address)
{
return CELL_OK;
}
const vm::var<u32> memory_id;
if (!sys_mmapper_unmap_shared_memory(ppu, data->address, memory_id))
{
data->address = 0;
// another mapping can keep this busy, firmware leaves the handle around here too
sys_mmapper_free_shared_memory(ppu, *memory_id);
}
return CELL_OK;
}
bool sysutilModuleInit(lv2_prx& prx)
{
if (!prx.hle_data)
{
prx.hle_data = vm::alloc(sizeof(sysutil_module), vm::main);
if (!prx.hle_data)
{
return false;
}
*vm::ptr<sysutil_module>{vm::cast(prx.hle_data)} = {};
}
const vm::ptr<sysutil_module> data = vm::cast(prx.hle_data);
const auto& funcs = g_fxo->get<ppu_function_manager>();
data->start[0] = funcs.func_addr(FIND_FUNC(sysutilModuleStart), true);
data->start[1] = data.addr();
data->stop[0] = funcs.func_addr(FIND_FUNC(sysutilModuleStop), true);
data->stop[1] = data.addr();
prx.start = vm::cast(data.ptr(&sysutil_module::start).addr());
prx.stop = vm::cast(data.ptr(&sysutil_module::stop).addr());
return true;
}
template<>
void fmt_class_string<CellSysutilError>::format(std::string& out, u64 arg)
{
@@ -1122,6 +1230,9 @@ extern void cellSysutil_SysCache_init();
DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []()
{
REG_HIDDEN_FUNC(sysutilModuleStart);
REG_HIDDEN_FUNC(sysutilModuleStop);
cellSysutil_SaveData_init(); // cellSaveData functions
cellSysutil_GameData_init(); // cellGameData, cellHddGame functions
cellSysutil_MsgDialog_init(); // cellMsgDialog functions
+2
View File
@@ -19,6 +19,8 @@ extern spu_printf_cb_t g_spu_printf_dtcb;
// Functions
error_code sys_mmapper_get_shared_memory_area(ppu_thread& ppu, u64 flags, vm::ptr<u32> address);
vm::ptr<void> _sys_memset(vm::ptr<void> dst, s32 value, u32 size);
struct sys_lwmutex_t;
+55
View File
@@ -1,9 +1,61 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_mmapper.h"
#include "Emu/savestate_utils.hpp"
#include "sysPrxForUser.h"
LOG_CHANNEL(sysPrxForUser);
u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name);
namespace
{
vm::gvar<u32> s_shared_memory_area;
struct shared_memory_area_lock
{
shared_mutex mutex;
};
}
error_code sys_mmapper_get_shared_memory_area(ppu_thread& ppu, u64 flags, vm::ptr<u32> address)
{
if (const u32 entry = ppu_get_exported_func_addr(0xb5d5f64e, "sysPrxForUser"))
{
return vm::ptr<error_code(u64, vm::ptr<u32>)>{vm::cast(entry)}(ppu, flags, address);
}
ppu.state += cpu_flag::wait;
sysPrxForUser.notice("sys_mmapper_get_shared_memory_area(flags=0x%llx, address=*0x%x)", flags, address);
if ((flags & 0xf00) != 0x200 || ((flags & 0xff) != 0 && (flags & 0xff) != 0xf))
{
return CELL_EINVAL;
}
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
std::lock_guard lock{g_fxo->get<shared_memory_area_lock>().mutex};
if (!*s_shared_memory_area)
{
if (const auto ret = sys_mmapper_allocate_address(ppu, 0x10000000, 0x20f, 0x10000000, s_shared_memory_area))
{
return ret;
}
}
*address = *s_shared_memory_area;
return CELL_OK;
}
error_code sys_mmapper_allocate_memory(ppu_thread& ppu, u32 size, u64 flags, vm::ptr<u32> mem_id)
{
sysPrxForUser.notice("sys_mmapper_allocate_memory(size=0x%x, flags=0x%llx, mem_id=*0x%x)", size, flags, mem_id);
@@ -41,6 +93,9 @@ error_code sys_mmapper_free_memory(ppu_thread& ppu, u32 mem_id)
extern void sysPrxForUser_sys_mmapper_init()
{
REG_VAR(sysPrxForUser, s_shared_memory_area).flag(MFF_HIDDEN);
// leave the guest import unimplemented for now, LLE sysutil waits for the missing vsh service otherwise
REG_FUNC(sysPrxForUser, sys_mmapper_allocate_memory);
REG_FUNC(sysPrxForUser, sys_mmapper_allocate_memory_from_container);
REG_FUNC(sysPrxForUser, sys_mmapper_map_memory);
+19 -1
View File
@@ -20,6 +20,7 @@
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/Cell/lv2/sys_prx.h"
#include "Emu/Cell/lv2/sys_memory.h"
#include "Emu/Cell/lv2/sys_mmapper.h"
#include "Emu/Cell/lv2/sys_overlay.h"
#include "Emu/Cell/Modules/StaticHLE.h"
@@ -27,6 +28,7 @@
#include <map>
#include <span>
#include <set>
#include <shared_mutex>
#include <algorithm>
#include "util/asm.hpp"
@@ -698,7 +700,18 @@ bool ppu_form_branch_to_code(u32 entry, u32 target);
extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name)
{
return g_fxo->get<ppu_linkage_info>().modules[module_name].functions[fnid].export_addr;
auto& link = g_fxo->get<ppu_linkage_info>();
std::shared_lock lock(link.mutex);
const auto module = link.modules.find(module_name);
if (module == link.modules.end())
{
return 0;
}
const auto function = module->second.functions.find(fnid);
return function == module->second.functions.end() ? 0 : function->second.export_addr;
}
extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib)
@@ -2652,6 +2665,11 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str
void init_fxo_for_exec(utils::serial* ar, bool full);
init_fxo_for_exec(ar, false);
if (!ar && !Emu.IsVsh())
{
init_system_shared_memory();
}
liblv2_begin = 0;
liblv2_end = 0;
}
+161 -36
View File
@@ -3,6 +3,7 @@
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/savestate_utils.hpp"
#include "Emu/Memory/vm_var.h"
#include "sys_memory.h"
#include "sys_sync.h"
@@ -14,6 +15,19 @@
LOG_CHANNEL(sys_mmapper);
void init_system_shared_memory()
{
for (u64 key : {SYS_MMAPPER_SYSUTIL_SHM_KEY, SYS_MMAPPER_MIO_SHM_KEY})
{
ensure(idm::import<lv2_obj, lv2_memory>([&]() -> shared_ptr<lv2_memory>
{
auto memory = make_shared<lv2_memory>(0x10000, 0x10000, 0x200, key, true, nullptr);
memory->system_handle = idm::last_id<lv2_memory>();
return lv2_obj::load(key, std::move(memory));
}));
}
}
template <>
void fmt_class_string<lv2_mem_container_id>::format(std::string& out, u64 arg)
{
@@ -46,23 +60,32 @@ lv2_memory::lv2_memory(utils::serial& ar)
, flags(ar)
, key(ar)
, pshared(ar)
, ct(lv2_memory_container::search(ar.pop<u32>()))
, shm([&](u32 addr) -> shared_ptr<std::shared_ptr<utils::shm>>
, ct([&](u32 id) -> lv2_memory_container*
{
if (addr)
{
return make_single_value(ensure(vm::get(vm::any, addr)->peek(addr).second));
}
return null_ptr;
return id ? ensure(lv2_memory_container::search(id)) : nullptr;
}(ar.pop<u32>()))
, counter(ar.pop<u32>())
, system_handle(ar)
, shm(null_ptr)
{
const u32 addr{ar};
counter = ar.pop<u32>();
external_refs = ar.pop<u32>();
if (addr)
{
shm = make_single_value(ensure(vm::get(vm::any, addr)->peek(addr).second));
}
else if (ar.pop<bool>())
{
auto data = std::make_shared<utils::shm>(size, 1);
ar(std::span<u8>(ensure(data->map_self()), size));
shm = make_single_value(std::move(data));
}
}
CellError lv2_memory::on_id_create()
{
if (!exists && !ct->take(size))
if (!exists && !external_refs && ct && !ct->take(size))
{
sys_mmapper.error("lv2_memory::on_id_create(): Cannot allocate 0x%x bytes (0x%x available)", size, ct->size - ct->used);
return CELL_ENOMEM;
@@ -72,9 +95,35 @@ CellError lv2_memory::on_id_create()
return {};
}
CellError lv2_memory::on_id_create(u64 requested_size, u32 requested_align, const shared_ptr<lv2_memory>& created)
{
if (this != created.get() && (size != requested_size || align != requested_align))
{
return CELL_EPERM;
}
if (exists > (system_handle ? 1u : 0u))
{
return CELL_EEXIST;
}
return on_id_create();
}
std::function<void(void*)> lv2_memory::load(utils::serial& ar)
{
auto mem = make_shared<lv2_memory>(stx::exact_t<utils::serial&>(ar));
if (mem->system_handle)
{
g_fxo->need<ipc_manager<lv2_memory, u64>>();
if (auto existing = g_fxo->get<ipc_manager<lv2_memory, u64>>().get(mem->key))
{
mem = std::move(existing);
}
}
mem->exists++; // Disable on_id_create()
auto func = load_func(mem, +mem->pshared);
mem->exists--;
@@ -82,12 +131,48 @@ std::function<void(void*)> lv2_memory::load(utils::serial& ar)
}
void lv2_memory::save(utils::serial& ar)
{
save_data(ar);
}
void lv2_memory::save_data(utils::serial& ar)
{
USING_SERIALIZATION_VERSION(lv2_memory);
ar(size, align, flags, key, pshared, ct->id);
ar(counter ? vm::get_shm_addr(*shm.load()) : 0);
ar(counter);
ar(size, align, flags, key, pshared, ct ? static_cast<u32>(ct->id) : 0, system_handle);
const auto data = shm.load();
const u32 addr = data ? vm::get_shm_addr(*data) : 0;
ar(addr);
ar(counter, external_refs);
if (!addr)
{
ar(static_cast<bool>(data));
if (data)
{
ar(std::span<u8>(ensure((*data)->map_self()), size));
}
}
}
void lv2_memory::release_memory()
{
// the caller holds the id manager lock
if (exists || external_refs)
{
return;
}
if (pshared)
{
g_fxo->get<ipc_manager<lv2_memory, u64>>().remove(key);
}
if (ct)
{
ct->free(size);
}
}
page_fault_notification_entries::page_fault_notification_entries(utils::serial& ar)
@@ -104,27 +189,20 @@ template <bool exclusive = false>
error_code create_lv2_shm(bool pshared, u64 ipc_key, u64 size, u32 align, u64 flags, lv2_memory_container* ct)
{
const u32 _pshared = pshared ? SYS_SYNC_PROCESS_SHARED : SYS_SYNC_NOT_PROCESS_SHARED;
const s32 create_mode = exclusive || (flags & 0xc000) == 0xc000 ? SYS_SYNC_NEWLY_CREATED
: (flags & 0x8000) ? SYS_SYNC_NOT_CARE : SYS_SYNC_NOT_CREATE;
if (!pshared)
{
ipc_key = 0;
}
if (auto error = lv2_obj::create<lv2_memory>(_pshared, ipc_key, exclusive ? SYS_SYNC_NEWLY_CREATED : SYS_SYNC_NOT_CARE, [&]()
shared_ptr<lv2_memory> created;
return lv2_obj::create<lv2_memory>(_pshared, ipc_key, create_mode, [&]()
{
return make_shared<lv2_memory>(
static_cast<u32>(size),
align,
flags,
ipc_key,
pshared,
ct);
}, false))
{
return error;
}
return CELL_OK;
created = make_shared<lv2_memory>(static_cast<u32>(size), align, flags, ipc_key, pshared, ct);
return created;
}, false, size, align, created);
}
error_code sys_mmapper_allocate_address(ppu_thread& ppu, u64 size, u64 flags, u64 alignment, vm::ptr<u32> alloc_addr)
@@ -189,6 +267,14 @@ error_code sys_mmapper_allocate_fixed_address(ppu_thread& ppu)
error_code sys_mmapper_allocate_shared_memory(ppu_thread& ppu, u64 ipc_key, u64 size, u64 flags, vm::ptr<u32> mem_id)
{
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
ppu.state += cpu_flag::wait;
sys_mmapper.warning("sys_mmapper_allocate_shared_memory(ipc_key=0x%x, size=0x%x, flags=0x%x, mem_id=*0x%x)", ipc_key, size, flags, mem_id);
@@ -241,6 +327,14 @@ error_code sys_mmapper_allocate_shared_memory(ppu_thread& ppu, u64 ipc_key, u64
error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u64 ipc_key, u64 size, u32 cid, u64 flags, vm::ptr<u32> mem_id)
{
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
ppu.state += cpu_flag::wait;
sys_mmapper.warning("sys_mmapper_allocate_shared_memory_from_container(ipc_key=0x%x, size=0x%x, cid=0x%x, flags=0x%x, mem_id=*0x%x)", ipc_key, size, cid, flags, mem_id);
@@ -278,6 +372,11 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
}
}
if (flags & 0xc000)
{
return CELL_EINVAL;
}
const auto ct = idm::get_unlocked<lv2_memory_container>(cid);
if (!ct)
@@ -285,7 +384,7 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
return CELL_ESRCH;
}
if (auto error = create_lv2_shm(ipc_key != SYS_MMAPPER_NO_SHM_KEY, ipc_key, size, flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000, flags, ct.get()))
if (auto error = create_lv2_shm<true>(ipc_key != SYS_MMAPPER_NO_SHM_KEY, ipc_key, size, flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000, flags, ct.get()))
{
return error;
}
@@ -297,6 +396,14 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key, u64 size, u32 flags, vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count, vm::ptr<u32> mem_id)
{
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
ppu.state += cpu_flag::wait;
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, entries, entry_count, mem_id);
@@ -396,6 +503,14 @@ error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key,
error_code sys_mmapper_allocate_shared_memory_from_container_ext(ppu_thread& ppu, u64 ipc_key, u64 size, u64 flags, u32 cid, vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count, vm::ptr<u32> mem_id)
{
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
if (!savestate_lock)
{
ppu.state += cpu_flag::again;
return {};
}
ppu.state += cpu_flag::wait;
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_from_container_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, cid=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, cid, entries,
@@ -581,18 +696,18 @@ error_code sys_mmapper_free_shared_memory(ppu_thread& ppu, u32 mem_id)
// Conditionally remove memory ID
const auto mem = idm::withdraw<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
{
if (mem_id == mem.system_handle)
{
return CELL_ESRCH;
}
if (mem.counter)
{
return CELL_EBUSY;
}
lv2_obj::on_id_destroy(mem, mem.key, +mem.pshared);
if (!mem.exists)
{
// Return "physical memory" to the memory container
mem.ct->free(mem.size);
}
lv2_obj::on_id_destroy(mem, mem.key, false);
mem.release_memory();
return {};
});
@@ -625,6 +740,11 @@ error_code sys_mmapper_map_shared_memory(ppu_thread& ppu, u32 addr, u32 mem_id,
const auto mem = idm::get<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
{
if (mem_id == mem.system_handle)
{
return CELL_ESRCH;
}
const u32 page_alignment = area->flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000;
if (mem.align < page_alignment)
@@ -700,6 +820,11 @@ error_code sys_mmapper_search_and_map(ppu_thread& ppu, u32 start_addr, u32 mem_i
const auto mem = idm::get<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
{
if (mem_id == mem.system_handle)
{
return CELL_ESRCH;
}
const u32 page_alignment = area->flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000;
if (mem.align < page_alignment)
@@ -783,7 +908,7 @@ error_code sys_mmapper_unmap_shared_memory(ppu_thread& ppu, u32 addr, vm::ptr<u3
const auto mem = idm::select<lv2_obj, lv2_memory>([&](u32 id, lv2_memory& mem) -> u32
{
if (auto shm0 = mem.shm.load(); shm0 && shm0->get() == shm.second.get())
if (auto shm0 = mem.shm.load(); id != mem.system_handle && shm0 && shm0->get() == shm.second.get())
{
return id;
}
+10 -1
View File
@@ -25,22 +25,30 @@ struct lv2_memory : lv2_obj
const u64 flags;
const u64 key; // IPC key
const bool pshared; // Process shared flag
lv2_memory_container* const ct; // Associated memory container
lv2_memory_container* const ct; // null for system memory
u32 system_handle = 0; // stands in for the vsh handle
atomic_ptr<std::shared_ptr<utils::shm>> shm;
atomic_t<u32> counter{0};
// the game can drop its handle while audio still needs the buffer
atomic_t<u32> external_refs{0};
lv2_memory(u32 size, u32 align, u64 flags, u64 key, bool pshared, lv2_memory_container* ct);
lv2_memory(utils::serial& ar);
static std::function<void(void*)> load(utils::serial& ar);
void save(utils::serial& ar);
void save_data(utils::serial& ar);
void release_memory();
CellError on_id_create();
CellError on_id_create(u64 requested_size, u32 requested_align, const shared_ptr<lv2_memory>& created);
};
enum : u64
{
SYS_MMAPPER_SYSUTIL_SHM_KEY = 0x8006010000000010ULL,
SYS_MMAPPER_MIO_SHM_KEY = 0x80004d494f323211ULL,
SYS_MEMORY_PAGE_FAULT_EVENT_KEY = 0xfffe000000000000ULL,
};
@@ -99,6 +107,7 @@ struct mmapper_unk_entry_struct0
// Aux
class ppu_thread;
void init_system_shared_memory();
error_code mmapper_thread_recover_page_fault(cpu_thread* cpu);
// SysCalls
+23 -2
View File
@@ -80,7 +80,20 @@ s32 sys_process_getppid()
template <typename T, typename Get>
u32 idm_get_count()
{
return idm::select<T, Get>([&](u32, Get&) {});
u32 count = 0;
idm::select<T, Get>([&](u32 id, Get& obj)
{
if constexpr (std::is_same_v<Get, lv2_memory>)
{
if (id == obj.system_handle)
{
return;
}
}
count++;
});
return count;
}
error_code sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump)
@@ -123,8 +136,16 @@ error_code sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump)
template <typename T, typename Get>
void idm_get_set(std::set<u32>& out)
{
idm::select<T, Get>([&](u32 id, Get&)
idm::select<T, Get>([&](u32 id, Get& obj)
{
if constexpr (std::is_same_v<Get, lv2_memory>)
{
if (id == obj.system_handle)
{
return;
}
}
out.emplace(id);
});
}
+24 -2
View File
@@ -180,6 +180,7 @@ bool ppu_register_library_lock(std::string_view libname, bool lock_lib);
extern error_code sysmoduleModuleStart(ppu_thread& ppu, u32 args, vm::ptr<void> argp);
extern error_code sysmoduleModuleStop(ppu_thread& ppu);
extern bool sysutilModuleInit(lv2_prx& prx);
static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> /*pOpt*/, fs::file src = {}, s64 file_offset = 0)
{
@@ -232,7 +233,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
ignore = g_prx_list.count(vpath0) && ::at32(g_prx_list, vpath0);
}
auto hle_load = [&]()
auto hle_load = [&]() -> error_code
{
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
@@ -241,6 +242,14 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
prx->start = vm::cast(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(sysmoduleModuleStart)));
prx->stop = vm::cast(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(sysmoduleModuleStop)));
}
else if (name == "libsysutil.sprx")
{
if (!sysutilModuleInit(*prx))
{
ensure((idm::remove<lv2_obj, lv2_prx>(idm::last_id<lv2_prx>())));
return CELL_ENOMEM;
}
}
prx->name = std::move(name);
prx->path = std::move(path);
@@ -322,6 +331,7 @@ std::function<void(void*)> lv2_prx::load(utils::serial& ar)
const std::string path = vfs::get(ar.pop<std::string>());
const s64 offset{ar};
const u32 state{ar};
const u32 hle_data{ar};
usz seg_count = 0;
ar.deserialize_vle<9>(seg_count);
@@ -332,7 +342,14 @@ std::function<void(void*)> lv2_prx::load(utils::serial& ar)
{
prx = make_shared<lv2_prx>();
prx->path = path;
prx->hle_data = hle_data;
prx->name = path.substr(path.find_last_of(fs::delim) + 1);
if (!seg_count && prx->name == "libsysutil.sprx")
{
ensure(prx->hle_data);
ensure(sysutilModuleInit(*prx));
}
};
if (seg_count)
@@ -392,7 +409,7 @@ void lv2_prx::save(utils::serial& ar)
const std::string vpath = vfs::retrieve(path);
ar(vpath, offset, state);
ar(vpath, offset, state, hle_data);
// Save segments count
ar.serialize_vle(segs.size());
@@ -765,6 +782,11 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sy
prx->mutex.lock_unlock();
if (prx->hle_data)
{
ensure(vm::dealloc(std::exchange(prx->hle_data, 0), vm::main));
}
ppu_unload_prx(*prx);
ppu_finalize(*prx);
+1
View File
@@ -190,6 +190,7 @@ struct lv2_prx final : ppu_module<lv2_obj>
atomic_t<u32> state = PRX_STATE_INITIALIZED;
shared_mutex mutex;
u32 hle_data = 0;
std::unordered_map<u32, u32> specials;
+10 -6
View File
@@ -308,8 +308,8 @@ public:
return (attr.pshared == SYS_SYNC_PROCESS_SHARED ? +attr.ipc_key : 0);
}
template <typename T, typename F>
static error_code create(u32 pshared, u64 ipc_key, s32 flags, F&& make, bool key_not_zero = true)
template <typename T, typename F, typename... Args>
static error_code create(u32 pshared, u64 ipc_key, s32 flags, F&& make, bool key_not_zero = true, Args&&... args)
{
switch (pshared)
{
@@ -340,8 +340,7 @@ public:
default: return CELL_EINVAL;
}
// EAGAIN for IDM IDs shortage
CellError error = CELL_EAGAIN;
CellError error{};
if (!idm::import<lv2_obj, T>([&]() -> shared_ptr<T>
{
@@ -349,7 +348,7 @@ public:
auto finalize_construct = [&]() -> shared_ptr<T>
{
if ((error = result->on_id_create()))
if ((error = result->on_id_create(std::forward<Args>(args)...)))
{
result.reset();
}
@@ -382,6 +381,11 @@ public:
bool added = false;
std::tie(added, result) = ipc_container.add(ipc_key, finalize_construct, flags != SYS_SYNC_NEWLY_CREATED);
if (error)
{
return {};
}
if (!added)
{
if (flags == SYS_SYNC_NEWLY_CREATED)
@@ -401,7 +405,7 @@ public:
return result;
}))
{
return error;
return error ? error : CELL_EAGAIN;
}
return CELL_OK;
+39 -23
View File
@@ -1320,6 +1320,8 @@ namespace vm
if (m_id.exchange(0))
{
std::unordered_map<const utils::shm*, s64> mapping_refs;
// Deallocate all memory
for (auto it = m_map.begin(), end = m_map.end(); it != end;)
{
@@ -1333,7 +1335,40 @@ namespace vm
{
if (it->second.second.use_count() != 1)
{
fmt::throw_exception("External memory usage at block 0x%x (addr=0x%x, size=0x%x)", this->addr, it->first, size);
if (mapping_refs.empty())
{
const auto count_refs = [&mapping_refs](const auto& map)
{
for (const auto& entry : map)
{
if (entry.second.second)
{
mapping_refs[entry.second.second.get()]++;
}
}
};
// count this block separately, vm::unmap removes it from g_locations before calling us
count_refs(m_map);
for (const auto& block : g_locations)
{
if (block && block.get() != this)
{
count_refs((block->m.*block_map)());
}
}
}
if (it->second.second.use_count() != mapping_refs.at(it->second.second.get()))
{
fmt::throw_exception("External memory usage at block 0x%x (addr=0x%x, size=0x%x)", this->addr, it->first, size);
}
}
if (!mapping_refs.empty())
{
mapping_refs.at(it->second.second.get())--;
}
it->second.second.reset();
@@ -2294,29 +2329,10 @@ namespace vm
std::map<utils::shm*, usz> shared_map;
#ifndef _MSC_VER
shared.erase(std::unique(shared.begin(), shared.end(), [](auto& a, auto& b) { return a.first == b.first; }), shared.end());
#else
// Workaround for bugged std::unique
for (auto it = shared.begin(); it != shared.end();)
std::erase_if(shared, [&](const auto& memory)
{
if (shared_map.count(it->first))
{
it = shared.erase(it);
continue;
}
shared_map.emplace(it->first, 0);
it++;
}
shared_map.clear();
#endif
for (auto& p : shared)
{
shared_map.emplace(p.first, &p - shared.data());
}
return !shared_map.emplace(memory.first, shared_map.size()).second;
});
// TODO: proper serialization of std::map
ar(static_cast<usz>(shared_map.size()));
+4 -5
View File
@@ -40,15 +40,15 @@ static std::array<serial_ver_t, 34> s_serial_versions;
return ::s_serial_versions[identifier].current_version;\
}
SERIALIZATION_VER(global_version, 0, 22) // For stuff not listed here
SERIALIZATION_VER(global_version, 0, 23) // For stuff not listed here
SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep order*/, 3/*PPU FNID and module*/)
SERIALIZATION_VER(spu, 2, 1)
SERIALIZATION_VER(lv2_sync, 3, 1)
SERIALIZATION_VER(lv2_vm, 4, 1)
SERIALIZATION_VER(lv2_net, 5, 1, 2/*TCP Feign conection loss*/, 3/*P2PS stream_disconnected status*/)
SERIALIZATION_VER(lv2_fs, 6, 1, 2/*NPDRM key saving*/)
SERIALIZATION_VER(lv2_prx_overlay, 7, 1)
SERIALIZATION_VER(lv2_memory, 8, 1)
SERIALIZATION_VER(lv2_prx_overlay, 7, 2)
SERIALIZATION_VER(lv2_memory, 8, 2)
SERIALIZATION_VER(lv2_config, 9, 1)
namespace rsx
@@ -68,7 +68,7 @@ SERIALIZATION_VER(sceNp, 11)
#endif
SERIALIZATION_VER(cellVdec, 12, 1)
SERIALIZATION_VER(cellAudio, 13, 1)
SERIALIZATION_VER(cellAudio, 13, 2)
SERIALIZATION_VER(cellCamera, 14, 1, 2/*gem_camera_shared*/)
SERIALIZATION_VER(cellGem, 15, 1, 2/*calibration_status_flags*/, 3/*video_conversion*/)
SERIALIZATION_VER(sceNpTrophy, 16, 1)
@@ -691,4 +691,3 @@ bool hle_locks_t::try_finalize(std::function<bool()> test)
lock_val.notify_all();
return true;
}
+3 -2
View File
@@ -350,14 +350,15 @@ void kernel_explorer::update()
{
auto& mem = static_cast<lv2_memory&>(obj);
const f64 size_mb = mem.size * 1. / (1024 * 1024);
const std::string container = mem.ct ? fmt::format("%s", mem.ct->id) : "System";
if (mem.pshared)
{
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mappings: %u, Mem Container: %s, Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, mem.ct->id, mem.key)));
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mappings: %u, Mem Container: %s, Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, container, mem.key)));
break;
}
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mem Container: %s, Mappings: %u", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", mem.ct->id, +mem.counter)));
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mem Container: %s, Mappings: %u", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", container, +mem.counter)));
break;
}
case SYS_MUTEX_OBJECT: