lightrec: implement clock cache clear on cycle_multiplier change
[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                         void *base_ptr = (void *)(base + j * 0x200000);
99                         map = mmap_huge(base_ptr, 0x200000, PROT_READ | PROT_WRITE,
100                                         MAP_SHARED | MAP_FIXED_NOREPLACE, memfd, 0);
101                         if (map == MAP_FAILED)
102                                 break;
103                         // some systems ignore MAP_FIXED_NOREPLACE
104                         if (map != base_ptr) {
105                                 munmap(map, 0x200000);
106                                 break;
107                         }
108                 }
109
110                 /* Impossible to map using this base */
111                 if (j == 0)
112                         continue;
113
114                 /* All mirrors mapped - we got a match! */
115                 if (j == 4)
116                         break;
117
118                 /* Only some mirrors mapped - clean the mess and try again */
119                 for (; j > 0; j--)
120                         munmap((void *)(base + (j - 1) * 0x200000), 0x200000);
121         }
122
123         if (i == ARRAY_SIZE(supported_io_bases)) {
124                 err = -EINVAL;
125                 goto err_close_memfd;
126         }
127
128         err = 0;
129         psxM = (s8 *)base;
130
131 err_close_memfd:
132         close(memfd);
133         return err;
134 }
135
136 int lightrec_init_mmap(void)
137 {
138         unsigned int i;
139         uintptr_t base;
140         void *map;
141         int err = lightrec_mmap_ram(true);
142         if (err) {
143                 err = lightrec_mmap_ram(false);
144                 if (err) {
145                         fprintf(stderr, "Unable to mmap RAM and mirrors\n");
146                         return err;
147                 }
148         }
149
150         base = (uintptr_t) psxM;
151
152         map = mmap((void *)(base + 0x1f000000), 0x10000,
153                    PROT_READ | PROT_WRITE,
154                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
155         if (map == MAP_FAILED) {
156                 err = -EINVAL;
157                 fprintf(stderr, "Unable to mmap parallel port\n");
158                 goto err_unmap;
159         }
160
161         psxP = (s8 *)map;
162
163         map = mmap_huge((void *)(base + 0x1fc00000), 0x200000,
164                         PROT_READ | PROT_WRITE,
165                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
166         if (map == MAP_FAILED) {
167                 err = -EINVAL;
168                 fprintf(stderr, "Unable to mmap BIOS\n");
169                 goto err_unmap_parallel;
170         }
171
172         psxR = (s8 *)map;
173
174         map = mmap((void *)(base + 0x1f800000), 0x10000,
175                    PROT_READ | PROT_WRITE,
176                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
177         if (map == MAP_FAILED) {
178                 err = -EINVAL;
179                 fprintf(stderr, "Unable to mmap scratchpad\n");
180                 goto err_unmap_bios;
181         }
182
183         psxH = (s8 *)map;
184
185         map = mmap_huge((void *)(base + 0x800000), CODE_BUFFER_SIZE,
186                         PROT_EXEC | PROT_READ | PROT_WRITE,
187                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS,
188                         -1, 0);
189         if (map == MAP_FAILED) {
190                 err = -EINVAL;
191                 fprintf(stderr, "Unable to mmap code buffer\n");
192                 goto err_unmap_scratch;
193         }
194
195         code_buffer = map;
196
197         return 0;
198
199 err_unmap_scratch:
200         munmap(psxH, 0x10000);
201 err_unmap_bios:
202         munmap(psxR, 0x200000);
203 err_unmap_parallel:
204         munmap(psxP, 0x10000);
205 err_unmap:
206         for (i = 0; i < 4; i++)
207                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
208         return err;
209 }
210
211 void lightrec_free_mmap(void)
212 {
213         unsigned int i;
214
215         munmap(code_buffer, CODE_BUFFER_SIZE);
216         munmap(psxH, 0x10000);
217         munmap(psxR, 0x200000);
218         munmap(psxP, 0x10000);
219         for (i = 0; i < 4; i++)
220                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
221 }