/[gxemul]/trunk/src/memory.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/memory.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (show annotations)
Mon Oct 8 16:19:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14945 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1004 2005/10/27 14:01:10 debug Exp $
20051011        Passing -A as the default boot arg for CATS (works fine with
                OpenBSD/cats).
20051012	Fixing the VGA cursor offset bug, and speeding up framebuffer
		redraws if character cells contain the same thing as during
		the last redraw.
20051013	Adding a slow strd ARM instruction hack.
20051017	Minor updates: Adding a dummy i80321 Verde controller (for
		XScale emulation), fixing the disassembly of the ARM "ldrd"
		instruction, adding "support" for less-than-4KB pages for ARM
		(by not adding them to translation tables).
20051020	Continuing on some HPCarm stuff. A NetBSD/hpcarm kernel prints
		some boot messages on an emulated Jornada 720.
		Making dev_ram work better with dyntrans (speeds up some things
		quite a bit).
20051021	Automatically generating some of the most common ARM load/store
		multiple instructions.
20051022	Better statistics gathering for the ARM load/store multiple.
		Various other dyntrans and device updates.
20051023	Various minor updates.
20051024	Continuing; minor device and dyntrans fine-tuning. Adding the
		first "reasonable" instruction combination hacks for ARM (the
		cores of NetBSD/cats' memset and memcpy).
20051025	Fixing a dyntrans-related bug in dev_vga. Also changing the
		dyntrans low/high access notification to only be updated on
		writes, not reads. Hopefully it will be enough. (dev_vga in
		charcell mode now seems to work correctly with both reads and
		writes.)
		Experimenting with gathering dyntrans statistics (which parts
		of emulated RAM that are actually executed), and adding
		instruction combination hacks for cache cleaning and a part of
		NetBSD's scanc() function.
20051026	Adding a bitmap for ARM emulation which indicates if a page is
		(specifically) user accessible; loads and stores with the t-
		flag set can now use the translation arrays, which results in
		a measurable speedup.
20051027	Dyntrans updates; adding an extra bitmap array for 32-bit
		emulation modes, speeding up the check whether a physical page
		has any code translations or not (O(n) -> O(1)). Doing a
		similar reduction of O(n) to O(1) by avoiding the scan through
		the translation entries on a translation update (32-bit mode
		only).
		Various other minor hacks.
20051029	Quick release, without any testing at all.

==============  RELEASE 0.3.6.2  ==============


1 /*
2 * Copyright (C) 2003-2005 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 * $Id: memory.c,v 1.180 2005/10/25 15:51:02 debug Exp $
29 *
30 * Functions for handling the memory of an emulated machine.
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 "bintrans.h"
40 #include "cop0.h"
41 #include "cpu.h"
42 #include "machine.h"
43 #include "memory.h"
44 #include "mips_cpu_types.h"
45 #include "misc.h"
46
47
48 extern int quiet_mode;
49 extern volatile int single_step;
50
51
52 /*
53 * memory_readmax64():
54 *
55 * Read at most 64 bits of data from a buffer. Length is given by
56 * len, and the byte order by cpu->byte_order.
57 *
58 * This function should not be called with cpu == NULL.
59 */
60 uint64_t memory_readmax64(struct cpu *cpu, unsigned char *buf, int len)
61 {
62 int i;
63 uint64_t x = 0;
64
65 /* Switch byte order for incoming data, if necessary: */
66 if (cpu->byte_order == EMUL_BIG_ENDIAN)
67 for (i=0; i<len; i++) {
68 x <<= 8;
69 x |= buf[i];
70 }
71 else
72 for (i=len-1; i>=0; i--) {
73 x <<= 8;
74 x |= buf[i];
75 }
76
77 return x;
78 }
79
80
81 /*
82 * memory_writemax64():
83 *
84 * Write at most 64 bits of data to a buffer. Length is given by
85 * len, and the byte order by cpu->byte_order.
86 *
87 * This function should not be called with cpu == NULL.
88 */
89 void memory_writemax64(struct cpu *cpu, unsigned char *buf, int len,
90 uint64_t data)
91 {
92 int i;
93
94 if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
95 for (i=0; i<len; i++) {
96 buf[i] = data & 255;
97 data >>= 8;
98 }
99 else
100 for (i=0; i<len; i++) {
101 buf[len - 1 - i] = data & 255;
102 data >>= 8;
103 }
104 }
105
106
107 /*
108 * zeroed_alloc():
109 *
110 * Allocates a block of memory using mmap(), and if that fails, try
111 * malloc() + memset(). The returned memory block contains only zeroes.
112 */
113 void *zeroed_alloc(size_t s)
114 {
115 void *p = mmap(NULL, s, PROT_READ | PROT_WRITE,
116 MAP_ANON | MAP_PRIVATE, -1, 0);
117 if (p == NULL) {
118 p = malloc(s);
119 if (p == NULL) {
120 fprintf(stderr, "out of memory\n");
121 exit(1);
122 }
123 memset(p, 0, s);
124 }
125 return p;
126 }
127
128
129 /*
130 * memory_new():
131 *
132 * This function creates a new memory object. An emulated machine needs one
133 * of these.
134 */
135 struct memory *memory_new(uint64_t physical_max, int arch)
136 {
137 struct memory *mem;
138 int bits_per_pagetable = BITS_PER_PAGETABLE;
139 int bits_per_memblock = BITS_PER_MEMBLOCK;
140 int entries_per_pagetable = 1 << BITS_PER_PAGETABLE;
141 int max_bits = MAX_BITS;
142 size_t s;
143
144 mem = malloc(sizeof(struct memory));
145 if (mem == NULL) {
146 fprintf(stderr, "out of memory\n");
147 exit(1);
148 }
149
150 memset(mem, 0, sizeof(struct memory));
151
152 /* Check bits_per_pagetable and bits_per_memblock for sanity: */
153 if (bits_per_pagetable + bits_per_memblock != max_bits) {
154 fprintf(stderr, "memory_new(): bits_per_pagetable and "
155 "bits_per_memblock mismatch\n");
156 exit(1);
157 }
158
159 mem->physical_max = physical_max;
160 mem->dev_dyntrans_alignment = 4095;
161 if (arch == ARCH_ALPHA)
162 mem->dev_dyntrans_alignment = 8191;
163
164 s = entries_per_pagetable * sizeof(void *);
165
166 mem->pagetable = (unsigned char *) mmap(NULL, s,
167 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
168 if (mem->pagetable == NULL) {
169 mem->pagetable = malloc(s);
170 if (mem->pagetable == NULL) {
171 fprintf(stderr, "out of memory\n");
172 exit(1);
173 }
174 memset(mem->pagetable, 0, s);
175 }
176
177 mem->mmap_dev_minaddr = 0xffffffffffffffffULL;
178 mem->mmap_dev_maxaddr = 0;
179
180 return mem;
181 }
182
183
184 /*
185 * memory_points_to_string():
186 *
187 * Returns 1 if there's something string-like at addr, otherwise 0.
188 */
189 int memory_points_to_string(struct cpu *cpu, struct memory *mem, uint64_t addr,
190 int min_string_length)
191 {
192 int cur_length = 0;
193 unsigned char c;
194
195 for (;;) {
196 c = '\0';
197 cpu->memory_rw(cpu, mem, addr+cur_length,
198 &c, sizeof(c), MEM_READ, CACHE_NONE | NO_EXCEPTIONS);
199 if (c=='\n' || c=='\t' || c=='\r' || (c>=' ' && c<127)) {
200 cur_length ++;
201 if (cur_length >= min_string_length)
202 return 1;
203 } else {
204 if (cur_length >= min_string_length)
205 return 1;
206 else
207 return 0;
208 }
209 }
210 }
211
212
213 /*
214 * memory_conv_to_string():
215 *
216 * Convert virtual memory contents to a string, placing it in a
217 * buffer provided by the caller.
218 */
219 char *memory_conv_to_string(struct cpu *cpu, struct memory *mem, uint64_t addr,
220 char *buf, int bufsize)
221 {
222 int len = 0;
223 int output_index = 0;
224 unsigned char c, p='\0';
225
226 while (output_index < bufsize-1) {
227 c = '\0';
228 cpu->memory_rw(cpu, mem, addr+len, &c, sizeof(c), MEM_READ,
229 CACHE_NONE | NO_EXCEPTIONS);
230 buf[output_index] = c;
231 if (c>=' ' && c<127) {
232 len ++;
233 output_index ++;
234 } else if (c=='\n' || c=='\r' || c=='\t') {
235 len ++;
236 buf[output_index] = '\\';
237 output_index ++;
238 switch (c) {
239 case '\n': p = 'n'; break;
240 case '\r': p = 'r'; break;
241 case '\t': p = 't'; break;
242 }
243 if (output_index < bufsize-1) {
244 buf[output_index] = p;
245 output_index ++;
246 }
247 } else {
248 buf[output_index] = '\0';
249 return buf;
250 }
251 }
252
253 buf[bufsize-1] = '\0';
254 return buf;
255 }
256
257
258 /*
259 * memory_device_dyntrans_access():
260 *
261 * Get the lowest and highest dyntrans (or bintrans) access since last time.
262 */
263 void memory_device_dyntrans_access(struct cpu *cpu, struct memory *mem,
264 void *extra, uint64_t *low, uint64_t *high)
265 {
266 int i, j;
267 size_t s;
268 int need_inval = 0;
269
270 /* TODO: This is O(n), so it might be good to rewrite it some day.
271 For now, it will be enough, as long as this function is not
272 called too often. */
273
274 for (i=0; i<mem->n_mmapped_devices; i++) {
275 if (mem->dev_extra[i] == extra &&
276 mem->dev_dyntrans_data[i] != NULL) {
277 if (mem->dev_dyntrans_write_low[i] != (uint64_t) -1)
278 need_inval = 1;
279 if (low != NULL)
280 *low = mem->dev_dyntrans_write_low[i];
281 mem->dev_dyntrans_write_low[i] = (uint64_t) -1;
282
283 if (high != NULL)
284 *high = mem->dev_dyntrans_write_high[i];
285 mem->dev_dyntrans_write_high[i] = 0;
286
287 if (!need_inval)
288 return;
289
290 /* Invalidate any pages of this device that might
291 be in the dyntrans load/store cache, by marking
292 the pages read-only. */
293 if (cpu->invalidate_translation_caches != NULL) {
294 for (s=0; s<mem->dev_length[i];
295 s+=cpu->machine->arch_pagesize)
296 cpu->invalidate_translation_caches
297 (cpu, mem->dev_baseaddr[i] + s,
298 JUST_MARK_AS_NON_WRITABLE
299 | INVALIDATE_PADDR);
300 }
301
302 if (cpu->machine->arch == ARCH_MIPS) {
303 /*
304 * ... and invalidate the "fast_vaddr_to_
305 * hostaddr" cache entries that contain
306 * pointers to this device: (NOTE: Device i,
307 * cache entry j)
308 */
309 for (j=0; j<N_BINTRANS_VADDR_TO_HOST; j++) {
310 if (cpu->cd.
311 mips.bintrans_data_hostpage[j] >=
312 mem->dev_dyntrans_data[i] &&
313 cpu->cd.mips.
314 bintrans_data_hostpage[j] <
315 mem->dev_dyntrans_data[i] +
316 mem->dev_length[i])
317 cpu->cd.mips.
318 bintrans_data_hostpage[j]
319 = NULL;
320 }
321 }
322 return;
323 }
324 }
325 }
326
327
328 /*
329 * memory_device_register_statefunction():
330 *
331 * TODO: Hm. This is semi-ugly. Should probably be rewritten/redesigned
332 * some day.
333 */
334 void memory_device_register_statefunction(
335 struct memory *mem, void *extra,
336 int (*dev_f_state)(struct cpu *,
337 struct memory *, void *extra, int wf, int nr,
338 int *type, char **namep, void **data, size_t *len))
339 {
340 int i;
341
342 for (i=0; i<mem->n_mmapped_devices; i++)
343 if (mem->dev_extra[i] == extra) {
344 mem->dev_f_state[i] = dev_f_state;
345 return;
346 }
347
348 printf("memory_device_register_statefunction(): "
349 "couldn't find the device\n");
350 exit(1);
351 }
352
353
354 /*
355 * memory_device_register():
356 *
357 * Register a (memory mapped) device by adding it to the dev_* fields of a
358 * memory struct.
359 */
360 void memory_device_register(struct memory *mem, const char *device_name,
361 uint64_t baseaddr, uint64_t len,
362 int (*f)(struct cpu *,struct memory *,uint64_t,unsigned char *,
363 size_t,int,void *),
364 void *extra, int flags, unsigned char *dyntrans_data)
365 {
366 int i;
367
368 if (mem->n_mmapped_devices >= MAX_DEVICES) {
369 fprintf(stderr, "memory_device_register(): too many "
370 "devices registered, cannot register '%s'\n", device_name);
371 exit(1);
372 }
373
374 /* Check for collisions: */
375 for (i=0; i<mem->n_mmapped_devices; i++) {
376 /* If we are not colliding with device i, then continue: */
377 if (baseaddr + len <= mem->dev_baseaddr[i])
378 continue;
379 if (baseaddr >= mem->dev_baseaddr[i] + mem->dev_length[i])
380 continue;
381
382 fatal("\nWARNING! \"%s\" collides with device %i (\"%s\")!\n"
383 " Run-time behaviour will be undefined!\n\n",
384 device_name, i, mem->dev_name[i]);
385 }
386
387 /* (40 bits of physical address is displayed) */
388 debug("device %2i at 0x%010llx: %s",
389 mem->n_mmapped_devices, (long long)baseaddr, device_name);
390
391 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)
392 && (baseaddr & mem->dev_dyntrans_alignment) != 0) {
393 fatal("\nWARNING: Device dyntrans access, but unaligned"
394 " baseaddr 0x%llx.\n", (long long)baseaddr);
395 }
396
397 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)) {
398 debug(" (dyntrans %s)",
399 (flags & MEM_DYNTRANS_WRITE_OK)? "R/W" : "R");
400 }
401 debug("\n");
402
403 mem->dev_name[mem->n_mmapped_devices] = strdup(device_name);
404 mem->dev_baseaddr[mem->n_mmapped_devices] = baseaddr;
405 mem->dev_endaddr[mem->n_mmapped_devices] = baseaddr + len;
406 mem->dev_length[mem->n_mmapped_devices] = len;
407 mem->dev_flags[mem->n_mmapped_devices] = flags;
408 mem->dev_dyntrans_data[mem->n_mmapped_devices] = dyntrans_data;
409
410 if (mem->dev_name[mem->n_mmapped_devices] == NULL) {
411 fprintf(stderr, "out of memory\n");
412 exit(1);
413 }
414
415 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)
416 && !(flags & MEM_EMULATED_RAM) && dyntrans_data == NULL) {
417 fatal("\nERROR: Device dyntrans access, but dyntrans_data"
418 " = NULL!\n");
419 exit(1);
420 }
421
422 if ((size_t)dyntrans_data & (sizeof(void *) - 1)) {
423 fprintf(stderr, "memory_device_register():"
424 " dyntrans_data not aligned correctly (%p)\n",
425 dyntrans_data);
426 exit(1);
427 }
428
429 mem->dev_dyntrans_write_low[mem->n_mmapped_devices] = (uint64_t)-1;
430 mem->dev_dyntrans_write_high[mem->n_mmapped_devices] = 0;
431 mem->dev_f[mem->n_mmapped_devices] = f;
432 mem->dev_extra[mem->n_mmapped_devices] = extra;
433 mem->n_mmapped_devices++;
434
435 if (baseaddr < mem->mmap_dev_minaddr)
436 mem->mmap_dev_minaddr = baseaddr & ~mem->dev_dyntrans_alignment;
437 if (baseaddr + len > mem->mmap_dev_maxaddr)
438 mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) |
439 mem->dev_dyntrans_alignment) + 1;
440 }
441
442
443 /*
444 * memory_device_remove():
445 *
446 * Unregister a (memory mapped) device from a memory struct.
447 */
448 void memory_device_remove(struct memory *mem, int i)
449 {
450 if (i < 0 || i >= mem->n_mmapped_devices) {
451 fatal("memory_device_remove(): invalid device number %i\n", i);
452 return;
453 }
454
455 mem->n_mmapped_devices --;
456
457 if (i == mem->n_mmapped_devices)
458 return;
459
460 /*
461 * YUCK! This is ugly. TODO: fix
462 */
463
464 memmove(&mem->dev_name[i], &mem->dev_name[i+1], sizeof(char *) *
465 (MAX_DEVICES - i - 1));
466 memmove(&mem->dev_baseaddr[i], &mem->dev_baseaddr[i+1],
467 sizeof(uint64_t) * (MAX_DEVICES - i - 1));
468 memmove(&mem->dev_length[i], &mem->dev_length[i+1], sizeof(uint64_t) *
469 (MAX_DEVICES - i - 1));
470 memmove(&mem->dev_flags[i], &mem->dev_flags[i+1], sizeof(int) *
471 (MAX_DEVICES - i - 1));
472 memmove(&mem->dev_extra[i], &mem->dev_extra[i+1], sizeof(void *) *
473 (MAX_DEVICES - i - 1));
474 memmove(&mem->dev_f[i], &mem->dev_f[i+1], sizeof(void *) *
475 (MAX_DEVICES - i - 1));
476 memmove(&mem->dev_f_state[i], &mem->dev_f_state[i+1], sizeof(void *) *
477 (MAX_DEVICES - i - 1));
478 memmove(&mem->dev_dyntrans_data[i], &mem->dev_dyntrans_data[i+1],
479 sizeof(void *) * (MAX_DEVICES - i - 1));
480 memmove(&mem->dev_dyntrans_write_low[i], &mem->dev_dyntrans_write_low
481 [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
482 memmove(&mem->dev_dyntrans_write_high[i], &mem->dev_dyntrans_write_high
483 [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
484 }
485
486
487 #define MEMORY_RW userland_memory_rw
488 #define MEM_USERLAND
489 #include "memory_rw.c"
490 #undef MEM_USERLAND
491 #undef MEMORY_RW
492
493
494 /*
495 * memory_paddr_to_hostaddr():
496 *
497 * Translate a physical address into a host address.
498 *
499 * Return value is a pointer to a host memblock, or NULL on failure.
500 * On reads, a NULL return value should be interpreted as reading all zeroes.
501 */
502 unsigned char *memory_paddr_to_hostaddr(struct memory *mem,
503 uint64_t paddr, int writeflag)
504 {
505 void **table;
506 int entry;
507 const int mask = (1 << BITS_PER_PAGETABLE) - 1;
508 const int shrcount = MAX_BITS - BITS_PER_PAGETABLE;
509
510 table = mem->pagetable;
511 entry = (paddr >> shrcount) & mask;
512
513 /* printf("memory_paddr_to_hostaddr(): p=%16llx w=%i => entry=0x%x\n",
514 (long long)paddr, writeflag, entry); */
515
516 if (table[entry] == NULL) {
517 size_t alloclen;
518
519 /*
520 * Special case: reading from a nonexistant memblock
521 * returns all zeroes, and doesn't allocate anything.
522 * (If any intermediate pagetable is nonexistant, then
523 * the same thing happens):
524 */
525 if (writeflag == MEM_READ)
526 return NULL;
527
528 /* Allocate a memblock: */
529 alloclen = 1 << BITS_PER_MEMBLOCK;
530
531 /* printf(" allocating for entry %i, len=%i\n",
532 entry, alloclen); */
533
534 /* Anonymous mmap() should return zero-filled memory,
535 try malloc + memset if mmap failed. */
536 table[entry] = (void *) mmap(NULL, alloclen,
537 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
538 -1, 0);
539 if (table[entry] == NULL) {
540 table[entry] = malloc(alloclen);
541 if (table[entry] == NULL) {
542 fatal("out of memory\n");
543 exit(1);
544 }
545 memset(table[entry], 0, alloclen);
546 }
547 }
548
549 return (unsigned char *) table[entry];
550 }
551

  ViewVC Help
Powered by ViewVC 1.1.26