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