b5caa78366b29705840cefdf1d98781d6a6ca176
[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 <stdbool.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/mman.h>
15 #include <sys/shm.h>
16 #include <sys/stat.h>
17 #include <sys/syscall.h>
18 #include <unistd.h>
19
20 #include "../psxhw.h"
21 #include "../psxmem.h"
22 #include "../r3000a.h"
23
24 #include "mem.h"
25
26 #define ARRAY_SIZE(a) (sizeof(a) ? (sizeof(a) / sizeof((a)[0])) : 0)
27
28 #ifndef MAP_FIXED_NOREPLACE
29 #define MAP_FIXED_NOREPLACE 0x100000
30 #endif
31
32 #ifndef MFD_HUGETLB
33 #define MFD_HUGETLB 0x0004
34 #endif
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 #ifdef MADV_HUGEPAGE
61                         madvise(map, length, MADV_HUGEPAGE);
62 #endif
63                 }
64         }
65
66         return map;
67 }
68
69 static int lightrec_mmap_ram(bool hugetlb)
70 {
71         unsigned int i, j;
72         int err, memfd, flags = 0;
73         uintptr_t base;
74         void *map;
75
76         if (hugetlb)
77                 flags |= MFD_HUGETLB;
78
79         memfd = syscall(SYS_memfd_create, "/lightrec_memfd",
80                         flags);
81         if (memfd < 0) {
82                 err = -errno;
83                 fprintf(stderr, "Failed to create memfd: %d\n", err);
84                 return err;
85         }
86
87         err = ftruncate(memfd, 0x200000);
88         if (err < 0) {
89                 err = -errno;
90                 fprintf(stderr, "Could not trim memfd: %d\n", err);
91                 goto err_close_memfd;
92         }
93
94         for (i = 0; i < ARRAY_SIZE(supported_io_bases); i++) {
95                 base = supported_io_bases[i];
96
97                 for (j = 0; j < 4; j++) {
98                         map = mmap_huge((void *)(base + j * 0x200000),
99                                         0x200000, PROT_READ | PROT_WRITE,
100                                         MAP_SHARED | MAP_FIXED_NOREPLACE, memfd, 0);
101                         if (map == MAP_FAILED)
102                                 break;
103                 }
104
105                 /* Impossible to map using this base */
106                 if (j == 0)
107                         continue;
108
109                 /* All mirrors mapped - we got a match! */
110                 if (j == 4)
111                         break;
112
113                 /* Only some mirrors mapped - clean the mess and try again */
114                 for (; j > 0; j--)
115                         munmap((void *)(base + (j - 1) * 0x200000), 0x200000);
116         }
117
118         if (i == ARRAY_SIZE(supported_io_bases)) {
119                 err = -EINVAL;
120                 goto err_close_memfd;
121         }
122
123         err = 0;
124         psxM = (s8 *)base;
125
126 err_close_memfd:
127         close(memfd);
128         return err;
129 }
130
131 int lightrec_init_mmap(void)
132 {
133         unsigned int i;
134         uintptr_t base;
135         void *map;
136         int err = lightrec_mmap_ram(true);
137         if (err) {
138                 err = lightrec_mmap_ram(false);
139                 if (err) {
140                         fprintf(stderr, "Unable to mmap RAM and mirrors\n");
141                         return err;
142                 }
143         }
144
145         base = (uintptr_t) psxM;
146
147         map = mmap((void *)(base + 0x1f000000), 0x10000,
148                    PROT_READ | PROT_WRITE,
149                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
150         if (map == MAP_FAILED) {
151                 err = -EINVAL;
152                 fprintf(stderr, "Unable to mmap parallel port\n");
153                 goto err_unmap;
154         }
155
156         psxP = (s8 *)map;
157
158         map = mmap_huge((void *)(base + 0x1fc00000), 0x200000,
159                         PROT_READ | PROT_WRITE,
160                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
161         if (map == MAP_FAILED) {
162                 err = -EINVAL;
163                 fprintf(stderr, "Unable to mmap BIOS\n");
164                 goto err_unmap_parallel;
165         }
166
167         psxR = (s8 *)map;
168
169         map = mmap((void *)(base + 0x1f800000), 0x10000,
170                    PROT_READ | PROT_WRITE,
171                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
172         if (map == MAP_FAILED) {
173                 err = -EINVAL;
174                 fprintf(stderr, "Unable to mmap scratchpad\n");
175                 goto err_unmap_bios;
176         }
177
178         psxH = (s8 *)map;
179
180         map = mmap_huge((void *)(base + 0x800000), CODE_BUFFER_SIZE,
181                         PROT_EXEC | PROT_READ | PROT_WRITE,
182                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS,
183                         -1, 0);
184         if (map == MAP_FAILED) {
185                 err = -EINVAL;
186                 fprintf(stderr, "Unable to mmap code buffer\n");
187                 goto err_unmap_scratch;
188         }
189
190         code_buffer = map;
191
192         return 0;
193
194 err_unmap_scratch:
195         munmap(psxH, 0x10000);
196 err_unmap_bios:
197         munmap(psxR, 0x200000);
198 err_unmap_parallel:
199         munmap(psxP, 0x10000);
200 err_unmap:
201         for (i = 0; i < 4; i++)
202                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
203         return err;
204 }
205
206 void lightrec_free_mmap(void)
207 {
208         unsigned int i;
209
210         munmap(code_buffer, CODE_BUFFER_SIZE);
211         munmap(psxH, 0x10000);
212         munmap(psxR, 0x200000);
213         munmap(psxP, 0x10000);
214         for (i = 0; i < 4; i++)
215                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
216 }