/[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 4 - (hide annotations)
Mon Oct 8 16:18:00 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14225 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.707 2005/04/27 16:37:33 debug Exp $
20050408	Some minor updates to the wdc. Linux now doesn't complain
		anymore if a disk is non-present.
20050409	Various minor fixes (a bintrans bug, and some other things).
		The wdc seems to work with Playstation2 emulation, but there
		is a _long_ annoying delay when disks are detected.
		Fixing a really important bintrans bug (when devices and RAM
		are mixed within 4KB pages), which was triggered with
		NetBSD/playstation2 kernels.
20050410	Adding a dummy dev_ps2_ether (just so that NetBSD doesn't
		complain as much during bootup).
		Symbols starting with '$' are now ignored.
		Renaming dev_ps2_ohci.c to dev_ohci.c, etc.
20050411	Moving the bintrans-cache-isolation check from cpu_mips.c to
		cpu_mips_coproc.c. (I thought this would give a speedup, but
		it's not noticable.)
		Better playstation2 sbus interrupt code.
		Skip ahead many ticks if the count register is read manually.
		(This increases the speed of delay-loops that simply read
		the count register.)
20050412	Updates to the playstation2 timer/interrupt code.
		Some other minor updates.
20050413	NetBSD/cobalt runs from a disk image :-) including userland;
		updating the documentation on how to install NetBSD/cobalt
		using NetBSD/pmax (!).
		Some minor bintrans updates (no real speed improvement) and
		other minor updates (playstation2 now uses the -o options).
20050414	Adding a dummy x86 (and AMD64) mode.
20050415	Adding some (32-bit and 16-bit) x86 instructions.
		Adding some initial support for non-SCSI, non-IDE floppy
		images. (The x86 mode can boot from these, more or less.)
		Moving the devices/ and include/ directories to src/devices/
		and src/include/, respectively.
20050416	Continuing on the x86 stuff. (Adding pc_bios.c and some simple
		support for software interrupts in 16-bit mode.)
20050417	Ripping out most of the x86 instruction decoding stuff, trying
		to rewrite it in a cleaner way.
		Disabling some of the least working CPU families in the
		configure script (sparc, x86, alpha, hppa), so that they are
		not enabled by default.
20050418	Trying to fix the bug which caused problems when turning on
		and off bintrans interactively, by flushing the bintrans cache
		whenever bintrans is manually (re)enabled.
20050419	Adding the 'lswi' ppc instruction.
		Minor updates to the x86 instruction decoding.
20050420	Renaming x86 register name indices from R_xx to X86_R_xx (this
		makes building on Tru64 nicer).
20050422	Adding a check for duplicate MIPS TLB entries on tlbwr/tlbwi.
20050427	Adding screenshots to guestoses.html.
		Some minor fixes and testing for the next release.

==============  RELEASE 0.3.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 4 * $Id: memory.c,v 1.164 2005/04/09 21:10:54 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     * malloc() + memset().
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)
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    
161     s = entries_per_pagetable * sizeof(void *);
162    
163     mem->pagetable = (unsigned char *) mmap(NULL, s,
164     PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
165     if (mem->pagetable == NULL) {
166     mem->pagetable = malloc(s);
167     if (mem->pagetable == NULL) {
168     fprintf(stderr, "out of memory\n");
169     exit(1);
170     }
171     memset(mem->pagetable, 0, s);
172     }
173    
174     mem->mmap_dev_minaddr = 0xffffffffffffffffULL;
175     mem->mmap_dev_maxaddr = 0;
176    
177     return mem;
178     }
179    
180    
181     /*
182     * memory_points_to_string():
183     *
184     * Returns 1 if there's something string-like at addr, otherwise 0.
185     */
186     int memory_points_to_string(struct cpu *cpu, struct memory *mem, uint64_t addr,
187     int min_string_length)
188     {
189     int cur_length = 0;
190     unsigned char c;
191    
192     for (;;) {
193     c = '\0';
194     cpu->memory_rw(cpu, mem, addr+cur_length,
195     &c, sizeof(c), MEM_READ, CACHE_NONE | NO_EXCEPTIONS);
196     if (c=='\n' || c=='\t' || c=='\r' || (c>=' ' && c<127)) {
197     cur_length ++;
198     if (cur_length >= min_string_length)
199     return 1;
200     } else {
201     if (cur_length >= min_string_length)
202     return 1;
203     else
204     return 0;
205     }
206     }
207     }
208    
209    
210     /*
211     * memory_conv_to_string():
212     *
213     * Convert virtual memory contents to a string, placing it in a
214     * buffer provided by the caller.
215     */
216     char *memory_conv_to_string(struct cpu *cpu, struct memory *mem, uint64_t addr,
217     char *buf, int bufsize)
218     {
219     int len = 0;
220     int output_index = 0;
221     unsigned char c, p='\0';
222    
223     while (output_index < bufsize-1) {
224     c = '\0';
225     cpu->memory_rw(cpu, mem, addr+len, &c, sizeof(c), MEM_READ,
226     CACHE_NONE | NO_EXCEPTIONS);
227     buf[output_index] = c;
228     if (c>=' ' && c<127) {
229     len ++;
230     output_index ++;
231     } else if (c=='\n' || c=='\r' || c=='\t') {
232     len ++;
233     buf[output_index] = '\\';
234     output_index ++;
235     switch (c) {
236     case '\n': p = 'n'; break;
237     case '\r': p = 'r'; break;
238     case '\t': p = 't'; break;
239     }
240     if (output_index < bufsize-1) {
241     buf[output_index] = p;
242     output_index ++;
243     }
244     } else {
245     buf[output_index] = '\0';
246     return buf;
247     }
248     }
249    
250     buf[bufsize-1] = '\0';
251     return buf;
252     }
253    
254    
255     /*
256     * memory_device_bintrans_access():
257     *
258     * Get the lowest and highest bintrans access since last time.
259     */
260     void memory_device_bintrans_access(struct cpu *cpu, struct memory *mem,
261     void *extra, uint64_t *low, uint64_t *high)
262     {
263     #ifdef BINTRANS
264     int i, j;
265     size_t s;
266     int need_inval = 0;
267    
268     /* TODO: This is O(n), so it might be good to rewrite it some day.
269     For now, it will be enough, as long as this function is not
270     called too often. */
271    
272     for (i=0; i<mem->n_mmapped_devices; i++) {
273     if (mem->dev_extra[i] == extra &&
274     mem->dev_bintrans_data[i] != NULL) {
275     if (mem->dev_bintrans_write_low[i] != (uint64_t) -1)
276     need_inval = 1;
277     if (low != NULL)
278     *low = mem->dev_bintrans_write_low[i];
279     mem->dev_bintrans_write_low[i] = (uint64_t) -1;
280    
281     if (high != NULL)
282     *high = mem->dev_bintrans_write_high[i];
283     mem->dev_bintrans_write_high[i] = 0;
284    
285     if (!need_inval)
286     return;
287    
288     if (cpu->machine->arch != ARCH_MIPS) {
289     /* TODO! */
290    
291     return;
292     }
293    
294     /* Invalidate any pages of this device that might
295     be in the bintrans load/store cache, by marking
296     the pages read-only. */
297    
298     for (s=0; s<mem->dev_length[i]; s+=4096) {
299     mips_invalidate_translation_caches_paddr(
300     cpu, mem->dev_baseaddr[i] + s);
301     }
302    
303     /* ... and invalidate the "fast_vaddr_to_hostaddr"
304     cache entries that contain pointers to this
305     device: (NOTE: Device i, cache entry j) */
306     for (j=0; j<N_BINTRANS_VADDR_TO_HOST; j++) {
307     if (cpu->cd.mips.bintrans_data_hostpage[j] >=
308     mem->dev_bintrans_data[i] &&
309     cpu->cd.mips.bintrans_data_hostpage[j] <
310     mem->dev_bintrans_data[i] +
311     mem->dev_length[i])
312     cpu->cd.mips.
313     bintrans_data_hostpage[j] = NULL;
314     }
315    
316     return;
317     }
318     }
319     #endif
320     }
321    
322    
323     /*
324     * memory_device_register_statefunction():
325     *
326     * TODO: Hm. This is semi-ugly. Should probably be rewritten/redesigned
327     * some day.
328     */
329     void memory_device_register_statefunction(
330     struct memory *mem, void *extra,
331     int (*dev_f_state)(struct cpu *,
332     struct memory *, void *extra, int wf, int nr,
333     int *type, char **namep, void **data, size_t *len))
334     {
335     int i;
336    
337     for (i=0; i<mem->n_mmapped_devices; i++)
338     if (mem->dev_extra[i] == extra) {
339     mem->dev_f_state[i] = dev_f_state;
340     return;
341     }
342    
343     printf("memory_device_register_statefunction(): "
344     "couldn't find the device\n");
345     exit(1);
346     }
347    
348    
349     /*
350     * memory_device_register():
351     *
352     * Register a (memory mapped) device by adding it to the dev_* fields of a
353     * memory struct.
354     */
355     void memory_device_register(struct memory *mem, const char *device_name,
356     uint64_t baseaddr, uint64_t len,
357     int (*f)(struct cpu *,struct memory *,uint64_t,unsigned char *,
358     size_t,int,void *),
359     void *extra, int flags, unsigned char *bintrans_data)
360     {
361     int i;
362    
363     if (mem->n_mmapped_devices >= MAX_DEVICES) {
364     fprintf(stderr, "memory_device_register(): too many "
365     "devices registered, cannot register '%s'\n", device_name);
366     exit(1);
367     }
368    
369     /* Check for collisions: */
370     for (i=0; i<mem->n_mmapped_devices; i++) {
371     /* If we are not colliding with device i, then continue: */
372     if (baseaddr + len <= mem->dev_baseaddr[i])
373     continue;
374     if (baseaddr >= mem->dev_baseaddr[i] + mem->dev_length[i])
375     continue;
376    
377     fatal("\nWARNING! \"%s\" collides with device %i (\"%s\")!\n"
378     " Run-time behaviour will be undefined!\n\n",
379     device_name, i, mem->dev_name[i]);
380     }
381    
382     /* (40 bits of physical address is displayed) */
383     debug("device %2i at 0x%010llx: %s",
384     mem->n_mmapped_devices, (long long)baseaddr, device_name);
385    
386     #ifdef BINTRANS
387     if (flags & (MEM_BINTRANS_OK | MEM_BINTRANS_WRITE_OK)
388     && (baseaddr & 0xfff) != 0) {
389     fatal("\nWARNING: Device bintrans access, but unaligned"
390     " baseaddr 0x%llx.\n", (long long)baseaddr);
391     }
392    
393     if (flags & (MEM_BINTRANS_OK | MEM_BINTRANS_WRITE_OK)) {
394     debug(" (bintrans %s)",
395     (flags & MEM_BINTRANS_WRITE_OK)? "R/W" : "R");
396     }
397     #endif
398     debug("\n");
399    
400     mem->dev_name[mem->n_mmapped_devices] = strdup(device_name);
401     mem->dev_baseaddr[mem->n_mmapped_devices] = baseaddr;
402     mem->dev_length[mem->n_mmapped_devices] = len;
403     mem->dev_flags[mem->n_mmapped_devices] = flags;
404     mem->dev_bintrans_data[mem->n_mmapped_devices] = bintrans_data;
405    
406     if (mem->dev_name[mem->n_mmapped_devices] == NULL) {
407     fprintf(stderr, "out of memory\n");
408     exit(1);
409     }
410    
411     if ((size_t)bintrans_data & 1) {
412     fprintf(stderr, "memory_device_register():"
413     " bintrans_data not aligned correctly\n");
414     exit(1);
415     }
416    
417     #ifdef BINTRANS
418     mem->dev_bintrans_write_low[mem->n_mmapped_devices] = (uint64_t)-1;
419     mem->dev_bintrans_write_high[mem->n_mmapped_devices] = 0;
420     #endif
421     mem->dev_f[mem->n_mmapped_devices] = f;
422     mem->dev_extra[mem->n_mmapped_devices] = extra;
423     mem->n_mmapped_devices++;
424    
425     if (baseaddr < mem->mmap_dev_minaddr)
426 dpavlin 4 mem->mmap_dev_minaddr = baseaddr & ~0xfff;
427 dpavlin 2 if (baseaddr + len > mem->mmap_dev_maxaddr)
428 dpavlin 4 mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) | 0xfff) + 1;
429 dpavlin 2 }
430    
431    
432     /*
433     * memory_device_remove():
434     *
435     * Unregister a (memory mapped) device from a memory struct.
436     */
437     void memory_device_remove(struct memory *mem, int i)
438     {
439     if (i < 0 || i >= mem->n_mmapped_devices) {
440     fatal("memory_device_remove(): invalid device number %i\n", i);
441     return;
442     }
443    
444     mem->n_mmapped_devices --;
445    
446     if (i == mem->n_mmapped_devices)
447     return;
448    
449     /*
450     * YUCK! This is ugly. TODO: fix
451     */
452    
453     memmove(&mem->dev_name[i], &mem->dev_name[i+1], sizeof(char *) *
454     (MAX_DEVICES - i - 1));
455     memmove(&mem->dev_baseaddr[i], &mem->dev_baseaddr[i+1],
456     sizeof(uint64_t) * (MAX_DEVICES - i - 1));
457     memmove(&mem->dev_length[i], &mem->dev_length[i+1], sizeof(uint64_t) *
458     (MAX_DEVICES - i - 1));
459     memmove(&mem->dev_flags[i], &mem->dev_flags[i+1], sizeof(int) *
460     (MAX_DEVICES - i - 1));
461     memmove(&mem->dev_extra[i], &mem->dev_extra[i+1], sizeof(void *) *
462     (MAX_DEVICES - i - 1));
463     memmove(&mem->dev_f[i], &mem->dev_f[i+1], sizeof(void *) *
464     (MAX_DEVICES - i - 1));
465     memmove(&mem->dev_f_state[i], &mem->dev_f_state[i+1], sizeof(void *) *
466     (MAX_DEVICES - i - 1));
467     memmove(&mem->dev_bintrans_data[i], &mem->dev_bintrans_data[i+1],
468     sizeof(void *) * (MAX_DEVICES - i - 1));
469     #ifdef BINTRANS
470     memmove(&mem->dev_bintrans_write_low[i], &mem->dev_bintrans_write_low
471     [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
472     memmove(&mem->dev_bintrans_write_high[i], &mem->dev_bintrans_write_high
473     [i+1], sizeof(void *) * (MAX_DEVICES - i - 1));
474     #endif
475     }
476    
477    
478     #define MEMORY_RW userland_memory_rw
479     #define MEM_USERLAND
480     #include "memory_rw.c"
481     #undef MEM_USERLAND
482     #undef MEMORY_RW
483    
484    
485     /*
486     * memory_paddr_to_hostaddr():
487     *
488     * Translate a physical address into a host address.
489     *
490     * Return value is a pointer to a host memblock, or NULL on failure.
491     * On reads, a NULL return value should be interpreted as reading all zeroes.
492     */
493     unsigned char *memory_paddr_to_hostaddr(struct memory *mem,
494     uint64_t paddr, int writeflag)
495     {
496     void **table;
497     int entry;
498     const int mask = (1 << BITS_PER_PAGETABLE) - 1;
499     const int shrcount = MAX_BITS - BITS_PER_PAGETABLE;
500    
501     table = mem->pagetable;
502     entry = (paddr >> shrcount) & mask;
503    
504     /* printf(" entry = %x\n", entry); */
505    
506     if (table[entry] == NULL) {
507     size_t alloclen;
508    
509     /*
510     * Special case: reading from a nonexistant memblock
511     * returns all zeroes, and doesn't allocate anything.
512     * (If any intermediate pagetable is nonexistant, then
513     * the same thing happens):
514     */
515     if (writeflag == MEM_READ)
516     return NULL;
517    
518     /* Allocate a memblock: */
519     alloclen = 1 << BITS_PER_MEMBLOCK;
520    
521     /* printf(" allocating for entry %i, len=%i\n",
522     entry, alloclen); */
523    
524     /* Anonymous mmap() should return zero-filled memory,
525     try malloc + memset if mmap failed. */
526     table[entry] = (void *) mmap(NULL, alloclen,
527     PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
528     -1, 0);
529     if (table[entry] == NULL) {
530     table[entry] = malloc(alloclen);
531     if (table[entry] == NULL) {
532     fatal("out of memory\n");
533     exit(1);
534     }
535     memset(table[entry], 0, alloclen);
536     }
537     }
538    
539     return (unsigned char *) table[entry];
540     }
541    

  ViewVC Help
Powered by ViewVC 1.1.26