void ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { u32 header_code = context.CommandBuffer()[0]; auto itr = handlers.find(header_code); const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; if (info == nullptr || info->handler_callback == nullptr) { return ReportUnimplementedFunction(context.CommandBuffer(), info); } LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), context.CommandBuffer())); handler_invoker(this, info->handler_callback, context); }
void HTTP_C::InitializeConnectionSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x8, 1, 2); const Context::Handle context_handle = rp.Pop<u32>(); u32 pid = rp.PopPID(); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (session_data->initialized) { LOG_ERROR(Service_HTTP, "Tried to initialize an already initialized session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_STATE_ERROR); return; } // TODO(Subv): Check that the input PID matches the PID that created the context. auto itr = contexts.find(context_handle); if (itr == contexts.end()) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ResultCode(ErrCodes::ContextNotFound, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent)); return; } session_data->initialized = true; session_data->session_id = ++session_counter; // Bind the context to the current session. session_data->current_http_context = context_handle; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_HTTP, "called, context_id={} pid={}", context_handle, pid); }
void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x13, 1, 2); u32 flags = rp.Pop<u32>(); auto interrupt_event = rp.PopObject<Kernel::Event>(); // TODO(mailwl): return right error code instead assert ASSERT_MSG((interrupt_event != nullptr), "handle is not valid!"); interrupt_event->SetName("GSP_GSP_GPU::interrupt_event"); SessionData* session_data = GetSessionData(ctx.Session()); session_data->interrupt_event = std::move(interrupt_event); session_data->registered = true; IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); if (first_initialization) { // This specific code is required for a successful initialization, rather than 0 first_initialization = false; rb.Push(RESULT_FIRST_INITIALIZATION); } else { rb.Push(RESULT_SUCCESS); } rb.Push(session_data->thread_id); rb.PushCopyObjects(shared_memory); LOG_DEBUG(Service_GSP, "called, flags=0x{:08X}", flags); }
void File::Write(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0803, 4, 2); u64 offset = rp.Pop<u64>(); u32 length = rp.Pop<u32>(); u32 flush = rp.Pop<u32>(); auto& buffer = rp.PopMappedBuffer(); LOG_TRACE(Service_FS, "Write {}: offset=0x{:x} length={}, flush=0x{:x}", GetName(), offset, length, flush); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); const FileSessionSlot* file = GetSessionData(ctx.Session()); // Subfiles can not be written to if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); rb.Push<u32>(0); rb.PushMappedBuffer(buffer); return; } std::vector<u8> data(length); buffer.Read(data.data(), 0, data.size()); ResultVal<std::size_t> written = backend->Write(offset, data.size(), flush != 0, data.data()); if (written.Failed()) { rb.Push(written.Code()); rb.Push<u32>(0); } else { rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(*written)); } rb.PushMappedBuffer(buffer); }
void HTTP_C::CloseClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x34, 1, 0); ClientCertContext::Handle cert_handle = rp.Pop<u32>(); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (client_certs.find(cert_handle) == client_certs.end()) { LOG_ERROR(Service_HTTP, "Command called with a unkown client cert handle {}", cert_handle); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // This just return success without doing anything rb.Push(RESULT_SUCCESS); return; } if (client_certs[cert_handle].session_id != session_data->session_id) { LOG_ERROR(Service_HTTP, "called from another main session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // This just return success without doing anything rb.Push(RESULT_SUCCESS); return; } client_certs.erase(cert_handle); session_data->num_client_certs--; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_HTTP, "called, cert_handle={}", cert_handle); }
void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x1, 1, 4); const u32 shmem_size = rp.Pop<u32>(); u32 pid = rp.PopPID(); shared_memory = rp.PopObject<Kernel::SharedMemory>(); if (shared_memory) { shared_memory->name = "HTTP_C:shared_memory"; } LOG_WARNING(Service_HTTP, "(STUBBED) called, shared memory size: {} pid: {}", shmem_size, pid); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (session_data->initialized) { LOG_ERROR(Service_HTTP, "Tried to initialize an already initialized session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_STATE_ERROR); return; } session_data->initialized = true; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // This returns 0xd8a0a046 if no network connection is available. // Just assume we are always connected. rb.Push(RESULT_SUCCESS); }
void File::GetPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x080B, 0, 0); const FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(file->priority); }
void File::SetPriority(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x080A, 1, 0); FileSessionSlot* file = GetSessionData(ctx.Session()); file->priority = rp.Pop<u32>(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); }
void File::GetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0804, 0, 0); const FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); rb.Push(RESULT_SUCCESS); rb.Push<u64>(file->size); }
void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x14, 0, 0); SessionData* session_data = GetSessionData(ctx.Session()); session_data->interrupt_event = nullptr; session_data->registered = false; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_GSP, "called"); }
void File::Flush(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0809, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); const FileSessionSlot* file = GetSessionData(ctx.Session()); // Subfiles can not be flushed. if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; } backend->Flush(); rb.Push(RESULT_SUCCESS); }
void File::OpenSubFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0801, 4, 0); s64 offset = rp.PopRaw<s64>(); s64 size = rp.PopRaw<s64>(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); const FileSessionSlot* original_file = GetSessionData(ctx.Session()); if (original_file->subfile) { // OpenSubFile can not be called on a file which is already as subfile rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; } if (offset < 0 || size < 0) { rb.Push(FileSys::ERR_WRITE_BEYOND_END); return; } std::size_t end = offset + size; // TODO(Subv): Check for overflow and return ERR_WRITE_BEYOND_END if (end > original_file->size) { rb.Push(FileSys::ERR_WRITE_BEYOND_END); return; } using Kernel::ClientSession; using Kernel::ServerSession; using Kernel::SharedPtr; auto sessions = system.Kernel().CreateSessionPair(GetName()); auto server = std::get<SharedPtr<ServerSession>>(sessions); ClientConnected(server); FileSessionSlot* slot = GetSessionData(server); slot->priority = original_file->priority; slot->offset = offset; slot->size = size; slot->subfile = true; rb.Push(RESULT_SUCCESS); rb.PushMoveObjects(std::get<SharedPtr<ClientSession>>(sessions)); }
void File::SetSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x0805, 2, 0); u64 size = rp.Pop<u64>(); FileSessionSlot* file = GetSessionData(ctx.Session()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // SetSize can not be called on subfiles. if (file->subfile) { rb.Push(FileSys::ERROR_UNSUPPORTED_OPEN_FLAGS); return; } file->size = size; backend->SetSize(size); rb.Push(RESULT_SUCCESS); }
void HTTP_C::CloseContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x3, 1, 0); u32 context_handle = rp.Pop<u32>(); LOG_WARNING(Service_HTTP, "(STUBBED) called, handle={}", context_handle); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (!session_data->initialized) { LOG_ERROR(Service_HTTP, "Tried to close a context on an uninitialized session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_STATE_ERROR); return; } ASSERT_MSG(!session_data->current_http_context, "Unimplemented CloseContext on context-bound session"); auto itr = contexts.find(context_handle); if (itr == contexts.end()) { // The real HTTP module just silently fails in this case. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); LOG_ERROR(Service_HTTP, "called, context {} not found", context_handle); return; } // TODO(Subv): What happens if you try to close a context that's currently being used? ASSERT(itr->second.state == RequestState::NotStarted); // TODO(Subv): Make sure that only the session that created the context can close it. contexts.erase(itr); session_data->num_http_contexts--; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); }
void HTTP_C::OpenClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x32, 2, 4); u32 cert_size = rp.Pop<u32>(); u32 key_size = rp.Pop<u32>(); Kernel::MappedBuffer& cert_buffer = rp.PopMappedBuffer(); Kernel::MappedBuffer& key_buffer = rp.PopMappedBuffer(); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); ResultCode result(RESULT_SUCCESS); if (!session_data->initialized) { LOG_ERROR(Service_HTTP, "Command called without Initialize"); result = ERROR_STATE_ERROR; } else if (session_data->current_http_context) { LOG_ERROR(Service_HTTP, "Command called with a bound context"); result = ERROR_NOT_IMPLEMENTED; } else if (session_data->num_client_certs >= 2) { LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs"); result = ERROR_TOO_MANY_CLIENT_CERTS; } else { ++client_certs_counter; client_certs[client_certs_counter].handle = client_certs_counter; client_certs[client_certs_counter].certificate.resize(cert_size); cert_buffer.Read(&client_certs[client_certs_counter].certificate[0], 0, cert_size); client_certs[client_certs_counter].private_key.resize(key_size); cert_buffer.Read(&client_certs[client_certs_counter].private_key[0], 0, key_size); client_certs[client_certs_counter].session_id = session_data->session_id; ++session_data->num_client_certs; } LOG_DEBUG(Service_HTTP, "called, cert_size {}, key_size {}", cert_size, key_size); IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); rb.Push(result); rb.PushMappedBuffer(cert_buffer); rb.PushMappedBuffer(key_buffer); }
void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile {}", GetName()); using Kernel::ClientSession; using Kernel::ServerSession; using Kernel::SharedPtr; IPC::RequestParser rp(ctx, 0x080C, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); auto sessions = system.Kernel().CreateSessionPair(GetName()); auto server = std::get<SharedPtr<ServerSession>>(sessions); ClientConnected(server); FileSessionSlot* slot = GetSessionData(server); const FileSessionSlot* original_file = GetSessionData(ctx.Session()); slot->priority = original_file->priority; slot->offset = 0; slot->size = backend->GetSize(); slot->subfile = false; rb.Push(RESULT_SUCCESS); rb.PushMoveObjects(std::get<SharedPtr<ClientSession>>(sessions)); }
void HTTP_C::OpenDefaultClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x33, 1, 0); u8 cert_id = rp.Pop<u8>(); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (!session_data->initialized) { LOG_ERROR(Service_HTTP, "Command called without Initialize"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_STATE_ERROR); return; } if (session_data->current_http_context) { LOG_ERROR(Service_HTTP, "Command called with a bound context"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_NOT_IMPLEMENTED); return; } if (session_data->num_client_certs >= 2) { LOG_ERROR(Service_HTTP, "tried to load more then 2 client certs"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_TOO_MANY_CLIENT_CERTS); return; } constexpr u8 default_cert_id = 0x40; if (cert_id != default_cert_id) { LOG_ERROR(Service_HTTP, "called with invalid cert_id {}", cert_id); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ERROR_WRONG_CERT_ID); return; } if (!ClCertA.init) { LOG_ERROR(Service_HTTP, "called but ClCertA is missing"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(static_cast<ResultCode>(-1)); return; } const auto& it = std::find_if(client_certs.begin(), client_certs.end(), [default_cert_id, &session_data](const auto& i) { return default_cert_id == i.second.cert_id && session_data->session_id == i.second.session_id; }); if (it != client_certs.end()) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push<u32>(it->first); LOG_DEBUG(Service_HTTP, "called, with an already loaded cert_id={}", cert_id); return; } ++client_certs_counter; client_certs[client_certs_counter].handle = client_certs_counter; client_certs[client_certs_counter].certificate = ClCertA.certificate; client_certs[client_certs_counter].private_key = ClCertA.private_key; client_certs[client_certs_counter].session_id = session_data->session_id; ++session_data->num_client_certs; IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push<u32>(client_certs_counter); LOG_DEBUG(Service_HTTP, "called, cert_id={}", cert_id); }
void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x12, 3, 4); const u32 context_handle = rp.Pop<u32>(); const u32 name_size = rp.Pop<u32>(); const u32 value_size = rp.Pop<u32>(); const std::vector<u8> name_buffer = rp.PopStaticBuffer(); Kernel::MappedBuffer& value_buffer = rp.PopMappedBuffer(); // Copy the name_buffer into a string without the \0 at the end const std::string name(name_buffer.begin(), name_buffer.end() - 1); // Copy the value_buffer into a string without the \0 at the end std::string value(value_size - 1, '\0'); value_buffer.Read(&value[0], 0, value_size - 1); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (!session_data->initialized) { LOG_ERROR(Service_HTTP, "Tried to add post data on an uninitialized session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ERROR_STATE_ERROR); rb.PushMappedBuffer(value_buffer); return; } // This command can only be called with a bound context if (!session_data->current_http_context) { LOG_ERROR(Service_HTTP, "Command called without a bound context"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP, ErrorSummary::Internal, ErrorLevel::Permanent)); rb.PushMappedBuffer(value_buffer); return; } if (session_data->current_http_context != context_handle) { LOG_ERROR(Service_HTTP, "Tried to add post data on a mismatched session input context={} session " "context={}", context_handle, *session_data->current_http_context); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ERROR_STATE_ERROR); rb.PushMappedBuffer(value_buffer); return; } auto itr = contexts.find(context_handle); ASSERT(itr != contexts.end()); if (itr->second.state != RequestState::NotStarted) { LOG_ERROR(Service_HTTP, "Tried to add post data on a context that has already been started."); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ResultCode(ErrCodes::InvalidRequestState, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent)); rb.PushMappedBuffer(value_buffer); return; } ASSERT(std::find_if(itr->second.post_data.begin(), itr->second.post_data.end(), [&name](const Context::PostData& m) -> bool { return m.name == name; }) == itr->second.post_data.end()); itr->second.post_data.emplace_back(name, value); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(value_buffer); LOG_DEBUG(Service_HTTP, "called, name={}, value={}, context_handle={}", name, value, context_handle); }
void HTTP_C::CreateContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x2, 2, 2); const u32 url_size = rp.Pop<u32>(); RequestMethod method = rp.PopEnum<RequestMethod>(); Kernel::MappedBuffer& buffer = rp.PopMappedBuffer(); // Copy the buffer into a string without the \0 at the end of the buffer std::string url(url_size, '\0'); buffer.Read(&url[0], 0, url_size - 1); LOG_DEBUG(Service_HTTP, "called, url_size={}, url={}, method={}", url_size, url, static_cast<u32>(method)); auto* session_data = GetSessionData(ctx.Session()); ASSERT(session_data); if (!session_data->initialized) { LOG_ERROR(Service_HTTP, "Tried to create a context on an uninitialized session"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ERROR_STATE_ERROR); rb.PushMappedBuffer(buffer); return; } // This command can only be called without a bound session. if (session_data->current_http_context) { LOG_ERROR(Service_HTTP, "Command called with a bound context"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ResultCode(ErrorDescription::NotImplemented, ErrorModule::HTTP, ErrorSummary::Internal, ErrorLevel::Permanent)); rb.PushMappedBuffer(buffer); return; } static constexpr std::size_t MaxConcurrentHTTPContexts = 8; if (session_data->num_http_contexts >= MaxConcurrentHTTPContexts) { // There can only be 8 HTTP contexts open at the same time for any particular session. LOG_ERROR(Service_HTTP, "Tried to open too many HTTP contexts"); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ResultCode(ErrCodes::TooManyContexts, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent)); rb.PushMappedBuffer(buffer); return; } if (method == RequestMethod::None || static_cast<u32>(method) >= TotalRequestMethods) { LOG_ERROR(Service_HTTP, "invalid request method={}", static_cast<u32>(method)); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ResultCode(ErrCodes::InvalidRequestMethod, ErrorModule::HTTP, ErrorSummary::InvalidState, ErrorLevel::Permanent)); rb.PushMappedBuffer(buffer); return; } contexts.emplace(++context_counter, Context()); contexts[context_counter].url = std::move(url); contexts[context_counter].method = method; contexts[context_counter].state = RequestState::NotStarted; // TODO(Subv): Find a correct default value for this field. contexts[context_counter].socket_buffer_size = 0; contexts[context_counter].handle = context_counter; contexts[context_counter].session_id = session_data->session_id; session_data->num_http_contexts++; IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); rb.Push<u32>(context_counter); rb.PushMappedBuffer(buffer); }