Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bootloader/hos/pkg3.c
3693 views
1
/*
2
* Atmosphère Package 3 parser.
3
*
4
* Copyright (c) 2019-2025 CTCaer
5
*
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms and conditions of the GNU General Public License,
8
* version 2, as published by the Free Software Foundation.
9
*
10
* This program is distributed in the hope it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13
* more details.
14
*
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <string.h>
20
21
#include <bdk.h>
22
23
#include "pkg3.h"
24
#include "hos.h"
25
#include "../config.h"
26
#include <libs/fatfs/ff.h>
27
#include "../storage/emummc.h"
28
29
//#define DPRINTF(...) gfx_printf(__VA_ARGS__)
30
#define DPRINTF(...)
31
32
extern bool is_ipl_updated(void *buf, u32 size, const char *path, bool force);
33
34
#define PKG3_KIP_SKIP_MAX 16
35
36
// PKG3 Magic and Meta header offset.
37
#define PKG3_MAGIC 0x30535346 // FSS0.
38
#define PKG3_META_OFFSET 0x4
39
#define PKG3_VERSION_0_17_0 0x110000
40
41
// PKG3 Content Types.
42
#define CNT_TYPE_FSP 0
43
#define CNT_TYPE_EXO 1 // Exosphere (Secure Monitor).
44
#define CNT_TYPE_WBT 2 // Warmboot (SC7Exit fw).
45
#define CNT_TYPE_RBT 3 // Rebootstub (Warmboot based reboot fw).
46
#define CNT_TYPE_SP1 4 // Sept Primary (TSEC and Sept Secondary loader).
47
#define CNT_TYPE_SP2 5 // Sept Secondary (Acts as pkg11 and derives keys).
48
#define CNT_TYPE_KIP 6 // KIP1 (Used for replacement or addition).
49
#define CNT_TYPE_BMP 7
50
#define CNT_TYPE_EMC 8
51
#define CNT_TYPE_KLD 9 // Kernel Loader.
52
#define CNT_TYPE_KRN 10 // Kernel.
53
#define CNT_TYPE_EXF 11 // Exosphere Mariko fatal payload.
54
#define CNT_TYPE_TKG 12 // Tsec Keygen.
55
56
// PKG3 Content Flags.
57
#define CNT_FLAG0_EXPERIMENTAL BIT(0)
58
59
// PKG3 Meta Header.
60
typedef struct _pkg3_meta_t
61
{
62
u32 magic;
63
u32 size;
64
u32 crt0_off;
65
u32 cnt_off;
66
u32 cnt_count;
67
u32 hos_ver;
68
u32 version;
69
u32 git_rev;
70
} pkg3_meta_t;
71
72
// PKG3 Content Header.
73
typedef struct _pkg3_content_t
74
{
75
u32 offset;
76
u32 size;
77
u8 type;
78
u8 flags0;
79
u8 flags1;
80
u8 flags2;
81
u32 rsvd1;
82
char name[0x10];
83
} pkg3_content_t;
84
85
static void _pkg3_update_r2p()
86
{
87
u32 size = 0;
88
u8 *r2p_payload = sd_file_read("atmosphere/reboot_payload.bin", &size);
89
90
is_ipl_updated(r2p_payload, size, "atmosphere/reboot_payload.bin", h_cfg.updater2p ? true : false);
91
92
free(r2p_payload);
93
}
94
95
static int _pkg3_kip1_skip(char ***pkg3_kip1_skip, u32 *pkg3_kip1_skip_num, char *value)
96
{
97
int len = strlen(value);
98
if (!len || (*pkg3_kip1_skip_num) >= PKG3_KIP_SKIP_MAX)
99
return 1;
100
101
// Allocate pointer list memory.
102
if (!(*pkg3_kip1_skip))
103
(*pkg3_kip1_skip) = zalloc(PKG3_KIP_SKIP_MAX * sizeof(char *));
104
105
// Set first kip name.
106
(*pkg3_kip1_skip)[(*pkg3_kip1_skip_num)++] = value;
107
108
// Check if more names are set separated by comma.
109
for (char *c = value; *c != 0; c++)
110
{
111
if (*c == ',')
112
{
113
*c = 0; // Null termination.
114
115
// Set next kip name to the list.
116
(*pkg3_kip1_skip)[(*pkg3_kip1_skip_num)++] = c + 1;
117
118
if ((*pkg3_kip1_skip_num) >= PKG3_KIP_SKIP_MAX)
119
return 1;
120
}
121
}
122
123
return 0;
124
}
125
126
int parse_pkg3(launch_ctxt_t *ctxt, const char *path)
127
{
128
FIL fp;
129
130
char **pkg3_kip1_skip = NULL;
131
u32 pkg3_kip1_skip_num = 0;
132
133
bool stock = false;
134
bool experimental = false;
135
136
// Skip if stock and Exosphere and warmboot are not needed.
137
bool pkg1_old = ctxt->pkg1_id->mkey <= HOS_MKEY_VER_620; // Should check if t210b01?
138
bool emummc_disabled = !emu_cfg.enabled || h_cfg.emummc_force_disable;
139
140
LIST_FOREACH_ENTRY(ini_kv_t, kv, &ctxt->cfg->kvs, link)
141
{
142
if (!strcmp("stock", kv->key))
143
if (kv->val[0] == '1')
144
stock = true;
145
146
if (!strcmp("pkg3ex", kv->key))
147
if (kv->val[0] == '1')
148
experimental = true;
149
150
if (!strcmp("pkg3kip1skip", kv->key))
151
_pkg3_kip1_skip(&pkg3_kip1_skip, &pkg3_kip1_skip_num, kv->val);
152
}
153
154
#ifdef HOS_MARIKO_STOCK_SECMON
155
if (stock && emummc_disabled && (pkg1_old || h_cfg.t210b01))
156
return 0;
157
#else
158
if (stock && emummc_disabled && pkg1_old)
159
return 0;
160
#endif
161
162
// Try to open PKG3.
163
if (f_open(&fp, path, FA_READ) != FR_OK)
164
return 1;
165
166
void *pkg3 = malloc(f_size(&fp));
167
168
// Read first 1024 bytes of the PKG3 file.
169
f_read(&fp, pkg3, 1024, NULL);
170
171
// Get PKG3 Meta header offset.
172
u32 pkg3_meta_addr = *(u32 *)(pkg3 + PKG3_META_OFFSET);
173
pkg3_meta_t *pkg3_meta = (pkg3_meta_t *)(pkg3 + pkg3_meta_addr);
174
175
// Check if valid PKG3 and parse it.
176
if (pkg3_meta->magic == PKG3_MAGIC)
177
{
178
gfx_printf("Atmosphere %d.%d.%d-%08x via PKG3\n"
179
"Max HOS: %d.%d.%d\n"
180
"Unpacking.. ",
181
pkg3_meta->version >> 24, (pkg3_meta->version >> 16) & 0xFF, (pkg3_meta->version >> 8) & 0xFF, pkg3_meta->git_rev,
182
pkg3_meta->hos_ver >> 24, (pkg3_meta->hos_ver >> 16) & 0xFF, (pkg3_meta->hos_ver >> 8) & 0xFF);
183
184
ctxt->patch_krn_proc_id = true;
185
ctxt->pkg3_hosver = pkg3_meta->hos_ver;
186
187
// Parse PKG3 contents.
188
pkg3_content_t *curr_pkg3_cnt = (pkg3_content_t *)(pkg3 + pkg3_meta->cnt_off);
189
void *content;
190
for (u32 i = 0; i < pkg3_meta->cnt_count; i++)
191
{
192
content = (void *)(pkg3 + curr_pkg3_cnt[i].offset);
193
194
// Check if offset is inside limits.
195
if ((curr_pkg3_cnt[i].offset + curr_pkg3_cnt[i].size) > pkg3_meta->size)
196
continue;
197
198
// If content is experimental and experimental config is not enabled, skip it.
199
if ((curr_pkg3_cnt[i].flags0 & CNT_FLAG0_EXPERIMENTAL) && !experimental)
200
continue;
201
202
// Prepare content.
203
switch (curr_pkg3_cnt[i].type)
204
{
205
case CNT_TYPE_KIP:
206
if (stock)
207
continue;
208
209
bool should_skip = false;
210
for (u32 k = 0; k < pkg3_kip1_skip_num; k++)
211
{
212
if (!strcmp(curr_pkg3_cnt[i].name, pkg3_kip1_skip[k]))
213
{
214
gfx_printf("Skipped %s.kip1 from PKG3\n", curr_pkg3_cnt[i].name);
215
should_skip = true;
216
break;
217
}
218
}
219
if (should_skip)
220
continue;
221
222
merge_kip_t *mkip1 = (merge_kip_t *)malloc(sizeof(merge_kip_t));
223
mkip1->kip1 = content;
224
list_append(&ctxt->kip1_list, &mkip1->link);
225
DPRINTF("Loaded %s.kip1 from PKG3 (size %08X)\n", curr_pkg3_cnt[i].name, curr_pkg3_cnt[i].size);
226
break;
227
228
case CNT_TYPE_KRN:
229
if (stock)
230
continue;
231
ctxt->kernel_size = curr_pkg3_cnt[i].size;
232
ctxt->kernel = content;
233
break;
234
235
case CNT_TYPE_EXO:
236
ctxt->secmon_size = curr_pkg3_cnt[i].size;
237
ctxt->secmon = content;
238
break;
239
240
case CNT_TYPE_EXF:
241
ctxt->exofatal_size = curr_pkg3_cnt[i].size;
242
ctxt->exofatal = content;
243
break;
244
245
case CNT_TYPE_WBT:
246
if (h_cfg.t210b01)
247
continue;
248
ctxt->warmboot_size = curr_pkg3_cnt[i].size;
249
ctxt->warmboot = content;
250
break;
251
252
default:
253
continue;
254
}
255
256
// Load content to launch context.
257
f_lseek(&fp, curr_pkg3_cnt[i].offset);
258
f_read(&fp, content, curr_pkg3_cnt[i].size, NULL);
259
}
260
261
gfx_printf("Done!\n");
262
f_close(&fp);
263
264
ctxt->pkg3 = pkg3;
265
266
// Update r2p if needed.
267
_pkg3_update_r2p();
268
269
free(pkg3_kip1_skip);
270
271
return 0;
272
}
273
274
// Failed. Close and free all.
275
f_close(&fp);
276
277
free(pkg3_kip1_skip);
278
free(pkg3);
279
280
return 1;
281
}
282
283