tma: First pass at tio file read/write

This commit is contained in:
Michael Scire 2018-12-06 15:32:27 -08:00
parent efcce68a56
commit 600ad660a6
16 changed files with 600 additions and 15 deletions

View file

@ -84,8 +84,8 @@ class DebugMonitorService final : public IServiceObject {
Result TargetIO_FileOpen(OutBuffer<u64> out_hnd, InBuffer<char> path, int open_mode, u32 create_mode);
Result TargetIO_FileClose(InBuffer<u64> hnd);
Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8> out_data, Out<u32> out_read, u64 offset);
Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8> data, Out<u32> out_written, u64 offset);
Result TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset);
Result TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset);
Result TargetIO_FileSetAttributes(InBuffer<char> path, InBuffer<u8> attributes);
Result TargetIO_FileGetInformation(InBuffer<char> path, OutBuffer<u64> out_info, Out<int> is_directory);
Result TargetIO_FileSetTime(InBuffer<char> path, u64 create, u64 access, u64 modify);

View file

@ -157,7 +157,7 @@ Result DebugMonitorService::TargetIO_FileClose(InBuffer<u64> hnd) {
return CloseFileByHandle(hnd[0]);
}
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8> out_data, Out<u32> out_read, u64 offset) {
Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8, BufferType_Type1> out_data, Out<u32> out_read, u64 offset) {
if (hnd.num_elements != 1) {
return 0xF601;
}
@ -174,7 +174,7 @@ Result DebugMonitorService::TargetIO_FileRead(InBuffer<u64> hnd, OutBuffer<u8> o
return rc;
}
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8> data, Out<u32> out_written, u64 offset) {
Result DebugMonitorService::TargetIO_FileWrite(InBuffer<u64> hnd, InBuffer<u8, BufferType_Type1> data, Out<u32> out_written, u64 offset) {
if (hnd.num_elements != 1) {
return 0xF601;
}

View file

@ -19,7 +19,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/test source/settings
SOURCES := source source/test source/settings source/target_io
DATA := data
INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src

View file

@ -20,16 +20,15 @@ def main(argc, argv):
print 'Waiting for connection...'
c.wait_connected()
print 'Connected!'
c.intf.send_packet(Packet().set_service(ServiceId.SETTINGS_SERVICE).set_task(0x01000000).set_cmd(0).write_str('platformconfig').write_str('platformtype'))
print 'Reading atmosphere/BCT.ini...'
c.intf.send_packet(Packet().set_service(ServiceId.TARGETIO_SERVICE).set_task(0x01000000).set_cmd(2).write_str('atmosphere/BCT.ini').write_u64(0x109).write_u64(0))
resp = c.intf.read_packet()
success = resp.read_u8() != 0
print 'Succeeded: %s' % str(success)
if success:
size = resp.read_u32()
value = resp.body[resp.offset:resp.offset+size]
print 'Value Size: 0x%x' % size
print 'Value: %s' % repr(value)
res_packet = c.intf.read_packet()
read_res, size_read = resp.read_u32(), resp.read_u32()
print 'Final Result: 0x%x' % res_packet.read_u32()
print 'Size Read: 0x%x' % size_read
print 'Data:\n%s' % resp.body[resp.offset:]
return 0
if __name__ == '__main__':

View file

@ -25,5 +25,6 @@ USB_CONNECT = hash("USBConnect")
USB_DISCONNECT = hash("USBDisconnect")
ATMOSPHERE_TEST_SERVICE = hash("AtmosphereTestService")
SETTINGS_SERVICE = hash ("SettingsService")
SETTINGS_SERVICE = hash("SettingsService")
TARGETIO_SERVICE = hash("TIOService")

View file

@ -0,0 +1,188 @@
/*
* Copyright (c) 2018 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 <switch.h>
#include "dmnt.h"
static Service g_dmntSrv;
static u64 g_refCnt;
Result dmntInitialize(void) {
atomicIncrement64(&g_refCnt);
if (serviceIsActive(&g_dmntSrv))
return 0;
return smGetService(&g_dmntSrv, "dmnt:-");
}
void dmntExit(void) {
if (atomicDecrement64(&g_refCnt) == 0)
serviceClose(&g_dmntSrv);
}
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, path, FS_MAX_PATH, BufferType_Normal);
ipcAddRecvBuffer(&c, out, sizeof(*out), BufferType_Normal);
struct {
u64 magic;
u64 cmd_id;
int flags;
u32 create_option;
} *raw;
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 29;
raw->flags = flags;
raw->create_option = create_option;
Result rc = serviceIpcDispatch(&g_dmntSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result dmntTargetIOFileClose(DmntFile *f) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 30;
Result rc = serviceIpcDispatch(&g_dmntSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t *out_read) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
ipcAddRecvBuffer(&c, buf, len, BufferType_Type1);
struct {
u64 magic;
u64 cmd_id;
u64 offset;
} *raw;
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 31;
raw->offset = off;
Result rc = serviceIpcDispatch(&g_dmntSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u32 out_read;
} *resp;
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc) && out_read) {
*out_read = resp->out_read;
}
}
return rc;
}
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t *out_written) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
ipcAddSendBuffer(&c, buf, len, BufferType_Type1);
struct {
u64 magic;
u64 cmd_id;
u64 offset;
} *raw;
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 32;
raw->offset = off;
Result rc = serviceIpcDispatch(&g_dmntSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u32 out_written;
} *resp;
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc) && out_written) {
*out_written = resp->out_written;
}
}
return rc;
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 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/>.
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
u64 handle;
} DmntFile;
typedef enum {
DmntTIOCreateOption_CreateNew = 1,
DmntTIOCreateOption_CreateAlways = 2,
DmntTIOCreateOption_OpenExisting = 3,
DmntTIOCreateOption_OpenAlways = 4,
DmntTIOCreateOption_ResetSize = 5,
} DmntTIOCreateOption;
Result dmntInitialize(void);
void dmntExit(void);
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option);
Result dmntTargetIOFileClose(DmntFile *f);
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t* out_read);
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t* out_written);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 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 <switch.h>
#include <stratosphere.hpp>
#include "tio_service.hpp"
#include "tio_task.hpp"
TmaTask *TIOService::NewTask(TmaPacket *packet) {
TmaTask *new_task = nullptr;
switch (packet->GetCommand()) {
case TIOServiceCmd_FileRead:
{
new_task = new TIOFileReadTask(this->manager);
}
break;
case TIOServiceCmd_FileWrite:
{
new_task = new TIOFileWriteTask(this->manager);
}
break;
default:
new_task = nullptr;
break;
}
if (new_task != nullptr) {
new_task->SetServiceId(this->GetServiceId());
new_task->SetTaskId(packet->GetTaskId());
new_task->OnStart(packet);
}
return new_task;
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2018 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/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../tma_conn_service_ids.hpp"
#include "../tma_service.hpp"
enum TIOServiceCmd : u32 {
TIOServiceCmd_FileRead = 2,
TIOServiceCmd_FileWrite = 3,
};
class TIOService : public TmaService {
public:
TIOService(TmaServiceManager *m) : TmaService(m, "TIOService") { }
virtual ~TIOService() { }
virtual TmaTask *NewTask(TmaPacket *packet) override;
};

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2018 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 <switch.h>
#include <stratosphere.hpp>
#include "tio_task.hpp"
void TIOFileReadTask::OnStart(TmaPacket *packet) {
char path[FS_MAX_PATH];
packet->ReadString(path, sizeof(path), nullptr);
packet->Read<u64>(this->size_remaining);
packet->Read<u64>(this->cur_offset);
if (strlen(path) == 0) {
this->SendResult(0x202);
return;
}
Result rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
if (R_FAILED(rc)) {
this->SendResult(rc);
return;
} else {
auto packet = this->AllocateSendPacket();
rc = this->ProcessPacket(packet);
if (R_SUCCEEDED(rc)) {
this->manager->SendPacket(packet);
if (this->size_remaining) {
this->SetNeedsPackets(true);
} else {
this->SendResult(rc);
}
} else {
this->manager->FreePacket(packet);
this->SendResult(rc);
}
}
}
void TIOFileReadTask::OnSendPacket(TmaPacket *packet) {
Result rc = this->ProcessPacket(packet);
if (this->size_remaining == 0 || R_FAILED(rc)) {
this->SendResult(rc);
}
}
void TIOFileReadTask::SendResult(Result rc) {
dmntTargetIOFileClose(&this->handle);
this->SetNeedsPackets(false);
auto packet = this->AllocateSendPacket();
packet->Write<Result>(rc);
this->manager->SendPacket(packet);
Complete();
}
Result TIOFileReadTask::ProcessPacket(TmaPacket *packet) {
Result rc = 0x196002;
size_t cur_read = static_cast<u32>((this->size_remaining > MaxDataSize) ? MaxDataSize : this->size_remaining);
u8 *buf = new u8[cur_read];
if (buf != nullptr) {
size_t actual_read = 0;
rc = dmntTargetIOFileRead(&this->handle, this->cur_offset, buf, cur_read, &actual_read);
if (R_SUCCEEDED(rc)) {
packet->Write<Result>(rc);
packet->Write<u32>(actual_read);
packet->Write(buf, actual_read);
this->cur_offset += actual_read;
this->size_remaining -= actual_read;
}
delete buf;
}
return rc;
}
void TIOFileWriteTask::OnStart(TmaPacket *packet) {
char path[FS_MAX_PATH];
packet->ReadString(path, sizeof(path), nullptr);
packet->Read<u64>(this->size_remaining);
packet->Read<u64>(this->cur_offset);
if (strlen(path) == 0) {
this->SendResult(0x202);
return;
}
Result rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
if (R_FAILED(rc)) {
this->SendResult(rc);
}
}
void TIOFileWriteTask::OnReceivePacket(TmaPacket *packet) {
Result rc = this->ProcessPacket(packet);
if (this->size_remaining == 0 || R_FAILED(rc)) {
this->SendResult(rc);
}
}
void TIOFileWriteTask::SendResult(Result rc) {
dmntTargetIOFileClose(&this->handle);
auto packet = this->AllocateSendPacket();
packet->Write<Result>(rc);
this->manager->SendPacket(packet);
Complete();
}
Result TIOFileWriteTask::ProcessPacket(TmaPacket *packet) {
Result rc = 0x196002;
/* Note: N does not bounds check this. We do. */
u32 cur_write = 0;
packet->Read<u32>(cur_write);
size_t actual_written = 0;
if (cur_write < MaxDataSize) {
if (cur_write > this->size_remaining) {
cur_write = this->size_remaining;
}
rc = dmntTargetIOFileWrite(&this->handle, this->cur_offset, packet->GetCurrentBodyPtr(), cur_write, &actual_written);
if (R_SUCCEEDED(rc)) {
this->size_remaining -= actual_written;
this->cur_offset += actual_written;
}
}
return rc;
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018 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/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../tma_task.hpp"
#include "../tma_service_manager.hpp"
#include "../dmnt.h"
class TIOTask : public TmaTask {
public:
TIOTask(TmaServiceManager *m) : TmaTask(m) { }
virtual ~TIOTask() { }
virtual void SendResult(Result rc) {
TmaPacket *packet = this->AllocateSendPacket();
packet->Write<Result>(rc);
this->manager->SendPacket(packet);
this->Complete();
}
virtual void OnStart(TmaPacket *packet) = 0;
virtual void OnReceivePacket(TmaPacket *packet) override {
this->Complete();
}
virtual void OnSendPacket(TmaPacket *packet) override {
this->Complete();
}
};
class TIOFileReadTask : public TIOTask {
private:
static constexpr size_t HeaderSize = sizeof(Result) + sizeof(u32);
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
private:
DmntFile handle = {0};
u64 size_remaining = 0;
u64 cur_offset = 0;
public:
TIOFileReadTask(TmaServiceManager *m) : TIOTask(m) { }
virtual ~TIOFileReadTask() { }
virtual void OnStart(TmaPacket *packet) override;
virtual void OnSendPacket(TmaPacket *packet) override;
virtual void SendResult(Result rc) override;
Result ProcessPacket(TmaPacket *packet);
};
class TIOFileWriteTask : public TIOTask {
private:
static constexpr size_t HeaderSize = sizeof(u32);
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
private:
DmntFile handle = {0};
u64 size_remaining = 0;
u64 cur_offset = 0;
public:
TIOFileWriteTask(TmaServiceManager *m) : TIOTask(m) { }
virtual ~TIOFileWriteTask() { }
virtual void OnStart(TmaPacket *packet) override;
virtual void OnReceivePacket(TmaPacket *packet) override;
virtual void SendResult(Result rc) override;
Result ProcessPacket(TmaPacket *packet);
};

View file

@ -166,6 +166,10 @@ class TmaPacket {
return GetHeader()->version;
}
u8 *GetCurrentBodyPtr() {
return GetBody(this->offset);
}
void ClearOffset() {
this->offset = 0;
}

View file

@ -25,6 +25,8 @@
#include "tma_target.hpp"
#include "dmnt.h"
extern "C" {
extern u32 __start__;
@ -70,11 +72,17 @@ void __appInit(void) {
fatalSimple(rc);
}
rc = dmntInitialize();
if (R_FAILED(rc)) {
fatalSimple(rc);
}
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
}
void __appExit(void) {
/* Cleanup services. */
dmntExit();
setsysExit();
pscExit();
smExit();

View file

@ -27,6 +27,7 @@
#include "test/atmosphere_test_service.hpp"
#include "settings/settings_service.hpp"
#include "target_io/tio_service.hpp"
struct TmaTargetConfig {
char configuration_id1[0x80];
@ -210,6 +211,7 @@ void TmaTarget::Initialize() {
/* TODO: Make this better. */
g_service_manager->AddService(new AtmosphereTestService(g_service_manager));
g_service_manager->AddService(new SettingsService(g_service_manager));
g_service_manager->AddService(new TIOService(g_service_manager));
RefreshTargetConfig();

View file

@ -24,6 +24,21 @@ void TmaTask::SetNeedsPackets(bool n) {
this->manager->Tick();
}
TmaPacket *TmaTask::AllocateSendPacket(bool continuation) {
auto packet = this->manager->AllocateSendPacket();
packet->SetServiceId(this->service_id);
packet->SetTaskId(this->task_id);
packet->SetCommand(this->command);
packet->SetContinuation(continuation);
return packet;
}
void TmaTask::FreePacket(TmaPacket *packet) {
this->manager->FreePacket(packet);
}
void TmaTask::Complete() {
SetNeedsPackets(false);
this->state = TmaTaskState::Complete;

View file

@ -68,6 +68,9 @@ class TmaTask {
void Signal() { this->signal.Signal(); }
void ResetSignal() { this->signal.Reset(); }
TmaPacket *AllocateSendPacket(bool continuation = true);
void FreePacket(TmaPacket *packet);
void Complete();
void Cancel();