mikage-dev/source/platform/sm.hpp
2024-03-08 10:54:13 +01:00

122 lines
4.2 KiB
C++

#pragma once
#include "ipc.hpp"
#include <boost/hana/fwd/integral_constant.hpp>
#include <string>
namespace Platform {
/**
* ServiceManager: Manages "services" on the system. Services are nameless
* ports, which implies that applications can not connect to them through
* ConnectToPort system call, but instead always have to go through the "srv:"
* port.
*
* This system allows for fine-grained service access control for each process.
* Before connecting to services through the "srv:" port, applications must be
* registered to the SM module through the "srv:pm" service. An exception to
* this rule is made for processes that are embedded in the system firmware:
* Those processes generally may access any service withour prior registering
* (hence in particular solving the "hen or egg" problem due to which
* unregistered processes technically cannot access the srv:pm service to
* register themselves).
*/
namespace SM {
struct PortName {
PortName(const char* name) {
std::string strname(name);
length = strname.length();
uint32_t value = 0;
for (int i = 0; i < 8; ++i) {
char c = (i < length) ? strname[i] : 0;
value = (c << 24) | (value >> 8);
if (i == 3 || i == 7) {
this->name[i / 4] = value;
value = 0;
}
}
}
PortName(uint32_t low, uint32_t high, uint32_t length) : name{low, high}, length(length) {
}
static auto IPCDeserialize(uint32_t word_low, uint32_t word_high, uint32_t length) {
return PortName(word_low, word_high, length);
}
static auto IPCSerialize(const PortName& data) {
return std::make_tuple(data.name[0], data.name[1], data.length);
}
std::string ToString() const {
std::string ret;
for (unsigned index = 0; index < 8; ++index) {
auto word_index = index / 4;
auto byte_within_word = index % 4;
auto byte = static_cast<uint8_t>(name[word_index] >> (byte_within_word * 8));
if (byte == 0)
break;
ret += byte;
}
return ret;
}
using ipc_data_size = boost::hana::size_t<3>;
uint32_t name[2];
uint32_t length;
};
namespace SRV {
namespace IPC = Platform::IPC;
struct RegisterClient : IPC::IPCCommand<0x1>::add_process_id
::response {};
struct EnableNotification : IPC::IPCCommand<0x2>
::response::add_handle<IPC::HandleType::Semaphore> {};
struct RegisterService : IPC::IPCCommand<0x3>::add_serialized<PortName>::add_uint32
::response::add_and_close_handle<IPC::HandleType::ServerPort> {};
/**
* NOTE: The native SM module returns IPC header 0x00050042 if the service is
* not in the source application's extended header (as provided to
* srvpm:RegisterProcess), which makes our kernel implementation throw
* an error due to attempting to translate a null handle.
* TODO: Double-check whether the reference implementation special-cases
* null handles for this or not.
*/
struct GetServiceHandle : IPC::IPCCommand<0x5>::add_serialized<PortName>::add_uint32
::response::add_and_close_handle<IPC::HandleType::ClientSession> {};
struct Subscribe : IPC::IPCCommand<0x9>::add_uint32
::response {};
struct PublishToSubscriber : IPC::IPCCommand<0xc>::add_uint32::add_uint32
::response {};
} // namespace SRV
namespace SRVPM {
// NOTE: This returns 0xe7e3ffff on success, and 0xd8606408 on error
struct PublishToAll : IPC::IPCCommand<0x402>::add_uint32
::response {};
/**
* Authorizes a process to access services listed in the static buffer
*
* Inputs:
* - ProcessId for the process to authorize
* - Number of words in the given static buffer (i.e. twice the number of services to grant access to)
* - List of PortNames of accessible services
*/
struct RegisterProcess : IPC::IPCCommand<0x403>::add_uint32::add_uint32::add_static_buffer
::response {};
} // namespace SRVPM
} // namespace SM
} // namespace Platform