psxcounters: try to support a dynarec with a very long timeslice
[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 static const uintptr_t supported_io_bases[] = {
35         0x0,
36         0x10000000,
37         0x40000000,
38         0x80000000,
39 };
40
41 static void * mmap_huge(void *addr, size_t length, int prot, int flags,
42                         int fd, off_t offset)
43 {
44         void *map = MAP_FAILED;
45
46         if (length >= 0x200000) {
47                 map = mmap(addr, length, prot,
48                            flags | MAP_HUGETLB | (21 << MAP_HUGE_SHIFT),
49                            fd, offset);
50                 if (map != MAP_FAILED)
51                         printf("Hugetlb mmap to address 0x%lx succeeded\n", (uintptr_t) addr);
52         }
53
54         if (map == MAP_FAILED) {
55                 map = mmap(addr, length, prot, flags, fd, offset);
56                 if (map != MAP_FAILED)
57                         printf("Regular mmap to address 0x%lx succeeded\n", (uintptr_t) addr);
58         }
59
60         return map;
61 }
62
63 static int lightrec_mmap_ram(bool hugetlb)
64 {
65         unsigned int i, j;
66         int err, memfd, flags = 0;
67         uintptr_t base;
68         void *map;
69
70         if (hugetlb)
71                 flags |= MFD_HUGETLB;
72
73         memfd = syscall(SYS_memfd_create, "/lightrec_memfd",
74                         flags);
75         if (memfd < 0) {
76                 err = -errno;
77                 fprintf(stderr, "Failed to create memfd: %d\n", err);
78                 return err;
79         }
80
81         err = ftruncate(memfd, 0x200000);
82         if (err < 0) {
83                 err = -errno;
84                 fprintf(stderr, "Could not trim memfd: %d\n", err);
85                 goto err_close_memfd;
86         }
87
88         for (i = 0; i < ARRAY_SIZE(supported_io_bases); i++) {
89                 base = supported_io_bases[i];
90
91                 for (j = 0; j < 4; j++) {
92                         map = mmap_huge((void *)(base + j * 0x200000),
93                                         0x200000, PROT_READ | PROT_WRITE,
94                                         MAP_SHARED | MAP_FIXED, memfd, 0);
95                         if (map == MAP_FAILED)
96                                 break;
97                 }
98
99                 /* Impossible to map using this base */
100                 if (j == 0)
101                         continue;
102
103                 /* All mirrors mapped - we got a match! */
104                 if (j == 4)
105                         break;
106
107                 /* Only some mirrors mapped - clean the mess and try again */
108                 for (; j > 0; j--)
109                         munmap((void *)(base + (j - 1) * 0x200000), 0x200000);
110         }
111
112         if (i == ARRAY_SIZE(supported_io_bases)) {
113                 err = -EINVAL;
114                 goto err_close_memfd;
115         }
116
117         err = 0;
118         psxM = (s8 *)base;
119
120 err_close_memfd:
121         close(memfd);
122         return err;
123 }
124
125 int lightrec_init_mmap(void)
126 {
127         unsigned int i;
128         uintptr_t base;
129         void *map;
130         int err = lightrec_mmap_ram(true);
131         if (err) {
132                 err = lightrec_mmap_ram(false);
133                 if (err) {
134                         fprintf(stderr, "Unable to mmap RAM and mirrors\n");
135                         return err;
136                 }
137         }
138
139         base = (uintptr_t) psxM;
140
141         map = mmap((void *)(base + 0x1f000000), 0x10000,
142                    PROT_READ | PROT_WRITE,
143                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
144         if (map == MAP_FAILED) {
145                 err = -EINVAL;
146                 fprintf(stderr, "Unable to mmap parallel port\n");
147                 goto err_unmap;
148         }
149
150         psxP = (s8 *)map;
151
152         map = mmap_huge((void *)(base + 0x1fc00000), 0x200000,
153                         PROT_READ | PROT_WRITE,
154                         MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
155         if (map == MAP_FAILED) {
156                 err = -EINVAL;
157                 fprintf(stderr, "Unable to mmap BIOS\n");
158                 goto err_unmap_parallel;
159         }
160
161         psxR = (s8 *)map;
162
163         map = mmap((void *)(base + 0x1f800000), 0x10000,
164                    PROT_READ | PROT_WRITE,
165                    MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, 0, 0);
166         if (map == MAP_FAILED) {
167                 err = -EINVAL;
168                 fprintf(stderr, "Unable to mmap scratchpad\n");
169                 goto err_unmap_bios;
170         }
171
172         psxH = (s8 *)map;
173
174         return 0;
175
176 err_unmap_bios:
177         munmap(psxR, 0x80000);
178 err_unmap_parallel:
179         munmap(psxP, 0x10000);
180 err_unmap:
181         for (i = 0; i < 4; i++)
182                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
183         return err;
184 }
185
186 void lightrec_free_mmap(void)
187 {
188         unsigned int i;
189
190         munmap(psxH, 0x10000);
191         munmap(psxR, 0x80000);
192         munmap(psxP, 0x10000);
193         for (i = 0; i < 4; i++)
194                 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
195 }