/[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

Annotation of /trunk/src/memory.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (hide 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 dpavlin 2 /*
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 dpavlin 18 * $Id: memory.c,v 1.180 2005/10/25 15:51:02 debug Exp $
29 dpavlin 2 *
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 dpavlin 12 * malloc() + memset(). The returned memory block contains only zeroes.
112 dpavlin 2 */
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 dpavlin 12 struct memory *memory_new(uint64_t physical_max, int arch)
136 dpavlin 2 {
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 dpavlin 12 mem->dev_dyntrans_alignment = 4095;
161     if (arch == ARCH_ALPHA)
162     mem->dev_dyntrans_alignment = 8191;
163 dpavlin 2
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 dpavlin 12 * memory_device_dyntrans_access():
260 dpavlin 2 *
261 dpavlin 12 * Get the lowest and highest dyntrans (or bintrans) access since last time.
262 dpavlin 2 */
263 dpavlin 12 void memory_device_dyntrans_access(struct cpu *cpu, struct memory *mem,
264 dpavlin 2 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 dpavlin 12 mem->dev_dyntrans_data[i] != NULL) {
277     if (mem->dev_dyntrans_write_low[i] != (uint64_t) -1)
278 dpavlin 2 need_inval = 1;
279     if (low != NULL)
280 dpavlin 12 *low = mem->dev_dyntrans_write_low[i];
281     mem->dev_dyntrans_write_low[i] = (uint64_t) -1;
282 dpavlin 2
283     if (high != NULL)
284 dpavlin 12 *high = mem->dev_dyntrans_write_high[i];
285     mem->dev_dyntrans_write_high[i] = 0;
286 dpavlin 2
287     if (!need_inval)
288     return;
289    
290     /* Invalidate any pages of this device that might
291 dpavlin 12 be in the dyntrans load/store cache, by marking
292 dpavlin 2 the pages read-only. */
293 dpavlin 18 if (cpu->invalidate_translation_caches != NULL) {
294 dpavlin 12 for (s=0; s<mem->dev_length[i];
295     s+=cpu->machine->arch_pagesize)
296 dpavlin 18 cpu->invalidate_translation_caches
297 dpavlin 14 (cpu, mem->dev_baseaddr[i] + s,
298 dpavlin 18 JUST_MARK_AS_NON_WRITABLE
299     | INVALIDATE_PADDR);
300 dpavlin 2 }
301    
302 dpavlin 12 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 dpavlin 2 }
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 dpavlin 12 void *extra, int flags, unsigned char *dyntrans_data)
365 dpavlin 2 {
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 dpavlin 12 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 dpavlin 2 " baseaddr 0x%llx.\n", (long long)baseaddr);
395     }
396    
397 dpavlin 12 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)) {
398     debug(" (dyntrans %s)",
399     (flags & MEM_DYNTRANS_WRITE_OK)? "R/W" : "R");
400 dpavlin 2 }
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 dpavlin 18 mem->dev_endaddr[mem->n_mmapped_devices] = baseaddr + len;
406 dpavlin 2 mem->dev_length[mem->n_mmapped_devices] = len;
407     mem->dev_flags[mem->n_mmapped_devices] = flags;
408 dpavlin 12 mem->dev_dyntrans_data[mem->n_mmapped_devices] = dyntrans_data;
409 dpavlin 2
410     if (mem->dev_name[mem->n_mmapped_devices] == NULL) {
411     fprintf(stderr, "out of memory\n");
412     exit(1);
413     }
414    
415 dpavlin 12 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)
416 dpavlin 18 && !(flags & MEM_EMULATED_RAM) && dyntrans_data == NULL) {
417 dpavlin 12 fatal("\nERROR: Device dyntrans access, but dyntrans_data"
418     " = NULL!\n");
419     exit(1);
420     }
421    
422 dpavlin 18 if ((size_t)dyntrans_data & (sizeof(void *) - 1)) {
423 dpavlin 2 fprintf(stderr, "memory_device_register():"
424 dpavlin 12 " dyntrans_data not aligned correctly (%p)\n",
425     dyntrans_data);
426 dpavlin 2 exit(1);
427     }
428    
429 dpavlin 12 mem->dev_dyntrans_write_low[mem->n_mmapped_devices] = (uint64_t)-1;
430     mem->dev_dyntrans_write_high[mem->n_mmapped_devices] = 0;
431 dpavlin 2 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 dpavlin 12 mem->mmap_dev_minaddr = baseaddr & ~mem->dev_dyntrans_alignment;
437 dpavlin 2 if (baseaddr + len > mem->mmap_dev_maxaddr)
438 dpavlin 12 mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) |
439     mem->dev_dyntrans_alignment) + 1;
440 dpavlin 2 }
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 dpavlin 12 memmove(&mem->dev_dyntrans_data[i], &mem->dev_dyntrans_data[i+1],
479 dpavlin 2 sizeof(void *) * (MAX_DEVICES - i - 1));
480 dpavlin 12 memmove(&mem->dev_dyntrans_write_low[i], &mem->dev_dyntrans_write_low
481 dpavlin 2 [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
482 dpavlin 12 memmove(&mem->dev_dyntrans_write_high[i], &mem->dev_dyntrans_write_high
483 dpavlin 2 [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 dpavlin 12 /* printf("memory_paddr_to_hostaddr(): p=%16llx w=%i => entry=0x%x\n",
514     (long long)paddr, writeflag, entry); */
515 dpavlin 2
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