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