4 #include "taihen_internal.h" 5 #include <psp2kern/kernel/sysmem.h> 11 #define assert(x) // turn off asserts 13 #define SLAB_DUMP_COLOURED 15 #ifdef SLAB_DUMP_COLOURED 16 # define GRAY(s) "\033[1;30m" s "\033[0m" 17 # define RED(s) "\033[0;31m" s "\033[0m" 18 # define GREEN(s) "\033[0;32m" s "\033[0m" 19 # define YELLOW(s) "\033[1;33m" s "\033[0m" 27 #define SLOTS_ALL_ZERO ((uint64_t) 0) 28 #define SLOTS_FIRST ((uint64_t) 1) 29 #define FIRST_FREE_SLOT(s) ((size_t) __builtin_ctzll(s)) 30 #define FREE_SLOTS(s) ((size_t) __builtin_popcountll(s)) 31 #define ONE_USED_SLOT(slots, empty_slotmask) \ 34 (~(slots) & (empty_slotmask)) & \ 35 ((~(slots) & (empty_slotmask)) - 1) \ 39 #define POWEROF2(x) ((x) != 0 && ((x) & ((x) - 1)) == 0) 41 #define LIKELY(exp) __builtin_expect(exp, 1) 42 #define UNLIKELY(exp) __builtin_expect(exp, 0) 44 const size_t slab_pagesize = 0x1000;
60 static SceUID sce_exe_alloc(SceUID pid,
void **ptr, uintptr_t *exe_addr, SceUID *exe_res,
size_t align,
size_t size) {
61 SceKernelAllocMemBlockKernelOpt opt;
62 SceKernelMemBlockType type;
65 LOG(
"Allocating exec slab for %x size 0x%08X", pid, size);
67 memset(&opt, 0,
sizeof(opt));
68 opt.size =
sizeof(opt);
69 opt.attr = 0xA0000000 | 0x400000;
70 opt.alignment = align;
72 opt.attr |= SCE_KERNEL_ALLOC_MEMBLOCK_ATTR_HAS_ALIGNMENT;
75 type = SCE_KERNEL_MEMBLOCK_TYPE_KERNEL_RX;
76 }
else if (pid == SHARED_PID) {
77 type = SCE_KERNEL_MEMBLOCK_TYPE_SHARED_RX;
79 type = SCE_KERNEL_MEMBLOCK_TYPE_USER_RX;
83 *exe_res = sceKernelAllocMemBlockForKernel(
"taislab", type, size, &opt);
84 LOG(
"sceKernelAllocMemBlockForKernel(taislab): 0x%08X", *exe_res);
88 res = sceKernelGetMemBlockBaseForKernel(*exe_res, (
void **)exe_addr);
89 LOG(
"sceKernelGetMemBlockBaseForKernel(%x): 0x%08X, addr: 0x%08X", *exe_res, res, *exe_addr);
96 res = sceKernelMapBlockUserVisible(*exe_res);
97 LOG(
"sceKernelMapBlockUserVisible: %x", res);
104 if (pid == SHARED_PID) {
109 memset(&opt, 0,
sizeof(opt));
110 opt.size =
sizeof(opt);
111 opt.attr = 0x1000040;
112 opt.mirror_blkid = *exe_res;
113 res = sceKernelAllocMemBlockForKernel(
"taimirror", SCE_KERNEL_MEMBLOCK_TYPE_RW_UNK0, 0, &opt);
114 LOG(
"sceKernelAllocMemBlockForKernel(taimirror): 0x%08X", res);
119 res = sceKernelGetMemBlockBaseForKernel(blkid, ptr);
120 LOG(
"sceKernelGetMemBlockBaseForKernel(%x): 0x%08X, addr: 0x%08X", blkid, res, *ptr);
128 sceKernelFreeMemBlockForKernel(blkid);
130 sceKernelFreeMemBlockForKernel(*exe_res);
142 static int sce_exe_free(SceUID write_res, SceUID exe_res) {
143 LOG(
"freeing slab %x, mirror %x", exe_res, write_res);
144 sceKernelFreeMemBlockForKernel(write_res);
145 sceKernelFreeMemBlockForKernel(exe_res);
156 static inline uint32_t next_pow_2(uint32_t v) {
168 void slab_init(
struct slab_chain *
const sch,
const size_t itemsize, SceUID pid)
171 assert(itemsize >= 1 && itemsize <= SIZE_MAX);
172 assert(POWEROF2(slab_pagesize));
174 sch->itemsize = itemsize;
177 const size_t data_offset = offsetof(
struct slab_header, data);
178 const size_t least_slabsize = data_offset + 64 * sch->itemsize;
179 sch->slabsize = (size_t) next_pow_2(least_slabsize);
182 if (sch->slabsize - least_slabsize != 0) {
183 const size_t shrinked_slabsize = sch->slabsize >> 1;
185 if (data_offset < shrinked_slabsize &&
186 shrinked_slabsize - data_offset >= 2 * sch->itemsize) {
188 sch->slabsize = shrinked_slabsize;
189 sch->itemcount = (shrinked_slabsize - data_offset) / sch->itemsize;
193 sch->pages_per_alloc = sch->slabsize > slab_pagesize ?
194 sch->slabsize : slab_pagesize;
196 sch->empty_slotmask = ~SLOTS_ALL_ZERO >> (64 - sch->itemcount);
197 sch->initial_slotmask = sch->empty_slotmask ^ SLOTS_FIRST;
198 sch->alignment_mask = ~(sch->slabsize - 1);
199 sch->partial = sch->empty = sch->full = NULL;
201 assert(slab_is_valid(sch));
204 void *slab_alloc(
struct slab_chain *
const sch, uintptr_t *exe_addr)
207 assert(slab_is_valid(sch));
209 if (LIKELY(sch->partial != NULL)) {
211 register const size_t slot = FIRST_FREE_SLOT(sch->partial->slots);
212 sch->partial->slots ^= SLOTS_FIRST << slot;
214 if (UNLIKELY(sch->partial->slots == SLOTS_ALL_ZERO)) {
219 if (LIKELY((sch->partial = sch->partial->next) != NULL))
220 sch->partial->prev = NULL;
222 if (LIKELY((tmp->next = sch->full) != NULL))
223 sch->full->prev = tmp;
226 *exe_addr = sch->full->exe_data + slot * sch->itemsize;
227 return sch->full->data + slot * sch->itemsize;
229 *exe_addr = sch->partial->exe_data + slot * sch->itemsize;
230 return sch->partial->data + slot * sch->itemsize;
232 }
else if (LIKELY((sch->partial = sch->empty) != NULL)) {
234 if (LIKELY((sch->empty = sch->empty->next) != NULL))
235 sch->empty->prev = NULL;
237 sch->partial->next = NULL;
240 UNLIKELY(sch->partial->refcount != 0) ?
241 sch->partial->refcount++ : sch->partial->page->refcount++;
243 sch->partial->slots = sch->initial_slotmask;
244 *exe_addr = sch->partial->exe_data;
245 return sch->partial->data;
248 SceUID write_res, exe_res;
250 if ((write_res = sce_exe_alloc(sch->pid, (
void **)&sch->partial, &exe_data,
251 &exe_res, sch->slabsize, sch->pages_per_alloc)) < 0) {
253 return sch->partial = NULL;
255 sch->partial->write_res = write_res;
256 sch->partial->exe_res = exe_res;
257 sch->partial->exe_data = exe_data + offsetof(
struct slab_header, data);
258 exe_data += sch->slabsize;
262 const char *
const page_end =
263 (
char *) sch->partial + sch->pages_per_alloc;
269 .c = (
const char *) sch->partial + sch->slabsize
272 __builtin_prefetch(sch->partial, 1);
274 sch->partial->prev = sch->partial->next = NULL;
275 sch->partial->refcount = 1;
276 sch->partial->slots = sch->initial_slotmask;
278 if (LIKELY(curr.c != page_end)) {
280 curr.s->refcount = 0;
281 curr.s->page = sch->partial;
282 curr.s->write_res = write_res;
283 curr.s->exe_res = exe_res;
284 curr.s->exe_data = exe_data;
285 exe_data += sch->slabsize;
286 curr.s->slots = sch->empty_slotmask;
287 sch->empty = prev = curr.s;
289 while (LIKELY((curr.c += sch->slabsize) != page_end)) {
292 curr.s->refcount = 0;
293 curr.s->page = sch->partial;
294 curr.s->write_res = write_res;
295 curr.s->exe_res = exe_res;
296 curr.s->exe_data = exe_data;
297 exe_data += sch->slabsize;
298 curr.s->slots = sch->empty_slotmask;
305 *exe_addr = sch->partial->exe_data;
306 return sch->partial->data;
312 void slab_free(
struct slab_chain *
const sch,
const void *
const addr)
315 assert(slab_is_valid(sch));
316 assert(addr != NULL);
319 ((uintptr_t) addr & sch->alignment_mask);
321 register const int slot = ((
char *) addr - (
char *) slab -
322 offsetof(
struct slab_header, data)) / sch->itemsize;
324 if (UNLIKELY(slab->slots == SLOTS_ALL_ZERO)) {
326 slab->slots = SLOTS_FIRST << slot;
328 if (LIKELY(slab != sch->full)) {
329 if (LIKELY((slab->prev->next = slab->next) != NULL))
330 slab->next->prev = slab->prev;
333 }
else if (LIKELY((sch->full = sch->full->next) != NULL)) {
334 sch->full->prev = NULL;
337 slab->next = sch->partial;
339 if (LIKELY(sch->partial != NULL))
340 sch->partial->prev = slab;
343 }
else if (UNLIKELY(ONE_USED_SLOT(slab->slots, sch->empty_slotmask))) {
345 if (UNLIKELY(slab->refcount == 1 || (slab->refcount == 0 &&
346 slab->page->refcount == 1))) {
349 if (LIKELY(slab != sch->partial)) {
350 if (LIKELY((slab->prev->next = slab->next) != NULL))
351 slab->next->prev = slab->prev;
352 }
else if (LIKELY((sch->partial = sch->partial->next) != NULL)) {
353 sch->partial->prev = NULL;
356 void *
const page = UNLIKELY(slab->refcount != 0) ? slab : slab->page;
357 const char *
const page_end = (
char *) page + sch->pages_per_alloc;
365 for (s.c = page; s.c != page_end; s.c += sch->slabsize) {
366 if (UNLIKELY(s.s == sch->empty))
368 else if (UNLIKELY(s.s == slab))
370 else if (LIKELY((s.s->prev->next = s.s->next) != NULL))
371 s.s->next->prev = s.s->prev;
374 if (UNLIKELY(found_head && (sch->empty = sch->empty->next) != NULL))
375 sch->empty->prev = NULL;
377 sce_exe_free(slab->write_res, slab->exe_res);
379 slab->slots = sch->empty_slotmask;
381 if (LIKELY(slab != sch->partial)) {
382 if (LIKELY((slab->prev->next = slab->next) != NULL))
383 slab->next->prev = slab->prev;
386 }
else if (LIKELY((sch->partial = sch->partial->next) != NULL)) {
387 sch->partial->prev = NULL;
390 slab->next = sch->empty;
392 if (LIKELY(sch->empty != NULL))
393 sch->empty->prev = slab;
397 UNLIKELY(slab->refcount != 0) ?
398 slab->refcount-- : slab->page->refcount--;
402 slab->slots |= SLOTS_FIRST << slot;
406 uintptr_t slab_getmirror(
struct slab_chain *
const sch,
const void *
const addr)
409 assert(slab_is_valid(sch));
410 assert(addr != NULL);
413 ((uintptr_t) addr & sch->alignment_mask);
416 return slab->exe_data - offsetof(
struct slab_header, data) + (ptrdiff_t)((
char *) addr - (
char *) slab);
419 void slab_traverse(
const struct slab_chain *
const sch,
void (*fn)(
const void *))
423 assert(slab_is_valid(sch));
426 const char *item, *end;
427 const size_t data_offset = offsetof(
struct slab_header, data);
429 for (slab = sch->partial; slab; slab = slab->next) {
430 item = (
const char *) slab + data_offset;
431 end = item + sch->itemcount * sch->itemsize;
432 uint64_t mask = SLOTS_FIRST;
435 if (!(slab->slots & mask))
439 }
while ((item += sch->itemsize) != end);
442 for (slab = sch->full; slab; slab = slab->next) {
443 item = (
const char *) slab + data_offset;
444 end = item + sch->itemcount * sch->itemsize;
447 while ((item += sch->itemsize) != end);
451 void slab_destroy(
const struct slab_chain *
const sch)
454 assert(slab_is_valid(sch));
456 struct slab_header *
const heads[] = {sch->partial, sch->empty, sch->full};
457 struct slab_header *pages_head = NULL, *pages_tail;
459 for (
size_t i = 0; i < 3; ++i) {
462 while (slab != NULL) {
463 if (slab->refcount != 0) {
467 if (UNLIKELY(pages_head == NULL))
470 pages_tail->next = page;
479 if (LIKELY(pages_head != NULL)) {
480 pages_tail->next = NULL;
486 sce_exe_free(target->write_res, target->exe_res);
487 }
while (page != NULL);