lightrec: Enable code buffer support
[pcsx_rearmed.git] / libpcsxcore / lightrec / mem.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3  * Copyright (C) 2022 Paul Cercueil <paul@crapouillou.net>
4  */
5
6 #define _GNU_SOURCE
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/mman.h>
13 #include <sys/shm.h>
14 #include <sys/stat.h>
15 #include <sys/syscall.h>
16 #include <unistd.h>
17
18 #include "../psxhw.h"
19 #include "../psxmem.h"
20 #include "../r3000a.h"
21
22 #include "mem.h"
23
24 #define ARRAY_SIZE(a) (sizeof(a) ? (sizeof(a) / sizeof((a)[0])) : 0)
25
26 #ifndef MAP_FIXED_NOREPLACE
27 #define MAP_FIXED_NOREPLACE 0x100000
28 #endif
29
30 #ifndef MFD_HUGETLB
31 #define MFD_HUGETLB 0x0004
32 #endif
33
34 void *code_buffer;
35
36 static const uintptr_t supported_io_bases[] = {
37         0x0,
38         0x10000000,
39         0x40000000,
40         0x80000000,
41 };
42
43 static void * mmap_huge(void *addr, size_t length, int prot, int flags,
44                         int fd, off_t offset)
45 {
46         void *map = MAP_FAILED;
47
48         if (length >= 0x200000) {
49                 map = mmap(addr, length, prot,
50                            flags | MAP_HUGETLB | (21 << MAP_HUGE_SHIFT),
51                            fd, offset);
52                 if (map != MAP_FAILED)
53                         printf("Hugetlb mmap to address 0x%lx succeeded\n", (uintptr_t) addr);
54         }
55
56         if (map == MAP_FAILED) {
57                 map = mmap(addr, length, prot, flags, fd, offset);
58                 if (map != MAP_FAILED)
59                         printf("Regular mmap to address 0x%lx succeeded\n", (uintptr_t) addr);
60         }
61
62         return map;
63 }
64
65 static int lightrec_mmap_ram(bool hugetlb)
66 {
67         unsigned int i, j;
68         int err, memfd, flags = 0;
69         uintptr_t base;
70         void *map;
71
72         if (hugetlb)
73                 flags |= MFD_HUGETLB;
74
75         memfd = syscall(SYS_memfd_create, "/lightrec_memfd",
76                         flags);
77         if (memfd < 0) {
78                 err = -errno;
79                 fprintf(stderr, "Failed to create memfd: %d\n", err);
80                 return err;
81         }
82
83         err = ftruncate(memfd, 0x200000);
84         if (err < 0) {
85                 err = -errno;
86                 fprintf(stderr, "Could not trim memfd: %d\n", err);
87                 goto err_close_memfd;
88         }
89
90         for (i = 0; i < ARRAY_SIZE(supported_io_bases); i++) {
91                 base = supported_io_bases[i];
92
93                 for (j = 0; j < 4; j++) {
94                         map = mmap_huge((void *)(base + j * 0x200000),
95                                         0x200000, PROT_READ | PROT_WRITE,
96                                         MAP_SHARED | MAP_FIXED, memfd, 0);
97                         if (map == MAP_FAILED)
98                                 break;
99                 }
100
101                 /* Impossible to map using this base */
102                 if (j == 0)
103                         continue;
104
105                 /* All mirrors mapped - we got a match! */
106                 if (j == 4)
107                         break;
108
109                 /* Only some mirrors mapped - clean the mess and try again */
110                 for (; j > 0; j--)
111                         munmap((void *)(base + (j - 1) * 0x200000), 0x200000);
112         }
113
114         if (i == ARRAY_SIZE(supported_io_bases)) {
115                 err = -EINVAL;
116                 goto err_close_memfd;
117         }
118
119         err = 0;
120         psxM = (s8 *)base;
121
122 err_close_memfd:
123         close(memfd);
124         return err;
125 }
126
127 int lightrec_init_mmap(void)
128 {
129         unsigned int i;
130         uintptr_t base;
131         void *map;
132         int err = lightrec_mmap_ram(true);
133         if (err) {
134                 err = lightrec_mmap_ram(false);
135                 if (err) {
136                         fprintf(stderr, "Unable to mmap RAM and mirrors\n");
137                         return err;
138                 }
139         }
140
141         base = (uintptr_t) psxM;
142
143         map = mmap((void *)(base + 0x1f000000), 0x10000,
144                    PROT_READ | PROT_WRITE,
145                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
146         if (map == MAP_FAILED) {
147                 err = -EINVAL;
148                 fprintf(stderr, "Unable to mmap parallel port\n");
149                 goto err_unmap;
150         }
151
152         psxP = (s8 *)map;
153
154         map = mmap_huge((void *)(base + 0x1fc00000), 0x200000,
155                         PROT_READ | PROT_WRITE,
156                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
157         if (map == MAP_FAILED) {
158                 err = -EINVAL;
159                 fprintf(stderr, "Unable to mmap BIOS\n");
160                 goto err_unmap_parallel;
161         }
162
163         psxR = (s8 *)map;
164
165         map = mmap((void *)(base + 0x1f800000), 0x10000,
166                    PROT_READ | PROT_WRITE,
167                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
168         if (map == MAP_FAILED) {
169                 err = -EINVAL;
170                 fprintf(stderr, "Unable to mmap scratchpad\n");
171                 goto err_unmap_bios;
172         }
173
174         psxH = (s8 *)map;
175
176         map = mmap_huge((void *)(base + 0x800000), CODE_BUFFER_SIZE,
177                         PROT_EXEC | PROT_READ | PROT_WRITE,
178                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS,
179                         0, 0);
180         if (map == MAP_FAILED) {
181                 err = -EINVAL;
182                 fprintf(stderr, "Unable to mmap code buffer\n");
183                 goto err_unmap_scratch;
184         }
185
186         code_buffer = map;
187
188         return 0;
189
190 err_unmap_scratch:
191         munmap(psxH, 0x10000);
192 err_unmap_bios:
193         munmap(psxR, 0x80000);
194 err_unmap_parallel:
195         munmap(psxP, 0x10000);
196 err_unmap:
197         for (i = 0; i < 4; i++)
198                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
199         return err;
200 }
201
202 void lightrec_free_mmap(void)
203 {
204         unsigned int i;
205
206         munmap(code_buffer, CODE_BUFFER_SIZE);
207         munmap(psxH, 0x10000);
208         munmap(psxR, 0x80000);
209         munmap(psxP, 0x10000);
210         for (i = 0; i < 4; i++)
211                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
212 }