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