lightrec: handle a case of ignored MAP_FIXED_NOREPLACE
[pcsx_rearmed.git] / libpcsxcore / lightrec / mem.c
CommitLineData
a093e81f
PC
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * Copyright (C) 2022 Paul Cercueil <paul@crapouillou.net>
4 */
5
3bc20280 6#ifndef _GNU_SOURCE
e5794429 7#define _GNU_SOURCE
3bc20280 8#endif
a093e81f
PC
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>
e5794429 17#include <sys/syscall.h>
a093e81f
PC
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
2f609094
PC
32#ifndef MFD_HUGETLB
33#define MFD_HUGETLB 0x0004
34#endif
35
a093e81f
PC
36static const uintptr_t supported_io_bases[] = {
37 0x0,
38 0x10000000,
39 0x40000000,
40 0x80000000,
41};
42
43static 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);
b992f6b7 58 if (map != MAP_FAILED) {
a093e81f 59 printf("Regular mmap to address 0x%lx succeeded\n", (uintptr_t) addr);
b992f6b7 60#ifdef MADV_HUGEPAGE
61 madvise(map, length, MADV_HUGEPAGE);
62#endif
63 }
a093e81f
PC
64 }
65
66 return map;
67}
68
69static 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
e5794429 79 memfd = syscall(SYS_memfd_create, "/lightrec_memfd",
80 flags);
a093e81f
PC
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++) {
5937ccce 98 void *base_ptr = (void *)(base + j * 0x200000);
99 map = mmap_huge(base_ptr, 0x200000, PROT_READ | PROT_WRITE,
8de0dd9f 100 MAP_SHARED | MAP_FIXED_NOREPLACE, memfd, 0);
a093e81f
PC
101 if (map == MAP_FAILED)
102 break;
5937ccce 103 // some systems ignore MAP_FIXED_NOREPLACE
104 if (map != base_ptr) {
105 munmap(map, 0x200000);
106 break;
107 }
a093e81f
PC
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
131err_close_memfd:
132 close(memfd);
133 return err;
134}
135
136int lightrec_init_mmap(void)
137{
138 unsigned int i;
139 uintptr_t base;
140 void *map;
e5794429 141 int err = lightrec_mmap_ram(true);
a093e81f
PC
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,
8de0dd9f 154 MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
a093e81f
PC
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,
8de0dd9f 165 MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, -1, 0);
a093e81f
PC
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
f8548105
PC
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,
8de0dd9f 188 -1, 0);
f8548105
PC
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
a093e81f
PC
197 return 0;
198
f8548105
PC
199err_unmap_scratch:
200 munmap(psxH, 0x10000);
a093e81f 201err_unmap_bios:
14a530af 202 munmap(psxR, 0x200000);
a093e81f
PC
203err_unmap_parallel:
204 munmap(psxP, 0x10000);
205err_unmap:
206 for (i = 0; i < 4; i++)
207 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
208 return err;
209}
210
211void lightrec_free_mmap(void)
212{
213 unsigned int i;
214
f8548105 215 munmap(code_buffer, CODE_BUFFER_SIZE);
a093e81f 216 munmap(psxH, 0x10000);
14a530af 217 munmap(psxR, 0x200000);
a093e81f
PC
218 munmap(psxP, 0x10000);
219 for (i = 0; i < 4; i++)
220 munmap((void *)((uintptr_t)psxM + i * 0x200000), 0x200000);
221}