2019-09-05 14:42:19 +02:00
|
|
|
#include "os.h"
|
|
|
|
#include "kernel/svc.h"
|
|
|
|
#include "loader/nro.h"
|
|
|
|
#include "nce.h"
|
|
|
|
|
|
|
|
extern bool halt;
|
|
|
|
|
|
|
|
namespace lightSwitch::kernel {
|
|
|
|
OS::OS(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, this_process, this_thread, std::make_shared<NCE>(), settings, logger) {}
|
|
|
|
|
|
|
|
void OS::Execute(std::string rom_file) {
|
2019-09-14 14:41:00 +02:00
|
|
|
state.nce->Initialize(state);
|
2019-09-05 14:42:19 +02:00
|
|
|
std::string rom_ext = rom_file.substr(rom_file.find_last_of('.') + 1);
|
|
|
|
std::transform(rom_ext.begin(), rom_ext.end(), rom_ext.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
|
|
|
|
|
|
if (rom_ext == "nro") loader::NroLoader loader(rom_file, state);
|
|
|
|
else throw exception("Unsupported ROM extension.");
|
|
|
|
|
2019-09-14 14:41:00 +02:00
|
|
|
auto main_process = CreateProcess(state.nce->memory_region_map.at(Memory::Region::text)->address, constant::def_stack_size);
|
2019-09-05 14:42:19 +02:00
|
|
|
main_process->thread_map[main_process->main_thread]->Start(); // The kernel itself is responsible for starting the main thread
|
2019-09-14 14:41:00 +02:00
|
|
|
state.nce->Execute();
|
2019-09-05 14:42:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function executed by all child processes after cloning
|
|
|
|
*/
|
|
|
|
int ExecuteChild(void *) {
|
|
|
|
ptrace(PTRACE_TRACEME);
|
|
|
|
asm volatile("brk #0xFF"); // BRK #constant::brk_rdy (So we know when the thread/process is ready)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-14 14:41:00 +02:00
|
|
|
std::shared_ptr<type::KProcess> OS::CreateProcess(u64 address, size_t stack_size) {
|
|
|
|
auto *stack = static_cast<u8 *>(mmap(nullptr, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); // NOLINT(hicpp-signed-bitwise)
|
2019-09-05 14:42:19 +02:00
|
|
|
if (stack == MAP_FAILED)
|
|
|
|
throw exception("Failed to allocate stack memory");
|
|
|
|
if (mprotect(stack, PAGE_SIZE, PROT_NONE)) {
|
|
|
|
munmap(stack, stack_size);
|
|
|
|
throw exception("Failed to create guard pages");
|
|
|
|
}
|
|
|
|
pid_t pid = clone(&ExecuteChild, stack + stack_size, CLONE_FS | SIGCHLD, nullptr); // NOLINT(hicpp-signed-bitwise)
|
2019-09-14 14:41:00 +02:00
|
|
|
if (pid == -1) throw exception(fmt::format("Call to clone() has failed: {}", strerror(errno)));
|
|
|
|
std::shared_ptr<type::KProcess> process = std::make_shared<kernel::type::KProcess>(pid, address, reinterpret_cast<u64>(stack), stack_size, state);
|
2019-09-05 14:42:19 +02:00
|
|
|
process_map[pid] = process;
|
2019-09-14 14:41:00 +02:00
|
|
|
process_vec.push_back(pid);
|
2019-09-05 14:42:19 +02:00
|
|
|
state.logger->Write(Logger::DEBUG, "Successfully created process with PID: {}", pid);
|
|
|
|
return process;
|
|
|
|
}
|
|
|
|
|
|
|
|
void OS::KillThread(pid_t pid) {
|
|
|
|
auto process = process_map.at(pid);
|
2019-09-14 14:41:00 +02:00
|
|
|
if (process->main_thread == pid) {
|
2019-09-05 14:42:19 +02:00
|
|
|
state.logger->Write(Logger::DEBUG, "Exiting process with PID: {}", pid);
|
|
|
|
// Erasing all shared_ptr instances to the process will call the destructor
|
|
|
|
// However, in the case these are not all instances of it we wouldn't want to call the destructor
|
2019-09-14 14:41:00 +02:00
|
|
|
for (auto&[key, value]: process->thread_map) {
|
2019-09-05 14:42:19 +02:00
|
|
|
process_map.erase(key);
|
|
|
|
};
|
2019-09-14 14:41:00 +02:00
|
|
|
process_vec.erase(std::remove(process_vec.begin(), process_vec.end(), pid), process_vec.end());
|
2019-09-05 14:42:19 +02:00
|
|
|
} else {
|
|
|
|
state.logger->Write(Logger::DEBUG, "Exiting thread with TID: {}", pid);
|
|
|
|
process->handle_table.erase(process->thread_map[pid]->handle);
|
|
|
|
process->thread_map.erase(pid);
|
|
|
|
process_map.erase(pid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-14 14:41:00 +02:00
|
|
|
void OS::SvcHandler(u16 svc, pid_t pid) {
|
2019-09-05 14:42:19 +02:00
|
|
|
this_process = process_map.at(pid);
|
|
|
|
this_thread = this_process->thread_map.at(pid);
|
|
|
|
if (svc::svcTable[svc]) {
|
|
|
|
state.logger->Write(Logger::DEBUG, "SVC called 0x{:X}", svc);
|
|
|
|
(*svc::svcTable[svc])(state);
|
|
|
|
} else {
|
|
|
|
throw exception(fmt::format("Unimplemented SVC 0x{:X}", svc));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-14 14:41:00 +02:00
|
|
|
ipc::IpcResponse OS::IpcHandler(handle_t handle) {
|
|
|
|
ipc::IpcRequest request(false, state);
|
|
|
|
ipc::IpcResponse response(false, state);
|
|
|
|
switch (request.header->type) {
|
|
|
|
case static_cast<u16>(ipc::CommandType::Request):
|
|
|
|
case static_cast<u16>(ipc::CommandType::RequestWithContext):
|
|
|
|
throw exception("Services are in-progress");
|
2019-09-05 14:42:19 +02:00
|
|
|
default:
|
2019-09-14 14:41:00 +02:00
|
|
|
throw exception(fmt::format("Unimplemented IPC message type {0}", u16(request.header->type)));
|
2019-09-05 14:42:19 +02:00
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
}
|