1 /*
2 * Copyright (C) 2004-2021 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * COMMENT: A generic RAM (memory) device
29 *
30 * Note: This device can also be used to mirror/alias another part of RAM.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/mman.h>
38
39 #include "cpu.h"
40 #include "devices.h"
41 #include "machine.h"
42 #include "memory.h"
43 #include "misc.h"
44
45
46 struct ram_data {
47 uint64_t baseaddress;
48 const char* name;
49
50 int trace;
51 int mode;
52 uint64_t otheraddress;
53
54 /* If mode = DEV_RAM_MIRROR: */
55 uint64_t offset;
56
57 /* If mode = DEV_RAM_RAM: */
58 unsigned char *data;
59 uint64_t length;
60 };
61
62
DEVICE_ACCESS(ram)63 DEVICE_ACCESS(ram)
64 {
65 struct ram_data *d = (struct ram_data *) extra;
66
67 if (cpu->running && d->trace && writeflag == MEM_WRITE) {
68 fatal("[ %s: write %i byte%s from 0x%x:",
69 d->name, (int)len, len==1? "":"s", (int)relative_addr);
70 for (size_t i=0; i<len; i++)
71 fatal(" %02x", data[i]);
72 fatal(" ]\n");
73 }
74
75 switch (d->mode) {
76
77 case DEV_RAM_MIRROR:
78 /* TODO: how about caches? */
79 {
80 int result = cpu->memory_rw(cpu, mem,
81 d->otheraddress + relative_addr, data, len,
82 writeflag, PHYSICAL);
83
84 /* Invalidate any code translations in both the MIRRORED
85 and the MIRROR address ranges on a write: */
86 if (writeflag == MEM_WRITE &&
87 cpu->invalidate_code_translation != NULL) {
88 cpu->invalidate_code_translation(
89 cpu, d->otheraddress + relative_addr,
90 INVALIDATE_PADDR);
91 cpu->invalidate_code_translation(
92 cpu, d->baseaddress + relative_addr,
93 INVALIDATE_PADDR);
94 }
95
96 if (cpu->running && d->trace && writeflag == MEM_READ) {
97 fatal("[ %s: read %i byte%s from 0x%x:",
98 d->name, (int)len, len==1? "":"s", (int)relative_addr);
99 for (size_t i=0; i<len; i++)
100 fatal(" %02x", data[i]);
101 fatal(" ]\n");
102 }
103
104 return result;
105 }
106
107 case DEV_RAM_RAM:
108 if (writeflag == MEM_WRITE) {
109 memcpy(&d->data[relative_addr], data, len);
110
111 /* Invalidate any code translations on a write: */
112 if (cpu->invalidate_code_translation != NULL) {
113 cpu->invalidate_code_translation(
114 cpu, d->baseaddress + relative_addr,
115 INVALIDATE_PADDR);
116 }
117 } else {
118 memcpy(data, &d->data[relative_addr], len);
119
120 if (cpu->running && d->trace) {
121 fatal("[ %s: read %i byte%s from 0x%x:",
122 d->name, (int)len, len==1? "":"s", (int)relative_addr);
123 for (size_t i=0; i<len; i++)
124 fatal(" %02x", data[i]);
125 fatal(" ]\n");
126 }
127 }
128 break;
129
130 default:
131 fatal("dev_ram_access(): unknown mode %i\n", d->mode);
132 exit(1);
133 }
134
135 return 1;
136 }
137
138
139 /*
140 * dev_ram_init():
141 *
142 * Initializes a RAM or mirror device. Things get a bit complicated because
143 * of dyntrans (i.e. mirrored memory ranges should be entered into the
144 * translation arrays just as normal memory and other devices are).
145 */
dev_ram_init(struct machine * machine,uint64_t baseaddr,uint64_t length,int mode,uint64_t otheraddress,const char * name)146 void dev_ram_init(struct machine *machine, uint64_t baseaddr, uint64_t length,
147 int mode, uint64_t otheraddress, const char* name)
148 {
149 struct ram_data *d;
150 int flags = DM_DEFAULT, points_to_ram = 1;
151
152 CHECK_ALLOCATION(d = (struct ram_data *) malloc(sizeof(struct ram_data)));
153 memset(d, 0, sizeof(struct ram_data));
154
155 d->name = strdup((name == NULL) ? "ram" : name);
156
157 if (mode & DEV_RAM_MIGHT_POINT_TO_DEVICES) {
158 mode &= ~DEV_RAM_MIGHT_POINT_TO_DEVICES;
159 points_to_ram = 0;
160 }
161
162 if (mode & DEV_RAM_TRACE_ALL_ACCESSES) {
163 mode &= ~DEV_RAM_TRACE_ALL_ACCESSES;
164 d->trace = 1;
165 }
166
167 d->mode = mode;
168 d->baseaddress = baseaddr;
169 d->otheraddress = otheraddress;
170
171 switch (d->mode) {
172
173 case DEV_RAM_MIRROR:
174 /*
175 * Calculate the amount that the mirror memory is offset from
176 * the real (physical) memory. This is used in src/memory_rw.c
177 * with dyntrans accesses if DM_EMULATED_RAM is set.
178 */
179 d->offset = baseaddr - otheraddress;
180
181 {
182 size_t mirrorNameAllocLen = strlen(d->name) + 15;
183 char *mirrorName = (char*) malloc(mirrorNameAllocLen);
184 snprintf(mirrorName, mirrorNameAllocLen, "%s [mirror]", d->name);
185 d->name = mirrorName;
186 }
187
188 /* Aligned RAM? Then it works with dyntrans. */
189 if (points_to_ram &&
190 (baseaddr & (machine->arch_pagesize-1)) == 0 &&
191 (otheraddress & (machine->arch_pagesize - 1)) == 0 &&
192 (length & (machine->arch_pagesize - 1)) == 0 &&
193 !d->trace)
194 flags |= DM_DYNTRANS_OK | DM_DYNTRANS_WRITE_OK
195 | DM_EMULATED_RAM;
196
197 memory_device_register(machine->memory, d->name,
198 baseaddr, length, dev_ram_access, d, flags
199 | DM_READS_HAVE_NO_SIDE_EFFECTS, (unsigned char*) (void *) &d->offset);
200 break;
201
202 case DEV_RAM_RAM:
203 /*
204 * Allocate zero-filled RAM using mmap(). If mmap() failed,
205 * try malloc(), but then memset() must also be called, which
206 * can be slow for large chunks of memory.
207 */
208 d->length = length;
209 d->data = (unsigned char *) mmap(NULL, length,
210 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
211 if (d->data == NULL) {
212 CHECK_ALLOCATION(d->data = (unsigned char *) malloc(length));
213 memset(d->data, 0, length);
214 }
215
216 /* Aligned memory? Then it works with dyntrans. */
217 if ((baseaddr & (machine->arch_pagesize - 1)) == 0 &&
218 (length & (machine->arch_pagesize - 1)) == 0 &&
219 !d->trace)
220 flags |= DM_DYNTRANS_OK | DM_DYNTRANS_WRITE_OK;
221
222 memory_device_register(machine->memory, d->name, baseaddr,
223 d->length, dev_ram_access, d, flags
224 | DM_READS_HAVE_NO_SIDE_EFFECTS, d->data);
225 break;
226
227 default:
228 fatal("dev_ram_access(): %s: unknown mode %i\n", d->name, d->mode);
229 exit(1);
230 }
231 }
232
233