Skip to content

Commit d1f7e94

Browse files
author
sthetix
committed
Fix payload launcher for Mariko (OLED) consoles
- Replace IRAM payload method with payload.bin swap approach - Works on both Erista and Mariko hardware - Simplifies code by removing complex SMC/IRAM logic - HATS installer payload handles hekate restoration automatically
1 parent e709dd9 commit d1f7e94

2 files changed

Lines changed: 129 additions & 62 deletions

File tree

sphaira/include/utils/utils.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ std::string formatSizeStorage(u64 size);
2929
// formats size to 1.23 MB in 1000 base (used for progress bars).
3030
std::string formatSizeNetwork(u64 size);
3131

32-
// Reboot to a payload file (e.g., TegraExplorer.bin)
32+
// Reboot to a payload file (HATS installer)
33+
// Swaps sd:\payload.bin with the installer, then reboots
3334
// Returns true on success, false on failure
3435
bool rebootToPayload(const char* path);
3536

sphaira/source/utils/utils.cpp

Lines changed: 127 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -62,85 +62,151 @@ std::string formatSizeNetwork(u64 size) {
6262
return formatSizeInetrnal(size, 1000.0);
6363
}
6464

65-
// Secure Monitor call for IRAM copy (smcAmsIramCopy)
66-
static void smcCopyToIram(uintptr_t iram_addr, const void* src_addr, u32 size) {
67-
SecmonArgs args = {};
68-
args.X[0] = 0xF0000201; /* smcAmsIramCopy */
69-
args.X[1] = (u64)src_addr; /* DRAM address */
70-
args.X[2] = (u64)iram_addr; /* IRAM address */
71-
args.X[3] = size; /* Amount to copy */
72-
args.X[4] = 1; /* 1 = Write */
73-
svcCallSecureMonitor(&args);
74-
}
65+
// Swap payload.bin with HATS installer and reboot
66+
// This works on both Erista and Mariko since the system loads sd:\payload.bin
67+
bool rebootToPayload(const char* path) {
68+
constexpr const char* PAYLOAD_BIN = "/payload.bin";
69+
constexpr const char* PAYLOAD_BAK = "/payload.bak";
7570

76-
// Set reboot to payload mode
77-
static void smcRebootToIramPayload(void) {
78-
splInitialize();
79-
splSetConfig((SplConfigItem)65001, 2); // NeedsReboot = 2 (reboot to IRAM payload)
80-
splExit();
81-
}
71+
log_write("rebootToPayload: launching HATS installer from: %s\n", path);
8272

83-
bool rebootToPayload(const char* path) {
84-
log_write("rebootToPayload: attempting to load payload from: %s\n", path);
73+
// Step 1: Check if payload.bin exists
74+
FILE* f_existing = fopen(PAYLOAD_BIN, "rb");
75+
if (!f_existing) {
76+
log_write("rebootToPayload: ERROR - %s not found! This system may not be configured correctly.\n", PAYLOAD_BIN);
77+
log_write("rebootToPayload: sd:\\payload.bin should contain hekate for normal boot.\n");
78+
return false;
79+
}
80+
fclose(f_existing);
81+
82+
// Step 2: Backup original payload.bin to payload.bak
83+
log_write("rebootToPayload: backing up %s to %s\n", PAYLOAD_BIN, PAYLOAD_BAK);
8584

86-
// Open the payload file using stdio (simpler, uses fsdev under the hood)
87-
FILE* f = fopen(path, "rb");
88-
if (!f) {
89-
log_write("rebootToPayload: failed to open file\n");
85+
// Read payload.bin (original hekate)
86+
FILE* f_src = fopen(PAYLOAD_BIN, "rb");
87+
if (!f_src) {
88+
log_write("rebootToPayload: failed to open %s for reading\n", PAYLOAD_BIN);
9089
return false;
9190
}
9291

93-
// Get file size
94-
fseek(f, 0, SEEK_END);
95-
long size = ftell(f);
96-
fseek(f, 0, SEEK_SET);
92+
fseek(f_src, 0, SEEK_END);
93+
long original_size = ftell(f_src);
94+
fseek(f_src, 0, SEEK_SET);
9795

98-
log_write("rebootToPayload: payload file size: %ld bytes (max: %u)\n", size, IRAM_PAYLOAD_MAX_SIZE);
96+
if (original_size <= 0) {
97+
log_write("rebootToPayload: invalid payload.bin size: %ld\n", original_size);
98+
fclose(f_src);
99+
return false;
100+
}
99101

100-
// Check payload size
101-
if (size <= 0) {
102-
log_write("rebootToPayload: invalid payload size (<= 0)\n");
103-
fclose(f);
102+
std::vector<u8> original_payload(original_size);
103+
size_t bytes_read = fread(original_payload.data(), 1, original_size, f_src);
104+
fclose(f_src);
105+
106+
if (bytes_read != (size_t)original_size) {
107+
log_write("rebootToPayload: failed to read %s\n", PAYLOAD_BIN);
104108
return false;
105109
}
106-
if (size > IRAM_PAYLOAD_MAX_SIZE) {
107-
log_write("rebootToPayload: payload too large (> %u)\n", IRAM_PAYLOAD_MAX_SIZE);
108-
fclose(f);
110+
111+
// Write backup
112+
FILE* f_bak = fopen(PAYLOAD_BAK, "wb");
113+
if (!f_bak) {
114+
log_write("rebootToPayload: failed to create %s\n", PAYLOAD_BAK);
109115
return false;
110116
}
111117

112-
// Read payload in chunks and copy to IRAM
113-
alignas(0x1000) u8 page_buf[0x1000];
114-
bool success = true;
115-
size_t offset = 0;
116-
117-
while (offset < (size_t)size) {
118-
size_t bytes_to_read = sizeof(page_buf);
119-
if (offset + bytes_to_read > (size_t)size) {
120-
bytes_to_read = (size_t)size - offset;
121-
}
122-
123-
size_t bytes_read = fread(page_buf, 1, bytes_to_read, f);
124-
if (bytes_read == 0) {
125-
log_write("rebootToPayload: failed to read payload at offset %zu\n", offset);
126-
success = false;
127-
break;
128-
}
129-
130-
smcCopyToIram(AMS_IWRAM_OFFSET + offset, page_buf, bytes_read);
131-
offset += bytes_read;
118+
size_t bytes_written = fwrite(original_payload.data(), 1, original_size, f_bak);
119+
fclose(f_bak);
120+
121+
if (bytes_written != (size_t)original_size) {
122+
log_write("rebootToPayload: failed to write backup\n");
123+
remove(PAYLOAD_BAK);
124+
return false;
132125
}
133126

134-
fclose(f);
127+
log_write("rebootToPayload: backup created (%ld bytes)\n", original_size);
135128

136-
if (success) {
137-
log_write("rebootToPayload: payload loaded to IRAM, triggering reboot...\n");
138-
smcRebootToIramPayload();
139-
} else {
140-
log_write("rebootToPayload: failed to load payload to IRAM\n");
129+
// Step 3: Copy HATS installer to payload.bin
130+
log_write("rebootToPayload: copying HATS installer to %s\n", PAYLOAD_BIN);
131+
132+
FILE* f_installer = fopen(path, "rb");
133+
if (!f_installer) {
134+
log_write("rebootToPayload: failed to open HATS installer: %s\n", path);
135+
// Restore backup before returning
136+
FILE* f_restore = fopen(PAYLOAD_BIN, "wb");
137+
fwrite(original_payload.data(), 1, original_size, f_restore);
138+
fclose(f_restore);
139+
remove(PAYLOAD_BAK);
140+
return false;
141141
}
142142

143-
return success;
143+
fseek(f_installer, 0, SEEK_END);
144+
long installer_size = ftell(f_installer);
145+
fseek(f_installer, 0, SEEK_SET);
146+
147+
if (installer_size <= 0) {
148+
log_write("rebootToPayload: invalid HATS installer size: %ld\n", installer_size);
149+
fclose(f_installer);
150+
// Restore backup
151+
FILE* f_restore = fopen(PAYLOAD_BIN, "wb");
152+
fwrite(original_payload.data(), 1, original_size, f_restore);
153+
fclose(f_restore);
154+
remove(PAYLOAD_BAK);
155+
return false;
156+
}
157+
158+
std::vector<u8> installer_data(installer_size);
159+
bytes_read = fread(installer_data.data(), 1, installer_size, f_installer);
160+
fclose(f_installer);
161+
162+
if (bytes_read != (size_t)installer_size) {
163+
log_write("rebootToPayload: failed to read HATS installer\n");
164+
// Restore backup
165+
FILE* f_restore = fopen(PAYLOAD_BIN, "wb");
166+
fwrite(original_payload.data(), 1, original_size, f_restore);
167+
fclose(f_restore);
168+
remove(PAYLOAD_BAK);
169+
return false;
170+
}
171+
172+
// Write HATS installer to payload.bin
173+
FILE* f_dst = fopen(PAYLOAD_BIN, "wb");
174+
if (!f_dst) {
175+
log_write("rebootToPayload: failed to open %s for writing\n", PAYLOAD_BIN);
176+
// Restore backup
177+
FILE* f_restore = fopen(PAYLOAD_BIN, "wb");
178+
fwrite(original_payload.data(), 1, original_size, f_restore);
179+
fclose(f_restore);
180+
remove(PAYLOAD_BAK);
181+
return false;
182+
}
183+
184+
bytes_written = fwrite(installer_data.data(), 1, installer_size, f_dst);
185+
fclose(f_dst);
186+
187+
if (bytes_written != (size_t)installer_size) {
188+
log_write("rebootToPayload: failed to write HATS installer\n");
189+
// Restore backup
190+
FILE* f_restore = fopen(PAYLOAD_BIN, "wb");
191+
fwrite(original_payload.data(), 1, original_size, f_restore);
192+
fclose(f_restore);
193+
remove(PAYLOAD_BAK);
194+
return false;
195+
}
196+
197+
// Step 4: Sync filesystem
198+
fsdevCommitDevice("sdmc");
199+
200+
log_write("rebootToPayload: payload swapped (%ld bytes), rebooting...\n", installer_size);
201+
202+
// Step 5: Reboot - system will load sd:\payload.bin (which is now HATS installer)
203+
log_write("rebootToPayload: HATS installer will restore hekate after installation\n");
204+
205+
spsmInitialize();
206+
spsmShutdown(true);
207+
208+
// Should not reach here
209+
return false;
144210
}
145211

146212
} // namespace sphaira::utils

0 commit comments

Comments
 (0)