subrepo:
  subdir:   "libraries"
  merged:   "07af583b"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "07af583b"
git-subrepo:
  version:  "0.4.0"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "5d6aba9"
This commit is contained in:
Michael Scire
2019-12-09 03:57:37 -08:00
committed by SciresM
parent 28717bfd27
commit 0105455086
294 changed files with 29915 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::sf::cmif {
ServerDomainManager::Domain::~Domain() {
while (!this->entries.empty()) {
Entry *entry = &this->entries.front();
{
std::scoped_lock lk(this->manager->entry_owner_lock);
AMS_ASSERT(entry->owner == this);
entry->owner = nullptr;
}
entry->object.Reset();
this->entries.pop_front();
this->manager->entry_manager.FreeEntry(entry);
}
}
void ServerDomainManager::Domain::DestroySelf() {
ServerDomainManager *manager = this->manager;
this->~Domain();
manager->FreeDomain(this);
}
Result ServerDomainManager::Domain::ReserveIds(DomainObjectId *out_ids, size_t count) {
for (size_t i = 0; i < count; i++) {
Entry *entry = this->manager->entry_manager.AllocateEntry();
R_UNLESS(entry != nullptr, sf::cmif::ResultOutOfDomainEntries());
AMS_ASSERT(entry->owner == nullptr);
out_ids[i] = this->manager->entry_manager.GetId(entry);
}
return ResultSuccess();
}
void ServerDomainManager::Domain::ReserveSpecificIds(const DomainObjectId *ids, size_t count) {
this->manager->entry_manager.AllocateSpecificEntries(ids, count);
}
void ServerDomainManager::Domain::UnreserveIds(const DomainObjectId *ids, size_t count) {
for (size_t i = 0; i < count; i++) {
Entry *entry = this->manager->entry_manager.GetEntry(ids[i]);
AMS_ASSERT(entry != nullptr);
AMS_ASSERT(entry->owner == nullptr);
this->manager->entry_manager.FreeEntry(entry);
}
}
void ServerDomainManager::Domain::RegisterObject(DomainObjectId id, ServiceObjectHolder &&obj) {
Entry *entry = this->manager->entry_manager.GetEntry(id);
AMS_ASSERT(entry != nullptr);
{
std::scoped_lock lk(this->manager->entry_owner_lock);
AMS_ASSERT(entry->owner == nullptr);
entry->owner = this;
this->entries.push_back(*entry);
}
entry->object = std::move(obj);
}
ServiceObjectHolder ServerDomainManager::Domain::UnregisterObject(DomainObjectId id) {
ServiceObjectHolder obj;
Entry *entry = this->manager->entry_manager.GetEntry(id);
if (entry == nullptr) {
return ServiceObjectHolder();
}
{
std::scoped_lock lk(this->manager->entry_owner_lock);
if (entry->owner != this) {
return ServiceObjectHolder();
}
entry->owner = nullptr;
obj = std::move(entry->object);
this->entries.erase(this->entries.iterator_to(*entry));
}
this->manager->entry_manager.FreeEntry(entry);
return obj;
}
ServiceObjectHolder ServerDomainManager::Domain::GetObject(DomainObjectId id) {
Entry *entry = this->manager->entry_manager.GetEntry(id);
if (entry == nullptr) {
return ServiceObjectHolder();
}
{
std::scoped_lock lk(this->manager->entry_owner_lock);
if (entry->owner != this) {
return ServiceObjectHolder();
}
}
return entry->object.Clone();
}
ServerDomainManager::EntryManager::EntryManager(DomainEntryStorage *entry_storage, size_t entry_count) {
this->entries = reinterpret_cast<Entry *>(entry_storage);
this->num_entries = entry_count;
for (size_t i = 0; i < this->num_entries; i++) {
Entry *entry = new (this->entries + i) Entry();
this->free_list.push_back(*entry);
}
}
ServerDomainManager::EntryManager::~EntryManager() {
for (size_t i = 0; i < this->num_entries; i++) {
this->entries[i].~Entry();
}
}
ServerDomainManager::Entry *ServerDomainManager::EntryManager::AllocateEntry() {
std::scoped_lock lk(this->lock);
if (this->free_list.empty()) {
return nullptr;
}
Entry *e = &this->free_list.front();
this->free_list.pop_front();
return e;
}
void ServerDomainManager::EntryManager::FreeEntry(Entry *entry) {
std::scoped_lock lk(this->lock);
AMS_ASSERT(entry->owner == nullptr);
AMS_ASSERT(!entry->object);
this->free_list.push_front(*entry);
}
void ServerDomainManager::EntryManager::AllocateSpecificEntries(const DomainObjectId *ids, size_t count) {
std::scoped_lock lk(this->lock);
/* Allocate new IDs. */
for (size_t i = 0; i < count; i++) {
const auto id = ids[i];
Entry *entry = this->GetEntry(id);
if (id != InvalidDomainObjectId) {
AMS_ASSERT(entry != nullptr);
AMS_ASSERT(entry->owner == nullptr);
this->free_list.erase(this->free_list.iterator_to(*entry));
}
}
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::sf::cmif {
Result DomainServiceObjectDispatchTable::ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
return this->ProcessMessageImpl(ctx, static_cast<DomainServiceObject *>(ctx.srv_obj)->GetServerDomain(), in_raw_data);
}
Result DomainServiceObjectDispatchTable::ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
return this->ProcessMessageForMitmImpl(ctx, static_cast<DomainServiceObject *>(ctx.srv_obj)->GetServerDomain(), in_raw_data);
}
Result DomainServiceObjectDispatchTable::ProcessMessageImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const {
const CmifDomainInHeader *in_header = reinterpret_cast<const CmifDomainInHeader *>(in_raw_data.GetPointer());
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), sf::cmif::ResultInvalidHeaderSize());
const cmif::PointerAndSize in_domain_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header));
const DomainObjectId target_object_id = DomainObjectId{in_header->object_id};
switch (in_header->type) {
case CmifDomainRequestType_SendMessage:
{
auto target_object = domain->GetObject(target_object_id);
R_UNLESS(static_cast<bool>(target_object), sf::cmif::ResultTargetNotFound());
R_UNLESS(in_header->data_size + in_header->num_in_objects * sizeof(DomainObjectId) <= in_domain_raw_data.GetSize(), sf::cmif::ResultInvalidHeaderSize());
const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_domain_raw_data.GetAddress(), in_header->data_size);
DomainObjectId in_object_ids[8];
R_UNLESS(in_header->num_in_objects <= util::size(in_object_ids), sf::cmif::ResultInvalidNumInObjects());
std::memcpy(in_object_ids, reinterpret_cast<DomainObjectId *>(in_message_raw_data.GetAddress() + in_message_raw_data.GetSize()), sizeof(DomainObjectId) * in_header->num_in_objects);
DomainServiceObjectProcessor domain_processor(domain, in_object_ids, in_header->num_in_objects);
if (ctx.processor == nullptr) {
ctx.processor = &domain_processor;
} else {
ctx.processor->SetImplementationProcessor(&domain_processor);
}
ctx.srv_obj = target_object.GetServiceObjectUnsafe();
return target_object.ProcessMessage(ctx, in_message_raw_data);
}
case CmifDomainRequestType_Close:
/* TODO: N doesn't error check here. Should we? */
domain->UnregisterObject(target_object_id);
return ResultSuccess();
default:
return sf::cmif::ResultInvalidInHeader();
}
}
Result DomainServiceObjectDispatchTable::ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, ServerDomainBase *domain, const cmif::PointerAndSize &in_raw_data) const {
const CmifDomainInHeader *in_header = reinterpret_cast<const CmifDomainInHeader *>(in_raw_data.GetPointer());
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), sf::cmif::ResultInvalidHeaderSize());
const cmif::PointerAndSize in_domain_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header));
const DomainObjectId target_object_id = DomainObjectId{in_header->object_id};
switch (in_header->type) {
case CmifDomainRequestType_SendMessage:
{
auto target_object = domain->GetObject(target_object_id);
/* Mitm. If we don't have a target object, we should forward to let the server handle. */
if (!target_object) {
return ctx.session->ForwardRequest(ctx);
}
R_UNLESS(in_header->data_size + in_header->num_in_objects * sizeof(DomainObjectId) <= in_domain_raw_data.GetSize(), sf::cmif::ResultInvalidHeaderSize());
const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_domain_raw_data.GetAddress(), in_header->data_size);
DomainObjectId in_object_ids[8];
R_UNLESS(in_header->num_in_objects <= util::size(in_object_ids), sf::cmif::ResultInvalidNumInObjects());
std::memcpy(in_object_ids, reinterpret_cast<DomainObjectId *>(in_message_raw_data.GetAddress() + in_message_raw_data.GetSize()), sizeof(DomainObjectId) * in_header->num_in_objects);
DomainServiceObjectProcessor domain_processor(domain, in_object_ids, in_header->num_in_objects);
if (ctx.processor == nullptr) {
ctx.processor = &domain_processor;
} else {
ctx.processor->SetImplementationProcessor(&domain_processor);
}
ctx.srv_obj = target_object.GetServiceObjectUnsafe();
return target_object.ProcessMessage(ctx, in_message_raw_data);
}
case CmifDomainRequestType_Close:
{
auto target_object = domain->GetObject(target_object_id);
/* If the object is not in the domain, tell the server to close it. */
if (!target_object) {
return ctx.session->ForwardRequest(ctx);
}
/* If the object is in the domain, close our copy of it. Mitm objects are required to close their associated domain id, so this shouldn't cause desynch. */
domain->UnregisterObject(target_object_id);
return ResultSuccess();
}
default:
return sf::cmif::ResultInvalidInHeader();
}
}
Result DomainServiceObjectProcessor::PrepareForProcess(const ServiceDispatchContext &ctx, const ServerMessageRuntimeMetadata runtime_metadata) const {
/* Validate in object count. */
R_UNLESS(this->impl_metadata.GetInObjectCount() == this->GetInObjectCount(), sf::cmif::ResultInvalidNumInObjects());
/* Nintendo reserves domain object IDs here. We do this later, to support mitm semantics. */
/* Pass onwards. */
return this->impl_processor->PrepareForProcess(ctx, runtime_metadata);
}
Result DomainServiceObjectProcessor::GetInObjects(ServiceObjectHolder *in_objects) const {
for (size_t i = 0; i < this->GetInObjectCount(); i++) {
in_objects[i] = this->domain->GetObject(this->in_object_ids[i]);
}
return ResultSuccess();
}
HipcRequest DomainServiceObjectProcessor::PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) {
/* Call into impl processor, get request. */
PointerAndSize raw_data;
HipcRequest request = this->impl_processor->PrepareForReply(ctx, raw_data, runtime_metadata);
/* Write out header. */
constexpr size_t out_header_size = sizeof(CmifDomainOutHeader);
const size_t impl_out_data_total_size = this->GetImplOutDataTotalSize();
AMS_ASSERT(out_header_size + impl_out_data_total_size + sizeof(DomainObjectId) * this->GetOutObjectCount() <= raw_data.GetSize());
*reinterpret_cast<CmifDomainOutHeader *>(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = static_cast<u32>(this->GetOutObjectCount()), };
/* Set output raw data. */
out_raw_data = cmif::PointerAndSize(raw_data.GetAddress() + out_header_size, raw_data.GetSize() - out_header_size);
this->out_object_ids = reinterpret_cast<DomainObjectId *>(out_raw_data.GetAddress() + impl_out_data_total_size);
return request;
}
void DomainServiceObjectProcessor::PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const ServerMessageRuntimeMetadata runtime_metadata) {
/* Call into impl processor, get request. */
PointerAndSize raw_data;
this->impl_processor->PrepareForErrorReply(ctx, raw_data, runtime_metadata);
/* Write out header. */
constexpr size_t out_header_size = sizeof(CmifDomainOutHeader);
const size_t impl_out_headers_size = this->GetImplOutHeadersSize();
AMS_ASSERT(out_header_size + impl_out_headers_size <= raw_data.GetSize());
*reinterpret_cast<CmifDomainOutHeader *>(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = 0, };
/* Set output raw data. */
out_raw_data = cmif::PointerAndSize(raw_data.GetAddress() + out_header_size, raw_data.GetSize() - out_header_size);
/* Nintendo unreserves domain entries here, but we haven't reserved them yet. */
}
void DomainServiceObjectProcessor::SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *selected_ids) {
const size_t num_out_objects = this->GetOutObjectCount();
/* Copy input object IDs from command impl (normally these are Invalid, in mitm they should be set). */
DomainObjectId object_ids[8];
bool is_reserved[8];
for (size_t i = 0; i < num_out_objects; i++) {
object_ids[i] = selected_ids[i];
is_reserved[i] = false;
}
/* Reserve object IDs as necessary. */
{
DomainObjectId reservations[8];
{
size_t num_unreserved_ids = 0;
DomainObjectId specific_ids[8];
size_t num_specific_ids = 0;
for (size_t i = 0; i < num_out_objects; i++) {
/* In the mitm case, we must not reserve IDs in use by other objects, so mitm objects will set this. */
if (object_ids[i] == InvalidDomainObjectId) {
num_unreserved_ids++;
} else {
specific_ids[num_specific_ids++] = object_ids[i];
}
}
/* TODO: Can we make this error non-fatal? It isn't for N, since they can reserve IDs earlier due to not having to worry about mitm. */
R_ASSERT(this->domain->ReserveIds(reservations, num_unreserved_ids));
this->domain->ReserveSpecificIds(specific_ids, num_specific_ids);
}
size_t reservation_index = 0;
for (size_t i = 0; i < num_out_objects; i++) {
if (object_ids[i] == InvalidDomainObjectId) {
object_ids[i] = reservations[reservation_index++];
is_reserved[i] = true;
}
}
}
/* Actually set out objects. */
for (size_t i = 0; i < num_out_objects; i++) {
if (!out_objects[i]) {
if (is_reserved[i]) {
this->domain->UnreserveIds(object_ids + i, 1);
}
object_ids[i] = InvalidDomainObjectId;
continue;
}
this->domain->RegisterObject(object_ids[i], std::move(out_objects[i]));
}
/* Set out object IDs in message. */
for (size_t i = 0; i < num_out_objects; i++) {
this->out_object_ids[i] = object_ids[i];
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::sf::cmif {
Result impl::ServiceDispatchTableBase::ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
/* Get versioning info. */
const auto hos_version = hos::GetVersion();
const u32 max_cmif_version = hos_version >= hos::Version_500 ? 1 : 0;
/* Parse the CMIF in header. */
const CmifInHeader *in_header = reinterpret_cast<const CmifInHeader *>(in_raw_data.GetPointer());
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), sf::cmif::ResultInvalidHeaderSize());
R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, sf::cmif::ResultInvalidInHeader());
const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header));
const u32 cmd_id = in_header->command_id;
/* Find a handler. */
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (entries[i].Matches(cmd_id, hos_version)) {
cmd_handler = entries[i].GetHandler();
break;
}
}
R_UNLESS(cmd_handler != nullptr, sf::cmif::ResultUnknownCommandId());
/* Invoke handler. */
CmifOutHeader *out_header = nullptr;
Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data);
/* Forward forwardable results, otherwise ensure we can send result to user. */
R_TRY_CATCH(command_result) {
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
R_CATCH_ALL() { AMS_ASSERT(out_header != nullptr); }
} R_END_TRY_CATCH;
/* Write output header to raw data. */
if (out_header != nullptr) {
*out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0};
}
return ResultSuccess();
}
Result impl::ServiceDispatchTableBase::ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
/* Get versioning info. */
const auto hos_version = hos::GetVersion();
const u32 max_cmif_version = hos_version >= hos::Version_500 ? 1 : 0;
/* Parse the CMIF in header. */
const CmifInHeader *in_header = reinterpret_cast<const CmifInHeader *>(in_raw_data.GetPointer());
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), sf::cmif::ResultInvalidHeaderSize());
R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, sf::cmif::ResultInvalidInHeader());
const cmif::PointerAndSize in_message_raw_data = cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*in_header), in_raw_data.GetSize() - sizeof(*in_header));
const u32 cmd_id = in_header->command_id;
/* Find a handler. */
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (entries[i].Matches(cmd_id, hos_version)) {
cmd_handler = entries[i].GetHandler();
break;
}
}
/* If we didn't find a handler, forward the request. */
if (cmd_handler == nullptr) {
return ctx.session->ForwardRequest(ctx);
}
/* Invoke handler. */
CmifOutHeader *out_header = nullptr;
Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data);
/* Forward forwardable results, otherwise ensure we can send result to user. */
R_TRY_CATCH(command_result) {
R_CATCH(sm::mitm::ResultShouldForwardToSession) {
return ctx.session->ForwardRequest(ctx);
}
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
R_CATCH_ALL() { AMS_ASSERT(out_header != nullptr); }
} R_END_TRY_CATCH;
/* Write output header to raw data. */
if (out_header != nullptr) {
*out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0};
}
return ResultSuccess();
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::sf::cmif {
Result ServiceObjectHolder::ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
const auto ProcessHandler = this->dispatch_meta->ProcessHandler;
const auto *DispatchTable = this->dispatch_meta->DispatchTable;
return (DispatchTable->*ProcessHandler)(ctx, in_raw_data);
}
}