/[gxemul]/upstream/0.3.6/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 /upstream/0.3.6/src/memory.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 15 - (hide annotations)
Mon Oct 8 16:18:56 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 14819 byte(s)
0.3.6
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 14 * $Id: memory.c,v 1.176 2005/08/28 20:16:23 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 12 if (cpu->invalidate_translation_caches_paddr != NULL) {
294     for (s=0; s<mem->dev_length[i];
295     s+=cpu->machine->arch_pagesize)
296     cpu->invalidate_translation_caches_paddr
297 dpavlin 14 (cpu, mem->dev_baseaddr[i] + s,
298     JUST_MARK_AS_NON_WRITABLE);
299 dpavlin 2 }
300    
301 dpavlin 12 if (cpu->machine->arch == ARCH_MIPS) {
302     /*
303     * ... and invalidate the "fast_vaddr_to_
304     * hostaddr" cache entries that contain
305     * pointers to this device: (NOTE: Device i,
306     * cache entry j)
307     */
308     for (j=0; j<N_BINTRANS_VADDR_TO_HOST; j++) {
309     if (cpu->cd.
310     mips.bintrans_data_hostpage[j] >=
311     mem->dev_dyntrans_data[i] &&
312     cpu->cd.mips.
313     bintrans_data_hostpage[j] <
314     mem->dev_dyntrans_data[i] +
315     mem->dev_length[i])
316     cpu->cd.mips.
317     bintrans_data_hostpage[j]
318     = NULL;
319     }
320 dpavlin 2 }
321     return;
322     }
323     }
324     }
325    
326    
327     /*
328     * memory_device_register_statefunction():
329     *
330     * TODO: Hm. This is semi-ugly. Should probably be rewritten/redesigned
331     * some day.
332     */
333     void memory_device_register_statefunction(
334     struct memory *mem, void *extra,
335     int (*dev_f_state)(struct cpu *,
336     struct memory *, void *extra, int wf, int nr,
337     int *type, char **namep, void **data, size_t *len))
338     {
339     int i;
340    
341     for (i=0; i<mem->n_mmapped_devices; i++)
342     if (mem->dev_extra[i] == extra) {
343     mem->dev_f_state[i] = dev_f_state;
344     return;
345     }
346    
347     printf("memory_device_register_statefunction(): "
348     "couldn't find the device\n");
349     exit(1);
350     }
351    
352    
353     /*
354     * memory_device_register():
355     *
356     * Register a (memory mapped) device by adding it to the dev_* fields of a
357     * memory struct.
358     */
359     void memory_device_register(struct memory *mem, const char *device_name,
360     uint64_t baseaddr, uint64_t len,
361     int (*f)(struct cpu *,struct memory *,uint64_t,unsigned char *,
362     size_t,int,void *),
363 dpavlin 12 void *extra, int flags, unsigned char *dyntrans_data)
364 dpavlin 2 {
365     int i;
366    
367     if (mem->n_mmapped_devices >= MAX_DEVICES) {
368     fprintf(stderr, "memory_device_register(): too many "
369     "devices registered, cannot register '%s'\n", device_name);
370     exit(1);
371     }
372    
373     /* Check for collisions: */
374     for (i=0; i<mem->n_mmapped_devices; i++) {
375     /* If we are not colliding with device i, then continue: */
376     if (baseaddr + len <= mem->dev_baseaddr[i])
377     continue;
378     if (baseaddr >= mem->dev_baseaddr[i] + mem->dev_length[i])
379     continue;
380    
381     fatal("\nWARNING! \"%s\" collides with device %i (\"%s\")!\n"
382     " Run-time behaviour will be undefined!\n\n",
383     device_name, i, mem->dev_name[i]);
384     }
385    
386     /* (40 bits of physical address is displayed) */
387     debug("device %2i at 0x%010llx: %s",
388     mem->n_mmapped_devices, (long long)baseaddr, device_name);
389    
390 dpavlin 12 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)
391     && (baseaddr & mem->dev_dyntrans_alignment) != 0) {
392     fatal("\nWARNING: Device dyntrans access, but unaligned"
393 dpavlin 2 " baseaddr 0x%llx.\n", (long long)baseaddr);
394     }
395    
396 dpavlin 12 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)) {
397     debug(" (dyntrans %s)",
398     (flags & MEM_DYNTRANS_WRITE_OK)? "R/W" : "R");
399 dpavlin 2 }
400     debug("\n");
401    
402     mem->dev_name[mem->n_mmapped_devices] = strdup(device_name);
403     mem->dev_baseaddr[mem->n_mmapped_devices] = baseaddr;
404     mem->dev_length[mem->n_mmapped_devices] = len;
405     mem->dev_flags[mem->n_mmapped_devices] = flags;
406 dpavlin 12 mem->dev_dyntrans_data[mem->n_mmapped_devices] = dyntrans_data;
407 dpavlin 2
408     if (mem->dev_name[mem->n_mmapped_devices] == NULL) {
409     fprintf(stderr, "out of memory\n");
410     exit(1);
411     }
412    
413 dpavlin 12 if (flags & (MEM_DYNTRANS_OK | MEM_DYNTRANS_WRITE_OK)
414     && dyntrans_data == NULL) {
415     fatal("\nERROR: Device dyntrans access, but dyntrans_data"
416     " = NULL!\n");
417     exit(1);
418     }
419    
420     if ((size_t)dyntrans_data & 7) {
421 dpavlin 2 fprintf(stderr, "memory_device_register():"
422 dpavlin 12 " dyntrans_data not aligned correctly (%p)\n",
423     dyntrans_data);
424 dpavlin 2 exit(1);
425     }
426    
427 dpavlin 12 mem->dev_dyntrans_write_low[mem->n_mmapped_devices] = (uint64_t)-1;
428     mem->dev_dyntrans_write_high[mem->n_mmapped_devices] = 0;
429 dpavlin 2 mem->dev_f[mem->n_mmapped_devices] = f;
430     mem->dev_extra[mem->n_mmapped_devices] = extra;
431     mem->n_mmapped_devices++;
432    
433     if (baseaddr < mem->mmap_dev_minaddr)
434 dpavlin 12 mem->mmap_dev_minaddr = baseaddr & ~mem->dev_dyntrans_alignment;
435 dpavlin 2 if (baseaddr + len > mem->mmap_dev_maxaddr)
436 dpavlin 12 mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) |
437     mem->dev_dyntrans_alignment) + 1;
438 dpavlin 2 }
439    
440    
441     /*
442     * memory_device_remove():
443     *
444     * Unregister a (memory mapped) device from a memory struct.
445     */
446     void memory_device_remove(struct memory *mem, int i)
447     {
448     if (i < 0 || i >= mem->n_mmapped_devices) {
449     fatal("memory_device_remove(): invalid device number %i\n", i);
450     return;
451     }
452    
453     mem->n_mmapped_devices --;
454    
455     if (i == mem->n_mmapped_devices)
456     return;
457    
458     /*
459     * YUCK! This is ugly. TODO: fix
460     */
461    
462     memmove(&mem->dev_name[i], &mem->dev_name[i+1], sizeof(char *) *
463     (MAX_DEVICES - i - 1));
464     memmove(&mem->dev_baseaddr[i], &mem->dev_baseaddr[i+1],
465     sizeof(uint64_t) * (MAX_DEVICES - i - 1));
466     memmove(&mem->dev_length[i], &mem->dev_length[i+1], sizeof(uint64_t) *
467     (MAX_DEVICES - i - 1));
468     memmove(&mem->dev_flags[i], &mem->dev_flags[i+1], sizeof(int) *
469     (MAX_DEVICES - i - 1));
470     memmove(&mem->dev_extra[i], &mem->dev_extra[i+1], sizeof(void *) *
471     (MAX_DEVICES - i - 1));
472     memmove(&mem->dev_f[i], &mem->dev_f[i+1], sizeof(void *) *
473     (MAX_DEVICES - i - 1));
474     memmove(&mem->dev_f_state[i], &mem->dev_f_state[i+1], sizeof(void *) *
475     (MAX_DEVICES - i - 1));
476 dpavlin 12 memmove(&mem->dev_dyntrans_data[i], &mem->dev_dyntrans_data[i+1],
477 dpavlin 2 sizeof(void *) * (MAX_DEVICES - i - 1));
478 dpavlin 12 memmove(&mem->dev_dyntrans_write_low[i], &mem->dev_dyntrans_write_low
479 dpavlin 2 [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
480 dpavlin 12 memmove(&mem->dev_dyntrans_write_high[i], &mem->dev_dyntrans_write_high
481 dpavlin 2 [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
482     }
483    
484    
485     #define MEMORY_RW userland_memory_rw
486     #define MEM_USERLAND
487     #include "memory_rw.c"
488     #undef MEM_USERLAND
489     #undef MEMORY_RW
490    
491    
492     /*
493     * memory_paddr_to_hostaddr():
494     *
495     * Translate a physical address into a host address.
496     *
497     * Return value is a pointer to a host memblock, or NULL on failure.
498     * On reads, a NULL return value should be interpreted as reading all zeroes.
499     */
500     unsigned char *memory_paddr_to_hostaddr(struct memory *mem,
501     uint64_t paddr, int writeflag)
502     {
503     void **table;
504     int entry;
505     const int mask = (1 << BITS_PER_PAGETABLE) - 1;
506     const int shrcount = MAX_BITS - BITS_PER_PAGETABLE;
507    
508     table = mem->pagetable;
509     entry = (paddr >> shrcount) & mask;
510    
511 dpavlin 12 /* printf("memory_paddr_to_hostaddr(): p=%16llx w=%i => entry=0x%x\n",
512     (long long)paddr, writeflag, entry); */
513 dpavlin 2
514     if (table[entry] == NULL) {
515     size_t alloclen;
516    
517     /*
518     * Special case: reading from a nonexistant memblock
519     * returns all zeroes, and doesn't allocate anything.
520     * (If any intermediate pagetable is nonexistant, then
521     * the same thing happens):
522     */
523     if (writeflag == MEM_READ)
524     return NULL;
525    
526     /* Allocate a memblock: */
527     alloclen = 1 << BITS_PER_MEMBLOCK;
528    
529     /* printf(" allocating for entry %i, len=%i\n",
530     entry, alloclen); */
531    
532     /* Anonymous mmap() should return zero-filled memory,
533     try malloc + memset if mmap failed. */
534     table[entry] = (void *) mmap(NULL, alloclen,
535     PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
536     -1, 0);
537     if (table[entry] == NULL) {
538     table[entry] = malloc(alloclen);
539     if (table[entry] == NULL) {
540     fatal("out of memory\n");
541     exit(1);
542     }
543     memset(table[entry], 0, alloclen);
544     }
545     }
546    
547     return (unsigned char *) table[entry];
548     }
549    

  ViewVC Help
Powered by ViewVC 1.1.26