/[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 4 - (show 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 /*
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.164 2005/04/09 21:10:54 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().
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 mem->mmap_dev_minaddr = baseaddr & ~0xfff;
427 if (baseaddr + len > mem->mmap_dev_maxaddr)
428 mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) | 0xfff) + 1;
429 }
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