mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,444 @@
|
||||
#include "Emulator/Kernel/EventFlag.h"
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Timer.h"
|
||||
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::EventFlag {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
class KernelEventFlagPrivate
|
||||
{
|
||||
public:
|
||||
enum class Result
|
||||
{
|
||||
Ok,
|
||||
AlreadyWaiting,
|
||||
TimedOut,
|
||||
Canceled,
|
||||
Deleted
|
||||
};
|
||||
|
||||
enum class ClearMode
|
||||
{
|
||||
None,
|
||||
All,
|
||||
Bits
|
||||
};
|
||||
|
||||
enum class WaitMode
|
||||
{
|
||||
And,
|
||||
Or
|
||||
};
|
||||
|
||||
KernelEventFlagPrivate(const String& name, bool flag, uint64_t bits): m_name(name), m_single_thread(flag), m_bits(bits) {};
|
||||
virtual ~KernelEventFlagPrivate();
|
||||
|
||||
KYTY_CLASS_NO_COPY(KernelEventFlagPrivate);
|
||||
|
||||
void Set(uint64_t bits);
|
||||
void Clear(uint64_t bits);
|
||||
void Cancel(uint64_t bits, int* num_waiting_threads);
|
||||
Result Wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros);
|
||||
|
||||
Result Poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result)
|
||||
{
|
||||
uint32_t micros = 0;
|
||||
return Wait(bits, wait_mode, clear_mode, result, µs);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Status
|
||||
{
|
||||
Set,
|
||||
Canceled,
|
||||
Deleted
|
||||
};
|
||||
|
||||
Core::Mutex m_mutex;
|
||||
Core::CondVar m_cond_var;
|
||||
Status m_status = Status::Set;
|
||||
int m_waiting_threads = 0;
|
||||
String m_name;
|
||||
bool m_single_thread = false;
|
||||
uint64_t m_bits = 0;
|
||||
};
|
||||
|
||||
KernelEventFlagPrivate::~KernelEventFlagPrivate()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
while (m_status != Status::Set)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
m_status = Status::Deleted;
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
|
||||
while (m_waiting_threads > 0)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
KernelEventFlagPrivate::Result KernelEventFlagPrivate::Wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result,
|
||||
uint32_t* ptr_micros)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint32_t micros = 0;
|
||||
bool infinitely = true;
|
||||
if (ptr_micros != nullptr)
|
||||
{
|
||||
micros = *ptr_micros;
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Core::Timer t;
|
||||
t.Start();
|
||||
|
||||
if (m_single_thread && m_waiting_threads > 0)
|
||||
{
|
||||
return Result::AlreadyWaiting;
|
||||
}
|
||||
|
||||
while (!((wait_mode == WaitMode::And && (m_bits & bits) == bits) || (wait_mode == WaitMode::Or && (m_bits & bits) != 0)))
|
||||
{
|
||||
if ((elapsed >= micros && !infinitely))
|
||||
{
|
||||
if (result != nullptr)
|
||||
{
|
||||
*result = m_bits;
|
||||
}
|
||||
*ptr_micros = 0;
|
||||
return Result::TimedOut;
|
||||
}
|
||||
|
||||
m_waiting_threads++;
|
||||
|
||||
if (infinitely)
|
||||
{
|
||||
m_cond_var.Wait(&m_mutex);
|
||||
} else
|
||||
{
|
||||
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
|
||||
}
|
||||
|
||||
m_waiting_threads--;
|
||||
|
||||
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
|
||||
|
||||
if (m_status == Status::Canceled)
|
||||
{
|
||||
if (result != nullptr)
|
||||
{
|
||||
*result = m_bits;
|
||||
}
|
||||
if (ptr_micros != nullptr)
|
||||
{
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
return Result::Canceled;
|
||||
}
|
||||
|
||||
if (m_status == Status::Deleted)
|
||||
{
|
||||
if (result != nullptr)
|
||||
{
|
||||
*result = m_bits;
|
||||
}
|
||||
if (ptr_micros != nullptr)
|
||||
{
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
return Result::Deleted;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != nullptr)
|
||||
{
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
if (clear_mode == ClearMode::All)
|
||||
{
|
||||
m_bits = 0;
|
||||
} else if (clear_mode == ClearMode::Bits)
|
||||
{
|
||||
m_bits &= ~bits;
|
||||
}
|
||||
|
||||
if (ptr_micros != nullptr)
|
||||
{
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void KernelEventFlagPrivate::Set(uint64_t bits)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
|
||||
|
||||
while (m_status != Status::Set)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
m_bits |= bits;
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
}
|
||||
|
||||
void KernelEventFlagPrivate::Clear(uint64_t bits)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
|
||||
|
||||
while (m_status != Status::Set)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
m_bits &= bits;
|
||||
}
|
||||
|
||||
void KernelEventFlagPrivate::Cancel(uint64_t bits, int* num_waiting_threads)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
|
||||
|
||||
while (m_status != Status::Set)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
if (num_waiting_threads != nullptr)
|
||||
{
|
||||
*num_waiting_threads = m_waiting_threads;
|
||||
}
|
||||
|
||||
m_status = Status::Canceled;
|
||||
m_bits = bits;
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
|
||||
while (m_waiting_threads > 0)
|
||||
{
|
||||
m_mutex.Unlock();
|
||||
Core::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
m_status = Status::Set;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEventFlag(KernelEventFlag* ef, const char* name, uint32_t attr, uint64_t init_pattern, const void* param)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(param != nullptr);
|
||||
|
||||
if (ef == nullptr || name == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
bool single = false;
|
||||
|
||||
switch (attr)
|
||||
{
|
||||
case 0x10: single = true; break;
|
||||
case 0x20: single = false; break;
|
||||
default: EXIT("unknown attr: %u\n", attr);
|
||||
}
|
||||
|
||||
*ef = new KernelEventFlagPrivate(String::FromUtf8(name), single, init_pattern);
|
||||
|
||||
printf("\tEventFlag create: %s\n", name);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteEventFlag(KernelEventFlag ef)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
delete ef;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelWaitEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat,
|
||||
KernelUseconds* timeout)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
if (bit_pattern == 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
KernelEventFlagPrivate::WaitMode wait = KernelEventFlagPrivate::WaitMode::And;
|
||||
KernelEventFlagPrivate::ClearMode clear = KernelEventFlagPrivate::ClearMode::None;
|
||||
|
||||
switch (wait_mode & 0xfu)
|
||||
{
|
||||
case 0x01: wait = KernelEventFlagPrivate::WaitMode::And; break;
|
||||
case 0x02: wait = KernelEventFlagPrivate::WaitMode::Or; break;
|
||||
default: EXIT("unknown mode: %u\n", wait_mode);
|
||||
}
|
||||
|
||||
switch (wait_mode & 0xf0u)
|
||||
{
|
||||
case 0x00: clear = KernelEventFlagPrivate::ClearMode::None; break;
|
||||
case 0x10: clear = KernelEventFlagPrivate::ClearMode::All; break;
|
||||
case 0x20: clear = KernelEventFlagPrivate::ClearMode::Bits; break;
|
||||
default: EXIT("unknown mode: %u\n", wait_mode);
|
||||
}
|
||||
|
||||
auto result = ef->Wait(bit_pattern, wait, clear, result_pat, timeout);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
|
||||
case KernelEventFlagPrivate::Result::TimedOut: ret = KERNEL_ERROR_ETIMEDOUT; break;
|
||||
case KernelEventFlagPrivate::Result::Canceled: ret = KERNEL_ERROR_ECANCELED; break;
|
||||
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EACCES; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelPollEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode, uint64_t* result_pat)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
if (bit_pattern == 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
KernelEventFlagPrivate::WaitMode wait = KernelEventFlagPrivate::WaitMode::And;
|
||||
KernelEventFlagPrivate::ClearMode clear = KernelEventFlagPrivate::ClearMode::None;
|
||||
|
||||
switch (wait_mode & 0xfu)
|
||||
{
|
||||
case 0x01: wait = KernelEventFlagPrivate::WaitMode::And; break;
|
||||
case 0x02: wait = KernelEventFlagPrivate::WaitMode::Or; break;
|
||||
default: EXIT("unknown mode: %u\n", wait_mode);
|
||||
}
|
||||
|
||||
switch (wait_mode & 0xf0u)
|
||||
{
|
||||
case 0x00: clear = KernelEventFlagPrivate::ClearMode::None; break;
|
||||
case 0x10: clear = KernelEventFlagPrivate::ClearMode::All; break;
|
||||
case 0x20: clear = KernelEventFlagPrivate::ClearMode::Bits; break;
|
||||
default: EXIT("unknown mode: %u\n", wait_mode);
|
||||
}
|
||||
|
||||
auto result = ef->Poll(bit_pattern, wait, clear, result_pat);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
|
||||
case KernelEventFlagPrivate::Result::TimedOut:
|
||||
case KernelEventFlagPrivate::Result::Canceled:
|
||||
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EBUSY; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelSetEventFlag(KernelEventFlag ef, uint64_t bit_pattern)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
ef->Set(bit_pattern);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelClearEventFlag(KernelEventFlag ef, uint64_t bit_pattern)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
ef->Clear(bit_pattern);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCancelEventFlag(KernelEventFlag ef, uint64_t set_pattern, int* num_wait_threads)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ef == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
ef->Cancel(set_pattern, num_wait_threads);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::EventFlag
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,389 @@
|
||||
#include "Emulator/Kernel/EventQueue.h"
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/LinkList.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Timer.h"
|
||||
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::EventQueue {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
class KernelEqueuePrivate
|
||||
{
|
||||
public:
|
||||
KernelEqueuePrivate() = default;
|
||||
virtual ~KernelEqueuePrivate();
|
||||
|
||||
KYTY_CLASS_NO_COPY(KernelEqueuePrivate);
|
||||
|
||||
[[nodiscard]] const String& GetName() const { return m_name; }
|
||||
void SetName(const String& m_name) { this->m_name = m_name; }
|
||||
|
||||
void AddEvent(const KernelEqueueEvent& event);
|
||||
bool TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data);
|
||||
bool DeleteEvent(uintptr_t ident, int16_t filter);
|
||||
|
||||
int GetTriggeredEvents(KernelEvent* ev, int num);
|
||||
int WaitForEvents(KernelEvent* ev, int num, uint32_t micros);
|
||||
|
||||
private:
|
||||
Core::List<KernelEqueueEvent> m_events;
|
||||
Core::Mutex m_mutex;
|
||||
Core::CondVar m_cond_var;
|
||||
String m_name;
|
||||
};
|
||||
|
||||
KernelEqueuePrivate::~KernelEqueuePrivate()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
FOR_LIST(index, m_events)
|
||||
{
|
||||
auto& event = m_events[index];
|
||||
|
||||
if (event.filter.delete_func != nullptr)
|
||||
{
|
||||
event.filter.delete_func(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_IF(num < 1);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
FOR_LIST(index, m_events)
|
||||
{
|
||||
auto& event = m_events[index];
|
||||
|
||||
if (event.triggered)
|
||||
{
|
||||
ev[ret++] = event.event;
|
||||
|
||||
if (event.filter.reset_func != nullptr)
|
||||
{
|
||||
event.filter.reset_func(&event);
|
||||
}
|
||||
|
||||
if (ret >= num)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KernelEqueuePrivate::WaitForEvents(KernelEvent* ev, int num, uint32_t micros)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_IF(num < 1);
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Core::Timer t;
|
||||
t.Start();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int ret = GetTriggeredEvents(ev, num);
|
||||
|
||||
if (ret > 0 || (elapsed >= micros && micros != 0))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (micros == 0)
|
||||
{
|
||||
m_cond_var.Wait(&m_mutex);
|
||||
} else
|
||||
{
|
||||
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
|
||||
}
|
||||
|
||||
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
if (auto index = m_events.Find(event.event.ident, event.event.filter,
|
||||
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
|
||||
m_events.IndexValid(index))
|
||||
{
|
||||
m_events[index] = event;
|
||||
} else
|
||||
{
|
||||
m_events.Add(event);
|
||||
}
|
||||
|
||||
if (event.triggered)
|
||||
{
|
||||
m_cond_var.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool KernelEqueuePrivate::TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
if (auto index = m_events.Find(ident, filter,
|
||||
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
|
||||
m_events.IndexValid(index))
|
||||
{
|
||||
auto& event = m_events[index];
|
||||
|
||||
if (event.filter.trigger_func != nullptr)
|
||||
{
|
||||
event.filter.trigger_func(&event, trigger_data);
|
||||
} else
|
||||
{
|
||||
event.triggered = true;
|
||||
}
|
||||
|
||||
m_cond_var.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KernelEqueuePrivate::DeleteEvent(uintptr_t ident, int16_t filter)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
if (auto index = m_events.Find(ident, filter,
|
||||
[](auto e, auto ident, auto filter) { return e.event.ident == ident && e.event.filter == filter; });
|
||||
m_events.IndexValid(index))
|
||||
{
|
||||
auto& event = m_events[index];
|
||||
|
||||
if (event.filter.delete_func != nullptr)
|
||||
{
|
||||
event.filter.delete_func(&event);
|
||||
}
|
||||
|
||||
m_events.Remove(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEqueue(KernelEqueue* eq, const char* name)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr || name == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
*eq = new KernelEqueuePrivate;
|
||||
|
||||
(*eq)->SetName(String::FromUtf8(name));
|
||||
|
||||
printf("\tEqueue create: %s\n", name);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddEvent(KernelEqueue eq, const KernelEqueueEvent& event)
|
||||
{
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
eq->AddEvent(event);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter, void* trigger_data)
|
||||
{
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->TriggerEvent(ident, filter, trigger_data))
|
||||
{
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter)
|
||||
{
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->DeleteEvent(ident, filter))
|
||||
{
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteEqueue(KernelEqueue eq)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
printf("\tEqueue delete: %s\n", eq->GetName().C_Str());
|
||||
|
||||
delete eq;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out, const KernelUseconds* timo)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (ev == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
if (num < 1)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(out == nullptr);
|
||||
|
||||
printf("\tEqueue wait: %s\n", eq->GetName().C_Str());
|
||||
|
||||
if (timo == nullptr)
|
||||
{
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
}
|
||||
|
||||
if (timo != nullptr)
|
||||
{
|
||||
if (*timo == 0)
|
||||
{
|
||||
*out = eq->GetTriggeredEvents(ev, num);
|
||||
} else
|
||||
{
|
||||
*out = eq->WaitForEvents(ev, num, *timo);
|
||||
}
|
||||
}
|
||||
|
||||
if (*out == 0)
|
||||
{
|
||||
printf("\ttimedout\n");
|
||||
return KERNEL_ERROR_ETIMEDOUT;
|
||||
}
|
||||
|
||||
printf("\treceived %u events\n", *out);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventData(const KernelEvent* ev)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr)
|
||||
{
|
||||
return ev->data;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventFflags(const KernelEvent* ev)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr)
|
||||
{
|
||||
return ev->fflags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetEventFilter(const KernelEvent* ev)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr)
|
||||
{
|
||||
return ev->filter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t KYTY_SYSV_ABI KernelGetEventId(const KernelEvent* ev)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr)
|
||||
{
|
||||
return ev->ident;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* KYTY_SYSV_ABI KernelGetEventUserData(const KernelEvent* ev)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr)
|
||||
{
|
||||
return ev->udata;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetEventError(const KernelEvent* /*ev*/)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
KYTY_NOT_IMPLEMENTED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::EventQueue
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,928 @@
|
||||
#include "Emulator/Kernel/FileSystem.h"
|
||||
|
||||
#include "Kyty/Core/Common.h"
|
||||
#include "Kyty/Core/DateTime.h"
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/File.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <climits>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::FileSystem {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
constexpr int DESCRIPTOR_MIN = 3;
|
||||
|
||||
class MountPoints
|
||||
{
|
||||
public:
|
||||
struct MountPair
|
||||
{
|
||||
String dir;
|
||||
String point;
|
||||
};
|
||||
|
||||
MountPoints() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~MountPoints() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(MountPoints);
|
||||
|
||||
void Mount(const String& folder, const String& point);
|
||||
void Umount(const String& folder_or_point);
|
||||
|
||||
[[nodiscard]] String GetRealFilename(const String& mounted_file_name);
|
||||
[[nodiscard]] String GetRealDirectory(const String& mounted_directory);
|
||||
|
||||
private:
|
||||
Vector<MountPair> m_mount_pairs;
|
||||
Core::Mutex m_mutex;
|
||||
};
|
||||
|
||||
struct File
|
||||
{
|
||||
Core::File f;
|
||||
String name;
|
||||
String real_name;
|
||||
std::atomic_bool opened;
|
||||
std::atomic_bool directory;
|
||||
Core::Mutex mutex;
|
||||
Vector<Core::File::DirEntry> dents;
|
||||
uint32_t dents_index;
|
||||
};
|
||||
|
||||
class FileDescriptors
|
||||
{
|
||||
public:
|
||||
FileDescriptors() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~FileDescriptors() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(FileDescriptors);
|
||||
|
||||
int CreateDescriptor();
|
||||
void DeleteDescriptor(int d);
|
||||
File* GetFile(int d);
|
||||
File* GetFile(const String& real_name);
|
||||
void CloseAll();
|
||||
|
||||
private:
|
||||
Vector<File*> m_files;
|
||||
Core::Mutex m_mutex;
|
||||
};
|
||||
|
||||
static MountPoints* g_mount_points = nullptr;
|
||||
static FileDescriptors* g_files = nullptr;
|
||||
|
||||
static void sec_to_timespec(KernelTimespec* ts, double sec)
|
||||
{
|
||||
ts->tv_sec = static_cast<int64_t>(sec);
|
||||
ts->tv_nsec = static_cast<int64_t>((sec - static_cast<double>(ts->tv_sec)) * 1000000000.0);
|
||||
}
|
||||
|
||||
int FileDescriptors::CreateDescriptor()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto* file = new File {};
|
||||
file->opened = false;
|
||||
file->directory = false;
|
||||
|
||||
int files_num = static_cast<int>(m_files.Size());
|
||||
for (int index = 0; index < files_num; index++)
|
||||
{
|
||||
if (m_files.At(index) == nullptr)
|
||||
{
|
||||
m_files[index] = file;
|
||||
return index + DESCRIPTOR_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
m_files.Add(file);
|
||||
return static_cast<int>(m_files.Size()) + DESCRIPTOR_MIN - 1;
|
||||
}
|
||||
|
||||
void FileDescriptors::DeleteDescriptor(int d)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto index = static_cast<uint32_t>(d - DESCRIPTOR_MIN);
|
||||
|
||||
EXIT_IF(!m_files.IndexValid(index));
|
||||
EXIT_IF(m_files.At(index) == nullptr);
|
||||
EXIT_IF(m_files.At(index)->opened);
|
||||
|
||||
delete m_files.At(index);
|
||||
m_files[index] = nullptr;
|
||||
}
|
||||
|
||||
File* FileDescriptors::GetFile(int d)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto index = static_cast<uint32_t>(d - DESCRIPTOR_MIN);
|
||||
|
||||
EXIT_IF(!m_files.IndexValid(index));
|
||||
|
||||
return m_files.At(index);
|
||||
}
|
||||
|
||||
File* FileDescriptors::GetFile(const String& real_name)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto* f: m_files)
|
||||
{
|
||||
if (f != nullptr && f->real_name == real_name)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FileDescriptors::CloseAll()
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& f: m_files)
|
||||
{
|
||||
if (f != nullptr && f->opened)
|
||||
{
|
||||
f->f.Close();
|
||||
delete f;
|
||||
f = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MountPoints::Mount(const String& folder, const String& point)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto folder_str = folder.FixDirectorySlash();
|
||||
auto point_str = point.FixDirectorySlash();
|
||||
|
||||
Umount(folder_str);
|
||||
Umount(point_str);
|
||||
|
||||
MountPair p;
|
||||
p.dir = folder_str;
|
||||
p.point = point_str;
|
||||
|
||||
m_mount_pairs.Add(p);
|
||||
}
|
||||
|
||||
void MountPoints::Umount(const String& folder_or_point)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto folder_or_point_str = folder_or_point.FixDirectorySlash();
|
||||
|
||||
if (auto index =
|
||||
m_mount_pairs.Find(folder_or_point_str, [](const MountPair& p, const String& s) { return p.dir == s || p.point == s; });
|
||||
m_mount_pairs.IndexValid(index))
|
||||
{
|
||||
m_mount_pairs.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
String MountPoints::GetRealFilename(const String& mounted_file_name)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto mounted_path = mounted_file_name.FixFilenameSlash().DirectoryWithoutFilename();
|
||||
|
||||
if (auto index = m_mount_pairs.Find(mounted_path, [](const MountPair& p, const String& s) { return s.StartsWith(p.point); });
|
||||
m_mount_pairs.IndexValid(index))
|
||||
{
|
||||
const auto& p = m_mount_pairs.At(index);
|
||||
return p.dir + mounted_file_name.RemoveFirst(p.point.Size());
|
||||
}
|
||||
|
||||
return mounted_file_name;
|
||||
}
|
||||
|
||||
String MountPoints::GetRealDirectory(const String& mounted_directory)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
auto mounted_path = mounted_directory.FixDirectorySlash();
|
||||
|
||||
if (auto index = m_mount_pairs.Find(mounted_path, [](const MountPair& p, const String& s) { return s.StartsWith(p.point); });
|
||||
m_mount_pairs.IndexValid(index))
|
||||
{
|
||||
const auto& p = m_mount_pairs.At(index);
|
||||
return p.dir + mounted_directory.RemoveFirst(p.point.Size());
|
||||
}
|
||||
|
||||
return mounted_directory;
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(FileSystem)
|
||||
{
|
||||
g_mount_points = new MountPoints;
|
||||
g_files = new FileDescriptors;
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(FileSystem)
|
||||
{
|
||||
if (g_files != nullptr)
|
||||
{
|
||||
g_files->CloseAll();
|
||||
}
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(FileSystem)
|
||||
{
|
||||
if (g_files != nullptr)
|
||||
{
|
||||
g_files->CloseAll();
|
||||
}
|
||||
}
|
||||
|
||||
void Mount(const String& folder, const String& point)
|
||||
{
|
||||
EXIT_IF(g_mount_points == nullptr);
|
||||
|
||||
g_mount_points->Mount(folder, point);
|
||||
}
|
||||
|
||||
void Umount(const String& folder_or_point)
|
||||
{
|
||||
EXIT_IF(g_mount_points == nullptr);
|
||||
|
||||
g_mount_points->Umount(folder_or_point);
|
||||
}
|
||||
|
||||
String GetRealFilename(const String& mounted_file_name)
|
||||
{
|
||||
EXIT_IF(g_mount_points == nullptr);
|
||||
|
||||
return g_mount_points->GetRealFilename(mounted_file_name);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_mount_points == nullptr || g_files == nullptr);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto flags_u = static_cast<uint32_t>(flags);
|
||||
|
||||
printf("\tpath = %s\n", path);
|
||||
printf("\tflags = %08" PRIx32 "\n", flags_u);
|
||||
printf("\tmode = %04" PRIx16 "\n", mode);
|
||||
|
||||
bool nonblock = (flags_u & 0x0004u) != 0;
|
||||
bool append = (flags_u & 0x0008u) != 0;
|
||||
bool fsync = (flags_u & 0x0080u) != 0;
|
||||
bool sync = (flags_u & 0x0080u) != 0;
|
||||
bool creat = (flags_u & 0x0200u) != 0;
|
||||
bool trunc = (flags_u & 0x0400u) != 0;
|
||||
bool excl = (flags_u & 0x0800u) != 0;
|
||||
bool dsync = (flags_u & 0x1000u) != 0;
|
||||
bool direct = (flags_u & 0x00010000u) != 0;
|
||||
bool directory = (flags_u & 0x00020000u) != 0;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(append || fsync || sync || excl || dsync || direct);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nonblock && !directory);
|
||||
|
||||
flags_u &= 0x3u;
|
||||
|
||||
Core::File::Mode rw_mode = Core::File::Mode::Read;
|
||||
|
||||
switch (flags_u)
|
||||
{
|
||||
case 0: rw_mode = Core::File::Mode::Read; break;
|
||||
case 1: rw_mode = Core::File::Mode::Write; break;
|
||||
case 2: rw_mode = Core::File::Mode::ReadWrite; break;
|
||||
default: EXIT("invalid flag_u: %u\n", flags_u);
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(directory && rw_mode != Core::File::Mode::Read);
|
||||
EXIT_NOT_IMPLEMENTED(directory && (trunc || creat));
|
||||
|
||||
int descriptor = g_files->CreateDescriptor();
|
||||
auto* file = g_files->GetFile(descriptor);
|
||||
|
||||
EXIT_IF(file == nullptr || file->opened || file->directory);
|
||||
|
||||
file->name = path;
|
||||
file->real_name = (directory ? g_mount_points->GetRealDirectory(file->name) : g_mount_points->GetRealFilename(file->name));
|
||||
|
||||
if (trunc && rw_mode == Core::File::Mode::Read)
|
||||
{
|
||||
return KERNEL_ERROR_EACCES;
|
||||
}
|
||||
|
||||
if (directory)
|
||||
{
|
||||
if (!Core::File::IsDirectoryExisting(file->real_name))
|
||||
{
|
||||
g_files->DeleteDescriptor(descriptor);
|
||||
return KERNEL_ERROR_ENOTDIR;
|
||||
}
|
||||
|
||||
file->dents = Core::File::GetDirEntries(file->real_name);
|
||||
file->dents_index = 0;
|
||||
file->directory = true;
|
||||
|
||||
printf("\tOpen dir: " FG_WHITE BOLD "%s" DEFAULT ", entries = %" PRIu32 ", " FG_GREEN "[ok]" FG_DEFAULT "\n",
|
||||
file->real_name.C_Str(), file->dents.Size());
|
||||
|
||||
for (const auto& f: file->dents)
|
||||
{
|
||||
printf("\t\t%s %s\n", f.is_file ? "[file]" : "[dir ]", f.name.C_Str());
|
||||
}
|
||||
} else
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(Core::File::IsDirectoryExisting(file->real_name));
|
||||
|
||||
if (creat)
|
||||
{
|
||||
result = file->f.Create(file->real_name);
|
||||
|
||||
printf("\tCreate: " FG_WHITE BOLD "%s" DEFAULT ", %s\n", file->real_name.C_Str(),
|
||||
(result ? FG_GREEN "[ok]" FG_DEFAULT : FG_RED "[fail]" FG_DEFAULT));
|
||||
} else
|
||||
{
|
||||
result = file->f.Open(file->real_name, rw_mode);
|
||||
|
||||
printf("\tOpen: " FG_WHITE BOLD "%s" DEFAULT ", %s\n", file->real_name.C_Str(),
|
||||
(result ? FG_GREEN "[ok]" FG_DEFAULT : FG_RED "[fail]" FG_DEFAULT));
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(creat && !trunc);
|
||||
|
||||
if (result && trunc)
|
||||
{
|
||||
result = file->f.Truncate(0);
|
||||
}
|
||||
|
||||
if (!result || file->f.IsInvalid())
|
||||
{
|
||||
g_files->DeleteDescriptor(descriptor);
|
||||
return KERNEL_ERROR_EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
file->opened = true;
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelClose(int d)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
if (!file->directory)
|
||||
{
|
||||
file->f.Close();
|
||||
}
|
||||
|
||||
file->opened = false;
|
||||
|
||||
printf("\tClose: " FG_WHITE BOLD "%s" DEFAULT "\n", file->real_name.C_Str());
|
||||
|
||||
g_files->DeleteDescriptor(d);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
uint32_t bytes_read = 0;
|
||||
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tRead %u bytes from: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_read, file->real_name.C_Str());
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
uint32_t bytes_written = 0;
|
||||
file->f.Write(buf, static_cast<uint32_t>(nbytes), &bytes_written);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tWrite %u bytes to: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_written, file->real_name.C_Str());
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
auto pos = file->f.Tell();
|
||||
uint32_t bytes_read = 0;
|
||||
file->f.Seek(offset);
|
||||
file->f.Read(buf, static_cast<uint32_t>(nbytes), &bytes_read);
|
||||
file->f.Seek(pos);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tRead %u bytes (pos = %" PRId64 ") from: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_read, offset, file->real_name.C_Str());
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(nbytes > UINT_MAX);
|
||||
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
auto pos = file->f.Tell();
|
||||
uint32_t bytes_written = 0;
|
||||
file->f.Seek(offset);
|
||||
file->f.Write(buf, static_cast<uint32_t>(nbytes), &bytes_written);
|
||||
file->f.Seek(pos);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tWrite %u bytes (pos = %" PRId64 ") to: " FG_WHITE BOLD "%s" DEFAULT "\n", bytes_written, offset, file->real_name.C_Str());
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(file->directory);
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
|
||||
if (whence == 1)
|
||||
{
|
||||
offset = static_cast<int64_t>(file->f.Tell()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
if (whence == 2)
|
||||
{
|
||||
offset = static_cast<int64_t>(file->f.Size()) + offset;
|
||||
whence = 0;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(whence != 0);
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
file->f.Seek(offset);
|
||||
auto pos = static_cast<int64_t>(file->f.Tell());
|
||||
|
||||
EXIT_IF(pos != offset);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tLseek (pos = %" PRId64 ") to: " FG_WHITE BOLD "%s" DEFAULT "\n", offset, file->real_name.C_Str());
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_mount_points == nullptr);
|
||||
|
||||
if (path == nullptr || sb == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
printf("\tKernelStat: %s\n", path);
|
||||
|
||||
String path_s = String::FromUtf8(path);
|
||||
auto real_file_name = g_mount_points->GetRealFilename(path_s);
|
||||
auto real_directory = g_mount_points->GetRealDirectory(path_s);
|
||||
|
||||
bool is_dir = Core::File::IsDirectoryExisting(real_file_name) || Core::File::IsDirectoryExisting(real_directory);
|
||||
bool is_file = Core::File::IsFileExisting(real_file_name);
|
||||
|
||||
if (!is_dir && !is_file)
|
||||
{
|
||||
printf("\tfile not found\n");
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(is_dir && is_file);
|
||||
|
||||
memset(sb, 0, sizeof(FileStat));
|
||||
|
||||
sb->st_mode = 0000777u | (is_dir ? 0040000u : 0100000u);
|
||||
|
||||
Core::DateTime at;
|
||||
Core::DateTime wt;
|
||||
|
||||
if (is_dir)
|
||||
{
|
||||
sb->st_size = 0;
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = 0;
|
||||
} else
|
||||
{
|
||||
sb->st_size = static_cast<int64_t>(Core::File::Size(real_file_name));
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = (sb->st_size + 511) / 512;
|
||||
|
||||
Core::File::GetLastAccessAndWriteTimeUTC(real_file_name, &at, &wt);
|
||||
}
|
||||
|
||||
sec_to_timespec(&sb->st_atim, at.ToUnix());
|
||||
sec_to_timespec(&sb->st_mtim, wt.ToUnix());
|
||||
sb->st_ctim = sb->st_atim;
|
||||
sb->st_birthtim = sb->st_mtim;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (d < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (sb == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(d);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
printf("\tKernelFstat: %s\n", file->real_name.C_Str());
|
||||
|
||||
memset(sb, 0, sizeof(FileStat));
|
||||
|
||||
sb->st_mode = 0000777u | (file->directory ? 0040000u : 0100000u);
|
||||
|
||||
Core::DateTime at;
|
||||
Core::DateTime wt;
|
||||
|
||||
if (!file->directory)
|
||||
{
|
||||
file->mutex.Lock();
|
||||
|
||||
bool is_invalid = file->f.IsInvalid();
|
||||
auto size = file->f.Size();
|
||||
file->f.GetLastAccessAndWriteTimeUTC(&at, &wt);
|
||||
|
||||
file->mutex.Unlock();
|
||||
|
||||
if (is_invalid)
|
||||
{
|
||||
printf("\tfile is invalid\n");
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
sb->st_size = static_cast<int64_t>(size);
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = (sb->st_size + 511) / 512;
|
||||
} else
|
||||
{
|
||||
sb->st_size = 0;
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = 0;
|
||||
}
|
||||
|
||||
sec_to_timespec(&sb->st_atim, at.ToUnix());
|
||||
sec_to_timespec(&sb->st_mtim, wt.ToUnix());
|
||||
sb->st_ctim = sb->st_atim;
|
||||
sb->st_birthtim = sb->st_mtim;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelUnlink(const char* path)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_mount_points == nullptr);
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto path_s = String::FromUtf8(path);
|
||||
auto real_file_name = g_mount_points->GetRealFilename(path_s);
|
||||
auto real_directory = g_mount_points->GetRealDirectory(path_s);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(g_files->GetFile(real_file_name) != nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(g_files->GetFile(real_directory) != nullptr);
|
||||
|
||||
bool is_dir = Core::File::IsDirectoryExisting(real_file_name) || Core::File::IsDirectoryExisting(real_directory);
|
||||
bool is_file = Core::File::IsFileExisting(real_file_name);
|
||||
|
||||
if (is_dir)
|
||||
{
|
||||
return KERNEL_ERROR_EPERM;
|
||||
}
|
||||
|
||||
if (!is_file)
|
||||
{
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
bool ok = Core::File::DeleteFile(real_file_name);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
printf("\tKernelUnlink: %s\n", path);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_files == nullptr);
|
||||
|
||||
if (fd < DESCRIPTOR_MIN)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
auto* file = g_files->GetFile(fd);
|
||||
|
||||
if (file == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!file->directory || nbytes < 512 || file->dents_index > file->dents.Size())
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EXIT_IF(!file->opened);
|
||||
|
||||
printf("\tdir = %s\n", file->real_name.C_Str());
|
||||
printf("\tnbytes = %d\n", nbytes);
|
||||
printf("\tindex = %d\n", file->dents_index);
|
||||
|
||||
if (basep != nullptr)
|
||||
{
|
||||
*basep = file->dents_index;
|
||||
}
|
||||
|
||||
if (file->dents_index == file->dents.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& entry = file->dents.At(file->dents_index++);
|
||||
|
||||
auto str = entry.name.utf8_str();
|
||||
auto str_size = str.Size() - 1;
|
||||
EXIT_NOT_IMPLEMENTED(str_size > 255);
|
||||
|
||||
printf("\tname = %s\n", str.GetDataConst());
|
||||
|
||||
*reinterpret_cast<uint32_t*>(buf + 0) = entry.name.Hash();
|
||||
*reinterpret_cast<uint16_t*>(buf + 4) = 512;
|
||||
*reinterpret_cast<uint8_t*>(buf + 6) = (entry.is_file ? 8 : 4);
|
||||
*reinterpret_cast<uint8_t*>(buf + 7) = static_cast<uint8_t>(str_size);
|
||||
strncpy(buf + 8, str.GetDataConst(), 255);
|
||||
buf[8 + 255] = '\0';
|
||||
|
||||
return 512;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::FileSystem
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
@@ -0,0 +1,604 @@
|
||||
#include "Emulator/Kernel/Memory.h"
|
||||
|
||||
#include "Kyty/Core/DbgAssert.h"
|
||||
#include "Kyty/Core/MagicEnum.h"
|
||||
#include "Kyty/Core/String.h"
|
||||
#include "Kyty/Core/Threads.h"
|
||||
#include "Kyty/Core/Vector.h"
|
||||
|
||||
#include "Emulator/Graphics/GpuMemory.h"
|
||||
#include "Emulator/Graphics/GraphicsRun.h"
|
||||
#include "Emulator/Graphics/Window.h"
|
||||
#include "Emulator/Libs/Errno.h"
|
||||
#include "Emulator/Libs/Libs.h"
|
||||
#include "Emulator/VirtualMemory.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef KYTY_EMU_ENABLED
|
||||
|
||||
namespace Kyty::Libs::LibKernel::Memory {
|
||||
|
||||
namespace VirtualMemory = Loader::VirtualMemory;
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
class PhysicalMemory
|
||||
{
|
||||
public:
|
||||
struct AllocatedBlock
|
||||
{
|
||||
uint64_t start_addr;
|
||||
uint64_t size;
|
||||
uint64_t map_vaddr;
|
||||
uint64_t map_size;
|
||||
int prot;
|
||||
VirtualMemory::Mode mode;
|
||||
Graphics::GpuMemoryMode gpu_mode;
|
||||
};
|
||||
|
||||
PhysicalMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~PhysicalMemory() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(PhysicalMemory);
|
||||
|
||||
static uint64_t Size() { return static_cast<uint64_t>(5376) * 1024 * 1024; }
|
||||
|
||||
bool Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out);
|
||||
bool Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size, Graphics::GpuMemoryMode* gpu_mode);
|
||||
bool Map(uint64_t vaddr, uint64_t phys_addr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode);
|
||||
bool Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode);
|
||||
bool Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode, Graphics::GpuMemoryMode* gpu_mode);
|
||||
|
||||
private:
|
||||
Vector<AllocatedBlock> m_allocated;
|
||||
Core::Mutex m_mutex;
|
||||
};
|
||||
|
||||
class FlexibleMemory
|
||||
{
|
||||
public:
|
||||
struct AllocatedBlock
|
||||
{
|
||||
uint64_t map_vaddr;
|
||||
uint64_t map_size;
|
||||
int prot;
|
||||
VirtualMemory::Mode mode;
|
||||
Graphics::GpuMemoryMode gpu_mode;
|
||||
};
|
||||
|
||||
FlexibleMemory() { EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread()); }
|
||||
virtual ~FlexibleMemory() { KYTY_NOT_IMPLEMENTED; }
|
||||
|
||||
KYTY_CLASS_NO_COPY(FlexibleMemory);
|
||||
|
||||
bool Map(uint64_t vaddr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode);
|
||||
bool Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode);
|
||||
bool Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode, Graphics::GpuMemoryMode* gpu_mode);
|
||||
|
||||
private:
|
||||
Vector<AllocatedBlock> m_allocated;
|
||||
Core::Mutex m_mutex;
|
||||
};
|
||||
|
||||
static PhysicalMemory* g_physical_memory = nullptr;
|
||||
static FlexibleMemory* g_flexible_memory = nullptr;
|
||||
|
||||
KYTY_SUBSYSTEM_INIT(Memory)
|
||||
{
|
||||
g_physical_memory = new PhysicalMemory;
|
||||
g_flexible_memory = new FlexibleMemory;
|
||||
}
|
||||
|
||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Memory) {}
|
||||
|
||||
KYTY_SUBSYSTEM_DESTROY(Memory) {}
|
||||
|
||||
static uint64_t get_aligned_pos(uint64_t pos, size_t align)
|
||||
{
|
||||
return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos);
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Alloc(uint64_t search_start, uint64_t search_end, size_t len, size_t alignment, uint64_t* phys_addr_out)
|
||||
{
|
||||
if (phys_addr_out == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint64_t free_pos = 0;
|
||||
|
||||
for (const auto& b: m_allocated)
|
||||
{
|
||||
uint64_t n = b.start_addr + b.size;
|
||||
if (n > free_pos)
|
||||
{
|
||||
free_pos = n;
|
||||
}
|
||||
}
|
||||
|
||||
free_pos = get_aligned_pos(free_pos, alignment);
|
||||
|
||||
if (free_pos >= search_start && free_pos + len <= search_end)
|
||||
{
|
||||
AllocatedBlock b {};
|
||||
b.size = len;
|
||||
b.start_addr = free_pos;
|
||||
b.gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
b.map_size = 0;
|
||||
b.map_vaddr = 0;
|
||||
b.prot = 0;
|
||||
b.mode = VirtualMemory::Mode::NoAccess;
|
||||
|
||||
m_allocated.Add(b);
|
||||
|
||||
*phys_addr_out = free_pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size, Graphics::GpuMemoryMode* gpu_mode)
|
||||
{
|
||||
EXIT_IF(vaddr == nullptr);
|
||||
EXIT_IF(size == nullptr);
|
||||
EXIT_IF(gpu_mode == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto& b: m_allocated)
|
||||
{
|
||||
if (start == b.start_addr && len == b.size)
|
||||
{
|
||||
*vaddr = b.map_vaddr;
|
||||
*size = b.map_size;
|
||||
*gpu_mode = b.gpu_mode;
|
||||
|
||||
m_allocated.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Map(uint64_t vaddr, uint64_t phys_addr, size_t len, int prot, VirtualMemory::Mode mode,
|
||||
Graphics::GpuMemoryMode gpu_mode)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& b: m_allocated)
|
||||
{
|
||||
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size)
|
||||
{
|
||||
if (b.map_vaddr != 0 || b.map_size != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
b.map_vaddr = vaddr;
|
||||
b.map_size = len;
|
||||
b.prot = prot;
|
||||
b.mode = mode;
|
||||
b.gpu_mode = gpu_mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode)
|
||||
{
|
||||
EXIT_IF(gpu_mode == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& b: m_allocated)
|
||||
{
|
||||
if (b.map_vaddr == vaddr && b.map_size == size)
|
||||
{
|
||||
*gpu_mode = b.gpu_mode;
|
||||
|
||||
b.gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
b.map_size = 0;
|
||||
b.map_vaddr = 0;
|
||||
b.prot = 0;
|
||||
b.mode = VirtualMemory::Mode::NoAccess;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode,
|
||||
Graphics::GpuMemoryMode* gpu_mode)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
return std::any_of(m_allocated.begin(), m_allocated.end(),
|
||||
[vaddr, base_addr, len, prot, mode, gpu_mode](auto& b)
|
||||
{
|
||||
if (vaddr >= b.map_vaddr && vaddr < b.map_vaddr + b.map_size)
|
||||
{
|
||||
if (base_addr != nullptr)
|
||||
{
|
||||
*base_addr = b.map_vaddr;
|
||||
}
|
||||
if (len != nullptr)
|
||||
{
|
||||
*len = b.map_size;
|
||||
}
|
||||
if (prot != nullptr)
|
||||
{
|
||||
*prot = b.prot;
|
||||
}
|
||||
if (mode != nullptr)
|
||||
{
|
||||
*mode = b.mode;
|
||||
}
|
||||
if (gpu_mode != nullptr)
|
||||
{
|
||||
*gpu_mode = b.gpu_mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool FlexibleMemory::Map(uint64_t vaddr, size_t len, int prot, VirtualMemory::Mode mode, Graphics::GpuMemoryMode gpu_mode)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
AllocatedBlock b {};
|
||||
b.map_vaddr = vaddr;
|
||||
b.map_size = len;
|
||||
b.prot = prot;
|
||||
b.mode = mode;
|
||||
b.gpu_mode = gpu_mode;
|
||||
|
||||
m_allocated.Add(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlexibleMemory::Unmap(uint64_t vaddr, uint64_t size, Graphics::GpuMemoryMode* gpu_mode)
|
||||
{
|
||||
EXIT_IF(gpu_mode == nullptr);
|
||||
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
uint32_t index = 0;
|
||||
for (auto& b: m_allocated)
|
||||
{
|
||||
if (b.map_vaddr == vaddr && b.map_size == size)
|
||||
{
|
||||
*gpu_mode = b.gpu_mode;
|
||||
|
||||
m_allocated.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlexibleMemory::Find(uint64_t vaddr, uint64_t* base_addr, size_t* len, int* prot, VirtualMemory::Mode* mode,
|
||||
Graphics::GpuMemoryMode* gpu_mode)
|
||||
{
|
||||
Core::LockGuard lock(m_mutex);
|
||||
|
||||
return std::any_of(m_allocated.begin(), m_allocated.end(),
|
||||
[vaddr, base_addr, len, prot, mode, gpu_mode](auto& b)
|
||||
{
|
||||
if (vaddr >= b.map_vaddr && vaddr < b.map_vaddr + b.map_size)
|
||||
{
|
||||
if (base_addr != nullptr)
|
||||
{
|
||||
*base_addr = b.map_vaddr;
|
||||
}
|
||||
if (len != nullptr)
|
||||
{
|
||||
*len = b.map_size;
|
||||
}
|
||||
if (prot != nullptr)
|
||||
{
|
||||
*prot = b.prot;
|
||||
}
|
||||
if (mode != nullptr)
|
||||
{
|
||||
*mode = b.mode;
|
||||
}
|
||||
if (gpu_mode != nullptr)
|
||||
{
|
||||
*gpu_mode = b.gpu_mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
int32_t KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, const char* name)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_flexible_memory == nullptr);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(addr_in_out == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||
|
||||
VirtualMemory::Mode mode = VirtualMemory::Mode::NoAccess;
|
||||
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
|
||||
switch (prot)
|
||||
{
|
||||
case 0: mode = VirtualMemory::Mode::NoAccess; break;
|
||||
case 1: mode = VirtualMemory::Mode::Read; break;
|
||||
case 2:
|
||||
case 3: mode = VirtualMemory::Mode::ReadWrite; break;
|
||||
case 4: mode = VirtualMemory::Mode::Execute; break;
|
||||
case 5: mode = VirtualMemory::Mode::ExecuteRead; break;
|
||||
case 6:
|
||||
case 7: mode = VirtualMemory::Mode::ExecuteReadWrite; break;
|
||||
default: EXIT("unknown prot: %d\n", prot);
|
||||
}
|
||||
|
||||
auto in_addr = reinterpret_cast<uint64_t>(*addr_in_out);
|
||||
auto out_addr = VirtualMemory::Alloc(in_addr, len, mode);
|
||||
*addr_in_out = reinterpret_cast<void*>(out_addr);
|
||||
|
||||
if (!g_flexible_memory->Map(out_addr, len, prot, mode, gpu_mode))
|
||||
{
|
||||
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
|
||||
VirtualMemory::Free(out_addr);
|
||||
return KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
printf("\tin_addr = 0x%016" PRIx64 "\n", in_addr);
|
||||
printf("\tout_addr = 0x%016" PRIx64 "\n", out_addr);
|
||||
printf("\tsize = %" PRIu64 "\n", len);
|
||||
printf("\tmode = %s\n", Core::EnumName(mode).C_Str());
|
||||
printf("\tname = %s\n", name);
|
||||
|
||||
if (out_addr == 0)
|
||||
{
|
||||
return KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelMunmap(uint64_t vaddr, size_t len)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t start = 0x%016" PRIx64 "\n", vaddr);
|
||||
printf("\t len = 0x%016" PRIx64 "\n", len);
|
||||
|
||||
EXIT_IF(g_physical_memory == nullptr);
|
||||
EXIT_IF(g_flexible_memory == nullptr);
|
||||
|
||||
if (vaddr < 0 || len == 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
|
||||
bool result = g_physical_memory->Unmap(vaddr, len, &gpu_mode);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
result = g_flexible_memory->Unmap(vaddr, len, &gpu_mode);
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!result);
|
||||
|
||||
if (vaddr != 0 || len != 0)
|
||||
{
|
||||
VirtualMemory::Free(vaddr);
|
||||
}
|
||||
|
||||
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
|
||||
{
|
||||
Graphics::GraphicsRunWait();
|
||||
Graphics::GpuMemoryFree(Graphics::WindowGetGraphicContext(), vaddr, len);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
size_t KYTY_SYSV_ABI KernelGetDirectMemorySize()
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
return PhysicalMemory::Size();
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAllocateDirectMemory(int64_t search_start, int64_t search_end, size_t len, size_t alignment, int memory_type,
|
||||
int64_t* phys_addr_out)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_physical_memory == nullptr);
|
||||
|
||||
printf("\t search_start = 0x%016" PRIx64 "\n", search_start);
|
||||
printf("\t search_end = 0x%016" PRIx64 "\n", search_end);
|
||||
printf("\t len = 0x%016" PRIx64 "\n", len);
|
||||
printf("\t alignment = 0x%016" PRIx64 "\n", alignment);
|
||||
printf("\t memory_type = %d\n", memory_type);
|
||||
|
||||
if (search_start < 0 || search_end <= search_start || len == 0 || phys_addr_out == nullptr)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
uint64_t addr = 0;
|
||||
if (!g_physical_memory->Alloc(search_start, search_end, len, alignment, &addr))
|
||||
{
|
||||
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
|
||||
return KERNEL_ERROR_EAGAIN;
|
||||
}
|
||||
|
||||
*phys_addr_out = static_cast<int64_t>(addr);
|
||||
|
||||
printf("\tphys_addr = %016" PRIx64 "\n", addr);
|
||||
printf(FG_GREEN "\t[Ok]\n" FG_DEFAULT);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelReleaseDirectMemory(int64_t start, size_t len)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
printf("\t start = 0x%016" PRIx64 "\n", start);
|
||||
printf("\t len = 0x%016" PRIx64 "\n", len);
|
||||
|
||||
EXIT_IF(g_physical_memory == nullptr);
|
||||
|
||||
if (start < 0 || len == 0)
|
||||
{
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
uint64_t vaddr = 0;
|
||||
uint64_t size = 0;
|
||||
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
|
||||
bool result = g_physical_memory->Release(start, len, &vaddr, &size, &gpu_mode);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(!result);
|
||||
|
||||
if (vaddr != 0 || size != 0)
|
||||
{
|
||||
VirtualMemory::Free(vaddr);
|
||||
}
|
||||
|
||||
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
|
||||
{
|
||||
Graphics::GraphicsRunWait();
|
||||
Graphics::GpuMemoryFree(Graphics::WindowGetGraphicContext(), vaddr, size);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int flags, int64_t direct_memory_start, size_t alignment)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_physical_memory == nullptr);
|
||||
|
||||
// EXIT_NOT_IMPLEMENTED(!Core::Thread::IsMainThread());
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(addr == nullptr);
|
||||
EXIT_NOT_IMPLEMENTED(flags != 0);
|
||||
|
||||
VirtualMemory::Mode mode = VirtualMemory::Mode::NoAccess;
|
||||
Graphics::GpuMemoryMode gpu_mode = Graphics::GpuMemoryMode::NoAccess;
|
||||
|
||||
switch (prot)
|
||||
{
|
||||
case 0x00: mode = VirtualMemory::Mode::NoAccess; break;
|
||||
case 0x01: mode = VirtualMemory::Mode::Read; break;
|
||||
case 0x02:
|
||||
case 0x03: mode = VirtualMemory::Mode::ReadWrite; break;
|
||||
case 0x04: mode = VirtualMemory::Mode::Execute; break;
|
||||
case 0x05: mode = VirtualMemory::Mode::ExecuteRead; break;
|
||||
case 0x06:
|
||||
case 0x07: mode = VirtualMemory::Mode::ExecuteReadWrite; break;
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
mode = VirtualMemory::Mode::ReadWrite;
|
||||
gpu_mode = Graphics::GpuMemoryMode::ReadWrite;
|
||||
break;
|
||||
default: EXIT("unknown prot: %d\n", prot);
|
||||
}
|
||||
|
||||
auto in_addr = reinterpret_cast<uint64_t>(*addr);
|
||||
auto out_addr = VirtualMemory::AllocAligned(in_addr, len, mode, alignment);
|
||||
*addr = reinterpret_cast<void*>(out_addr);
|
||||
|
||||
printf("\tin_addr = 0x%016" PRIx64 "\n", in_addr);
|
||||
printf("\tout_addr = 0x%016" PRIx64 "\n", out_addr);
|
||||
printf("\tsize = 0x%016" PRIx64 "\n", len);
|
||||
printf("\tmode = %s\n", Core::EnumName(mode).C_Str());
|
||||
printf("\talign = 0x%016" PRIx64 "\n", alignment);
|
||||
printf("\tgpu_mode = %s\n", Core::EnumName(gpu_mode).C_Str());
|
||||
|
||||
if (out_addr == 0)
|
||||
{
|
||||
return KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
if (!g_physical_memory->Map(out_addr, direct_memory_start, len, prot, mode, gpu_mode))
|
||||
{
|
||||
printf(FG_RED "\t[Fail]\n" FG_DEFAULT);
|
||||
VirtualMemory::Free(out_addr);
|
||||
return KERNEL_ERROR_EBUSY;
|
||||
}
|
||||
|
||||
if (gpu_mode != Graphics::GpuMemoryMode::NoAccess)
|
||||
{
|
||||
Graphics::GpuMemorySetAllocatedRange(out_addr, len);
|
||||
}
|
||||
|
||||
printf(FG_GREEN "\t[Ok]\n" FG_DEFAULT);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelQueryMemoryProtection(void* addr, void** start, void** end, int* prot)
|
||||
{
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_IF(g_physical_memory == nullptr);
|
||||
EXIT_IF(g_flexible_memory == nullptr);
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(addr == nullptr);
|
||||
|
||||
size_t len = 0;
|
||||
int p = 0;
|
||||
uint64_t base = 0;
|
||||
|
||||
if (!g_physical_memory->Find(reinterpret_cast<uint64_t>(addr), &base, &len, &p, nullptr, nullptr))
|
||||
{
|
||||
if (!g_flexible_memory->Find(reinterpret_cast<uint64_t>(addr), &base, &len, &p, nullptr, nullptr))
|
||||
{
|
||||
return KERNEL_ERROR_EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
if (start != nullptr)
|
||||
{
|
||||
*start = reinterpret_cast<void*>(base);
|
||||
}
|
||||
if (end != nullptr)
|
||||
{
|
||||
*end = reinterpret_cast<void*>(base + len - 1);
|
||||
}
|
||||
if (prot != nullptr)
|
||||
{
|
||||
*prot = p;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Kyty::Libs::LibKernel::Memory
|
||||
|
||||
#endif // KYTY_EMU_ENABLED
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user