/[gxemul]/trunk/src/disk/diskimage.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/disk/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Mon Oct 8 16:22:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 26704 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1613 2007/06/15 20:11:26 debug Exp $
20070501	Continuing a little on m88k disassembly (control registers,
		more instructions).
		Adding a dummy mvme88k machine mode.
20070502	Re-adding MIPS load/store alignment exceptions.
20070503	Implementing more of the M88K disassembly code.
20070504	Adding disassembly of some more M88K load/store instructions.
		Implementing some relatively simple M88K instructions (br.n,
		xor[.u] imm, and[.u] imm).
20070505	Implementing M88K three-register and, or, xor, and jmp[.n],
		bsr[.n] including function call trace stuff.
		Applying a patch from Bruce M. Simpson which implements the
		SYSCON_BOARD_CPU_CLOCK_FREQ_ID object of the syscon call in
		the yamon PROM emulation.
20070506	Implementing M88K bb0[.n] and bb1[.n], and skeletons for
		ldcr and stcr (although no control regs are implemented yet).
20070509	Found and fixed the bug which caused Linux for QEMU_MIPS to
		stop working in 0.4.5.1: It was a faulty change to the MIPS
		'sc' and 'scd' instructions I made while going through gcc -W
		warnings on 20070428.
20070510	Updating the Linux/QEMU_MIPS section in guestoses.html to
		use mips-test-0.2.tar.gz instead of 0.1.
		A big thank you to Miod Vallat for sending me M88K manuals.
		Implementing more M88K instructions (addu, subu, div[u], mulu,
		ext[u], clr, set, cmp).
20070511	Fixing bugs in the M88K "and" and "and.u" instructions (found
		by comparing against the manual).
		Implementing more M88K instructions (mask[.u], mak, bcnd (auto-
		generated)) and some more control register details.
		Cleanup: Removing the experimental AVR emulation mode and
		corresponding devices; AVR emulation wasn't really meaningful.
		Implementing autogeneration of most M88K loads/stores. The
		rectangle drawing demo (with -O0) for M88K runs :-)
		Beginning on M88K exception handling.
		More M88K instructions: tb0, tb1, rte, sub, jsr[.n].
		Adding some skeleton MVME PROM ("BUG") emulation.
20070512	Fixing a bug in the M88K cmp instruction.
		Adding the M88K lda (scaled register) instruction.
		Fixing bugs in 64-bit (32-bit pairs) M88K loads/stores.
		Removing the unused tick_hz stuff from the machine struct.
		Implementing the M88K xmem instruction. OpenBSD/mvme88k gets
		far enough to display the Copyright banner :-)
		Implementing subu.co (guess), addu.co, addu.ci, ff0, and ff1.
		Adding a dev_mvme187, for MVME187-specific devices/registers.
		OpenBSD/mvme88k prints more boot messages. :)
20070515	Continuing on MVME187 emulation (adding more devices, beginning
		on the CMMUs, etc).
		Adding the M88K and.c, xor.c, and or.c instructions, and making
		sure that mul, div, etc cause exceptions if executed when SFD1
		is disabled.
20070517	Continuing on M88K and MVME187 emulation in general; moving
		the CMMU registers to the CPU struct, separating dev_pcc2 from
		dev_mvme187, and beginning on memory_m88k.c (BATC and PATC).
		Fixing a bug in 64-bit (32-bit pairs) M88K fast stores.
		Implementing the clock part of dev_mk48txx.
		Implementing the M88K fstcr and xcr instructions.
		Implementing m88k_cpu_tlbdump().
		Beginning on the implementation of a separate address space
		for M88K .usr loads/stores.
20070520	Removing the non-working (skeleton) Sandpoint, SonyNEWS, SHARK
		Dnard, and Zaurus machine modes.
		Experimenting with dyntrans to_be_translated read-ahead. It
		seems to give a very small performance increase for MIPS
		emulation, but a large performance degradation for SuperH. Hm.
20070522	Disabling correct SuperH ITLB emulation; it does not seem to be
		necessary in order to let SH4 guest OSes run, and it slows down
		userspace code.
		Implementing "samepage" branches for SuperH emulation, and some
		other minor speed hacks.
20070525	Continuing on M88K memory-related stuff: exceptions, memory
		transaction register contents, etc.
		Implementing the M88K subu.ci instruction.
		Removing the non-working (skeleton) Iyonix machine mode.
		OpenBSD/mvme88k reaches userland :-), starts executing
		/sbin/init's instructions, and issues a few syscalls, before
		crashing.
20070526	Fixing bugs in dev_mk48txx, so that OpenBSD/mvme88k detects
		the correct time-of-day.
		Implementing a generic IRQ controller for the test machines
		(dev_irqc), similar to a proposed patch from Petr Stepan.
		Experimenting some more with translation read-ahead.
		Adding an "expect" script for automated OpenBSD/landisk
		install regression/performance tests.
20070527	Adding a dummy mmEye (SH3) machine mode skeleton.
		FINALLY found the strange M88K bug I have been hunting: I had
		not emulated the SNIP value for exceptions occurring in
		branch delay slots correctly.
		Implementing correct exceptions for 64-bit M88K loads/stores.
		Address to symbol lookups are now disabled when M88K is
		running in usermode (because usermode addresses don't have
		anything to do with supervisor addresses).
20070531	Removing the mmEye machine mode skeleton.
20070604	Some minor code cleanup.
20070605	Moving src/useremul.c into a subdir (src/useremul/), and
		cleaning up some more legacy constructs.
		Adding -Wstrict-aliasing and -fstrict-aliasing detection to
		the configure script.
20070606	Adding a check for broken GCC on Solaris to the configure
		script. (GCC 3.4.3 on Solaris cannot handle static variables
		which are initialized to 0 or NULL. :-/)
		Removing the old (non-working) ARC emulation modes: NEC RD94,
		R94, R96, and R98, and the last traces of Olivetti M700 and
		Deskstation Tyne.
		Removing the non-working skeleton WDSC device (dev_wdsc).
20070607	Thinking about how to use the host's cc + ld at runtime to
		generate native code. (See experiments/native_cc_ld_test.i
		for an example.)
20070608	Adding a program counter sampling timer, which could be useful
		for native code generation experiments.
		The KN02_CSR_NRMMOD bit in the DECstation 5000/200 (KN02) CSR
		should always be set, to allow a 5000/200 PROM to boot.
20070609	Moving out breakpoint details from the machine struct into
		a helper struct, and removing the limit on max nr of
		breakpoints.
20070610	Moving out tick functions into a helper struct as well (which
		also gets rid of the max limit).
20070612	FINALLY figured out why Debian/DECstation stopped working when
		translation read-ahead was enabled: in src/memory_rw.c, the
		call to invalidate_code_translation was made also if the
		memory access was an instruction load (if the page was mapped
		as writable); it shouldn't be called in that case.
20070613	Implementing some more MIPS32/64 revision 2 instructions: di,
		ei, ext, dext, dextm, dextu, and ins.
20070614	Implementing an instruction combination for the NetBSD/arm
		idle loop (making the host not use any cpu if NetBSD/arm
		inside the emulator is not using any cpu).
		Increasing the nr of ARM VPH entries from 128 to 384.
20070615	Removing the ENABLE_arch stuff from the configure script, so
		that all included architectures are included in both release
		and development builds.
		Moving memory related helper functions from misc.c to memory.c.
		Adding preliminary instructions for netbooting NetBSD/pmppc to
		guestoses.html; it doesn't work yet, there are weird timeouts.
		Beginning a total rewrite of the userland emulation modes
		(removing all emulation modes, beginning from scratch with
		NetBSD/MIPS and FreeBSD/Alpha only).
20070616	After fixing a bug in the DEC21143 NIC (the TDSTAT_OWN bit was
		only cleared for the last segment when transmitting, not all
		segments), NetBSD/pmppc boots with root-on-nfs without the
		timeouts. Updating guestoses.html.
		Removing the skeleton PSP (Playstation Portable) mode.
		Moving X11-related stuff in the machine struct into a helper
		struct.
		Cleanup of out-of-memory checks, to use a new CHECK_ALLOCATION
		macro (which prints a meaningful error message).
		Adding a COMMENT to each machine and device (for automagic
		.index comment generation).
		Doing regression testing for the next release.

==============  RELEASE 0.4.6  ==============


1 /*
2 * Copyright (C) 2003-2007 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: diskimage.c,v 1.7 2007/06/15 17:02:39 debug Exp $
29 *
30 * Disk image support.
31 *
32 * TODO: diskimage_remove()? This would be useful for floppies in PC-style
33 * machines, where disks may need to be swapped during boot etc.
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42
43 #include "cpu.h"
44 #include "diskimage.h"
45 #include "machine.h"
46 #include "misc.h"
47
48
49 /* #define debug fatal */
50
51 extern int single_step;
52
53 static char *diskimage_types[] = DISKIMAGE_TYPES;
54
55
56 /**************************************************************************/
57
58 /*
59 * my_fseek():
60 *
61 * A helper function, like fseek() but takes off_t. If the system has
62 * fseeko, then that is used. Otherwise I try to fake off_t offsets here.
63 *
64 * The correct position is reached by seeking 2 billion bytes at a time
65 * (or less). Note: This method is only used for SEEK_SET, for SEEK_CUR
66 * and SEEK_END, normal fseek() is used!
67 *
68 * TODO: It seemed to work on Linux/i386, but not on Solaris/sparc (?).
69 * Anyway, most modern systems have fseeko(), so it shouldn't be a problem.
70 */
71 static int my_fseek(FILE *f, off_t offset, int whence)
72 {
73 #ifdef HACK_FSEEKO
74 if (whence == SEEK_SET) {
75 int res = 0;
76 off_t curoff = 0;
77 off_t cur_step;
78
79 fseek(f, 0, SEEK_SET);
80 while (curoff < offset) {
81 /* How far to seek? */
82 cur_step = offset - curoff;
83 if (cur_step > 2000000000)
84 cur_step = 2000000000;
85 res = fseek(f, cur_step, SEEK_CUR);
86 if (res)
87 return res;
88 curoff += cur_step;
89 }
90 return 0;
91 } else
92 return fseek(f, offset, whence);
93 #else
94 return fseeko(f, offset, whence);
95 #endif
96 }
97
98
99 /**************************************************************************/
100
101
102 /*
103 * diskimage_exist():
104 *
105 * Returns 1 if the specified disk id (for a specific type) exists, 0
106 * otherwise.
107 */
108 int diskimage_exist(struct machine *machine, int id, int type)
109 {
110 struct diskimage *d = machine->first_diskimage;
111
112 while (d != NULL) {
113 if (d->type == type && d->id == id)
114 return 1;
115 d = d->next;
116 }
117 return 0;
118 }
119
120
121 /*
122 * diskimage_add_overlay():
123 *
124 * Opens an overlay data file and its corresponding bitmap file, and adds
125 * the overlay to a disk image.
126 */
127 void diskimage_add_overlay(struct diskimage *d, char *overlay_basename)
128 {
129 struct diskimage_overlay overlay;
130 size_t bitmap_name_len = strlen(overlay_basename) + 20;
131 char *bitmap_name;
132
133 CHECK_ALLOCATION(bitmap_name = malloc(bitmap_name_len));
134 snprintf(bitmap_name, bitmap_name_len, "%s.map", overlay_basename);
135
136 CHECK_ALLOCATION(overlay.overlay_basename = strdup(overlay_basename));
137 overlay.f_data = fopen(overlay_basename, d->writable? "r+" : "r");
138 if (overlay.f_data == NULL) {
139 perror(overlay_basename);
140 exit(1);
141 }
142
143 overlay.f_bitmap = fopen(bitmap_name, d->writable? "r+" : "r");
144 if (overlay.f_bitmap == NULL) {
145 perror(bitmap_name);
146 fprintf(stderr, "Please create the map file first.\n");
147 exit(1);
148 }
149
150 d->nr_of_overlays ++;
151
152 CHECK_ALLOCATION(d->overlays = realloc(d->overlays,
153 sizeof(struct diskimage_overlay) * d->nr_of_overlays));
154
155 d->overlays[d->nr_of_overlays - 1] = overlay;
156
157 free(bitmap_name);
158 }
159
160
161 /*
162 * diskimage_recalc_size():
163 *
164 * Recalculate a disk's size by stat()-ing it.
165 * d is assumed to be non-NULL.
166 */
167 void diskimage_recalc_size(struct diskimage *d)
168 {
169 struct stat st;
170 int res;
171 off_t size = 0;
172
173 res = stat(d->fname, &st);
174 if (res) {
175 fprintf(stderr, "[ diskimage_recalc_size(): could not stat "
176 "'%s' ]\n", d->fname);
177 return;
178 }
179
180 size = st.st_size;
181
182 /*
183 * TODO: CD-ROM devices, such as /dev/cd0c, how can one
184 * check how much data is on that cd-rom without reading it?
185 * For now, assume some large number, hopefully it will be
186 * enough to hold any cd-rom image.
187 */
188 if (d->is_a_cdrom && size == 0)
189 size = 762048000;
190
191 d->total_size = size;
192 d->ncyls = d->total_size / 1048576;
193
194 /* TODO: There is a mismatch between d->ncyls and d->cylinders,
195 SCSI-based stuff usually doesn't care. TODO: Fix this. */
196 }
197
198
199 /*
200 * diskimage_getsize():
201 *
202 * Returns -1 if the specified disk id/type does not exists, otherwise
203 * the size of the disk image is returned.
204 */
205 int64_t diskimage_getsize(struct machine *machine, int id, int type)
206 {
207 struct diskimage *d = machine->first_diskimage;
208
209 while (d != NULL) {
210 if (d->type == type && d->id == id)
211 return d->total_size;
212 d = d->next;
213 }
214 return -1;
215 }
216
217
218 /*
219 * diskimage_get_baseoffset():
220 *
221 * Returns -1 if the specified disk id/type does not exists, otherwise
222 * the base offset of the disk image is returned.
223 */
224 int64_t diskimage_get_baseoffset(struct machine *machine, int id, int type)
225 {
226 struct diskimage *d = machine->first_diskimage;
227
228 while (d != NULL) {
229 if (d->type == type && d->id == id)
230 return d->override_base_offset;
231 d = d->next;
232 }
233 return -1;
234 }
235
236
237 /*
238 * diskimage_getchs():
239 *
240 * Returns the current CHS values of a disk image.
241 */
242 void diskimage_getchs(struct machine *machine, int id, int type,
243 int *c, int *h, int *s)
244 {
245 struct diskimage *d = machine->first_diskimage;
246
247 while (d != NULL) {
248 if (d->type == type && d->id == id) {
249 *c = d->cylinders;
250 *h = d->heads;
251 *s = d->sectors_per_track;
252 return;
253 }
254 d = d->next;
255 }
256 fatal("diskimage_getchs(): disk id %i (type %i) not found?\n",
257 id, diskimage_types[type]);
258 exit(1);
259 }
260
261
262 /*
263 * diskimage_access__cdrom():
264 *
265 * This is a special-case function, called from diskimage__internal_access().
266 * On my FreeBSD 4.9 system, the cdrom device /dev/cd0c seems to not be able
267 * to handle something like "fseek(512); fread(512);" but it handles
268 * "fseek(2048); fread(512);" just fine. So, if diskimage__internal_access()
269 * fails in reading a block of data, this function is called as an attempt to
270 * align reads at 2048-byte sectors instead.
271 *
272 * (Ugly hack. TODO: how to solve this cleanly?)
273 *
274 * NOTE: Returns the number of bytes read, 0 if nothing was successfully
275 * read. (These are not the same as diskimage_access()).
276 */
277 #define CDROM_SECTOR_SIZE 2048
278 static size_t diskimage_access__cdrom(struct diskimage *d, off_t offset,
279 unsigned char *buf, size_t len)
280 {
281 off_t aligned_offset;
282 size_t bytes_read, total_copied = 0;
283 unsigned char cdrom_buf[CDROM_SECTOR_SIZE];
284 off_t buf_ofs, i = 0;
285
286 /* printf("diskimage_access__cdrom(): offset=0x%llx size=%lli\n",
287 (long long)offset, (long long)len); */
288
289 aligned_offset = (offset / CDROM_SECTOR_SIZE) * CDROM_SECTOR_SIZE;
290 my_fseek(d->f, aligned_offset, SEEK_SET);
291
292 while (len != 0) {
293 bytes_read = fread(cdrom_buf, 1, CDROM_SECTOR_SIZE, d->f);
294 if (bytes_read != CDROM_SECTOR_SIZE)
295 return 0;
296
297 /* Copy (part of) cdrom_buf into buf: */
298 buf_ofs = offset - aligned_offset;
299 while (buf_ofs < CDROM_SECTOR_SIZE && len != 0) {
300 buf[i ++] = cdrom_buf[buf_ofs ++];
301 total_copied ++;
302 len --;
303 }
304
305 aligned_offset += CDROM_SECTOR_SIZE;
306 offset = aligned_offset;
307 }
308
309 return total_copied;
310 }
311
312
313 /* Helper function. */
314 static void overlay_set_block_in_use(struct diskimage *d,
315 int overlay_nr, off_t ofs)
316 {
317 off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE;
318 off_t bitmap_file_offset = bit_nr / 8;
319 int res;
320 unsigned char data;
321
322 res = my_fseek(d->overlays[overlay_nr].f_bitmap,
323 bitmap_file_offset, SEEK_SET);
324 if (res) {
325 perror("my_fseek");
326 fprintf(stderr, "Could not seek in bitmap file?"
327 " offset = %lli, read\n", (long long)bitmap_file_offset);
328 exit(1);
329 }
330
331 /* Read the original bitmap data, and OR in the new bit: */
332 res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
333 if (res != 1)
334 data = 0x00;
335
336 data |= (1 << (bit_nr & 7));
337
338 /* Write it back: */
339 res = my_fseek(d->overlays[overlay_nr].f_bitmap,
340 bitmap_file_offset, SEEK_SET);
341 if (res) {
342 perror("my_fseek");
343 fprintf(stderr, "Could not seek in bitmap file?"
344 " offset = %lli, write\n", (long long)bitmap_file_offset);
345 exit(1);
346 }
347 res = fwrite(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
348 if (res != 1) {
349 fprintf(stderr, "Could not write to bitmap file. Aborting.\n");
350 exit(1);
351 }
352 }
353
354
355 /* Helper function. */
356 static int overlay_has_block(struct diskimage *d, int overlay_nr, off_t ofs)
357 {
358 off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE;
359 off_t bitmap_file_offset = bit_nr / 8;
360 int res;
361 unsigned char data;
362
363 res = my_fseek(d->overlays[overlay_nr].f_bitmap,
364 bitmap_file_offset, SEEK_SET);
365 if (res != 0)
366 return 0;
367
368 /* The seek succeeded, now read the bit: */
369 res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
370 if (res != 1)
371 return 0;
372
373 if (data & (1 << (bit_nr & 7)))
374 return 1;
375
376 return 0;
377 }
378
379
380 /*
381 * fwrite_helper():
382 *
383 * Internal helper function. Writes to a disk image file, or if the
384 * disk image has overlays, to the last overlay.
385 */
386 static size_t fwrite_helper(off_t offset, unsigned char *buf,
387 size_t len, struct diskimage *d)
388 {
389 off_t curofs;
390
391 /* Fast return-path for the case when no overlays are used: */
392 if (d->nr_of_overlays == 0) {
393 int res = my_fseek(d->f, offset, SEEK_SET);
394 if (res != 0) {
395 fatal("[ diskimage__internal_access(): fseek() failed"
396 " on disk id %i \n", d->id);
397 return 0;
398 }
399
400 return fwrite(buf, 1, len, d->f);
401 }
402
403 if ((len & (OVERLAY_BLOCK_SIZE-1)) != 0) {
404 fatal("TODO: overlay access (write), len not multiple of "
405 "overlay block size. not yet implemented.\n");
406 fatal("len = %lli\n", (long long) len);
407 abort();
408 }
409 if ((offset & (OVERLAY_BLOCK_SIZE-1)) != 0) {
410 fatal("TODO: unaligned overlay access\n");
411 fatal("offset = %lli\n", (long long) offset);
412 abort();
413 }
414
415 /* Split the write into OVERLAY_BLOCK_SIZE writes: */
416 for (curofs = offset; curofs < (off_t) (offset+len);
417 curofs += OVERLAY_BLOCK_SIZE) {
418 /* Always write to the last overlay: */
419 int overlay_nr = d->nr_of_overlays-1;
420 off_t lenwritten;
421 int res = my_fseek(d->overlays[overlay_nr].f_data,
422 curofs, SEEK_SET);
423 if (res != 0) {
424 fatal("[ diskimage__internal_access(): fseek()"
425 " failed on disk id %i \n", d->id);
426 return 0;
427 }
428
429 lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE,
430 d->overlays[overlay_nr].f_data);
431 buf += OVERLAY_BLOCK_SIZE;
432
433 /* Mark this block in the last overlay as in use: */
434 overlay_set_block_in_use(d, overlay_nr, curofs);
435 }
436
437 return len;
438 }
439
440
441 /*
442 * fread_helper():
443 *
444 * Internal helper function. Reads from a disk image file, or if the
445 * disk image has overlays, from the last overlay that has the specific
446 * data (or the disk image file itself).
447 */
448 static size_t fread_helper(off_t offset, unsigned char *buf,
449 size_t len, struct diskimage *d)
450 {
451 off_t curofs;
452 size_t totallenread = 0;
453
454 /* Fast return-path for the case when no overlays are used: */
455 if (d->nr_of_overlays == 0) {
456 int res = my_fseek(d->f, offset, SEEK_SET);
457 if (res != 0) {
458 fatal("[ diskimage__internal_access(): fseek() failed"
459 " on disk id %i \n", d->id);
460 return 0;
461 }
462
463 return fread(buf, 1, len, d->f);
464 }
465
466 /* Split the read into OVERLAY_BLOCK_SIZE reads: */
467 for (curofs=offset; len != 0;
468 curofs = (curofs | (OVERLAY_BLOCK_SIZE-1)) + 1) {
469 /* Find the overlay, if any, that has this block: */
470 off_t lenread, lentoread;
471 int overlay_nr;
472 for (overlay_nr = d->nr_of_overlays-1;
473 overlay_nr >= 0; overlay_nr --) {
474 if (overlay_has_block(d, overlay_nr, curofs))
475 break;
476 }
477
478 lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len;
479
480 if (overlay_nr >= 0) {
481 /* Read from overlay: */
482 int res = my_fseek(d->overlays[overlay_nr].f_data,
483 curofs, SEEK_SET);
484 if (res != 0) {
485 fatal("[ diskimage__internal_access(): fseek()"
486 " failed on disk id %i \n", d->id);
487 return 0;
488 }
489 lenread = fread(buf, 1, lentoread,
490 d->overlays[overlay_nr].f_data);
491 } else {
492 /* Read from the base disk image: */
493 int res = my_fseek(d->f, curofs, SEEK_SET);
494 if (res != 0) {
495 fatal("[ diskimage__internal_access(): fseek()"
496 " failed on disk id %i \n", d->id);
497 return 0;
498 }
499 lenread = fread(buf, 1, lentoread, d->f);
500 }
501
502 if (lenread != lentoread) {
503 fatal("[ INCOMPLETE READ from disk id %i, offset"
504 " %lli ]\n", d->id, (long long)curofs);
505 }
506
507 len -= lentoread;
508 totallenread += lenread;
509 buf += OVERLAY_BLOCK_SIZE;
510 }
511
512 return totallenread;
513 }
514
515
516 /*
517 * diskimage__internal_access():
518 *
519 * Read from or write to a struct diskimage.
520 *
521 * Returns 1 if the access completed successfully, 0 otherwise.
522 */
523 int diskimage__internal_access(struct diskimage *d, int writeflag,
524 off_t offset, unsigned char *buf, size_t len)
525 {
526 ssize_t lendone;
527
528 if (buf == NULL) {
529 fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");
530 exit(1);
531 }
532 if (len == 0)
533 return 1;
534 if (d->f == NULL)
535 return 0;
536
537 if (writeflag) {
538 if (!d->writable)
539 return 0;
540
541 lendone = fwrite_helper(offset, buf, len, d);
542 } else {
543 /*
544 * Special case for CD-ROMs. Actually, this is not needed
545 * for .iso images, only for physical CDROMS on some OSes,
546 * such as FreeBSD.
547 */
548 if (d->is_a_cdrom)
549 lendone = diskimage_access__cdrom(d, offset, buf, len);
550 else
551 lendone = fread_helper(offset, buf, len, d);
552
553 if (lendone < (ssize_t)len)
554 memset(buf + lendone, 0, len - lendone);
555 }
556
557 /* Incomplete data transfer? Then return failure: */
558 if (lendone != (ssize_t)len) {
559 #ifdef UNSTABLE_DEVEL
560 fatal
561 #else
562 debug
563 #endif
564 ("[ diskimage__internal_access(): disk_id %i, offset %lli"
565 ", transfer not completed. len=%i, len_done=%i ]\n",
566 d->id, (long long)offset, (int)len, (int)lendone);
567 return 0;
568 }
569
570 return 1;
571 }
572
573
574 /*
575 * diskimage_access():
576 *
577 * Read from or write to a disk image on a machine.
578 *
579 * Returns 1 if the access completed successfully, 0 otherwise.
580 */
581 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
582 off_t offset, unsigned char *buf, size_t len)
583 {
584 struct diskimage *d = machine->first_diskimage;
585
586 while (d != NULL) {
587 if (d->type == type && d->id == id)
588 break;
589 d = d->next;
590 }
591
592 if (d == NULL) {
593 fatal("[ diskimage_access(): ERROR: trying to access a "
594 "non-existant %s disk image (id %i)\n",
595 diskimage_types[type], id);
596 return 0;
597 }
598
599 offset -= d->override_base_offset;
600 if (offset < 0 && offset + d->override_base_offset >= 0) {
601 debug("[ reading before start of disk image ]\n");
602 /* Returning zeros. */
603 memset(buf, 0, len);
604 return 1;
605 }
606
607 return diskimage__internal_access(d, writeflag, offset, buf, len);
608 }
609
610
611 /*
612 * diskimage_add():
613 *
614 * Add a disk image. fname is the filename of the disk image.
615 * The filename may be prefixed with one or more modifiers, followed
616 * by a colon.
617 *
618 * b specifies that this is a bootable device
619 * c CD-ROM (instead of a normal DISK)
620 * d DISK (this is the default)
621 * f FLOPPY (instead of SCSI)
622 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
623 * automatically calculated). (This is ignored for floppies.)
624 * i IDE (instead of SCSI)
625 * oOFS; set base offset in bytes, when booting from an ISO9660 fs
626 * r read-only (don't allow changes to the file)
627 * s SCSI (this is the default)
628 * t tape
629 * V add an overlay to a disk image
630 * 0-7 force a specific SCSI ID number
631 *
632 * machine is assumed to be non-NULL.
633 * Returns an integer >= 0 identifying the disk image.
634 */
635 int diskimage_add(struct machine *machine, char *fname)
636 {
637 struct diskimage *d, *d2;
638 int id = 0, override_heads=0, override_spt=0;
639 int64_t bytespercyl, override_base_offset=0;
640 char *cp;
641 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
642 int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1;
643 int prefix_o=0, prefix_V=0;
644
645 if (fname == NULL) {
646 fprintf(stderr, "diskimage_add(): NULL ptr\n");
647 return 0;
648 }
649
650 /* Get prefix from fname: */
651 cp = strchr(fname, ':');
652 if (cp != NULL) {
653 while (fname <= cp) {
654 char c = *fname++;
655 switch (c) {
656 case '0':
657 case '1':
658 case '2':
659 case '3':
660 case '4':
661 case '5':
662 case '6':
663 case '7':
664 prefix_id = c - '0';
665 break;
666 case 'b':
667 prefix_b = 1;
668 break;
669 case 'c':
670 prefix_c = 1;
671 break;
672 case 'd':
673 prefix_d = 1;
674 break;
675 case 'f':
676 prefix_f = 1;
677 break;
678 case 'g':
679 prefix_g = 1;
680 override_heads = atoi(fname);
681 while (*fname != '\0' && *fname != ';')
682 fname ++;
683 if (*fname == ';')
684 fname ++;
685 override_spt = atoi(fname);
686 while (*fname != '\0' && *fname != ';' &&
687 *fname != ':')
688 fname ++;
689 if (*fname == ';')
690 fname ++;
691 if (override_heads < 1 ||
692 override_spt < 1) {
693 fatal("Bad geometry: heads=%i "
694 "spt=%i\n", override_heads,
695 override_spt);
696 exit(1);
697 }
698 break;
699 case 'i':
700 prefix_i = 1;
701 break;
702 case 'o':
703 prefix_o = 1;
704 override_base_offset = atoi(fname);
705 while (*fname != '\0' && *fname != ':'
706 && *fname != ';')
707 fname ++;
708 if (*fname == ':' || *fname == ';')
709 fname ++;
710 if (override_base_offset < 0) {
711 fatal("Bad base offset: %"PRIi64
712 "\n", override_base_offset);
713 exit(1);
714 }
715 break;
716 case 'r':
717 prefix_r = 1;
718 break;
719 case 's':
720 prefix_s = 1;
721 break;
722 case 't':
723 prefix_t = 1;
724 break;
725 case 'V':
726 prefix_V = 1;
727 break;
728 case ':':
729 break;
730 default:
731 fprintf(stderr, "diskimage_add(): invalid "
732 "prefix char '%c'\n", c);
733 exit(1);
734 }
735 }
736 }
737
738 /* Allocate a new diskimage struct: */
739 CHECK_ALLOCATION(d = malloc(sizeof(struct diskimage)));
740 memset(d, 0, sizeof(struct diskimage));
741
742 /* Default to IDE disks... */
743 d->type = DISKIMAGE_IDE;
744
745 /* ... but some machines use SCSI by default: */
746 if (machine->machine_type == MACHINE_PMAX ||
747 machine->machine_type == MACHINE_ARC)
748 d->type = DISKIMAGE_SCSI;
749
750 if (prefix_i + prefix_f + prefix_s > 1) {
751 fprintf(stderr, "Invalid disk image prefix(es). You can"
752 "only use one of i, f, and s\nfor each disk image.\n");
753 exit(1);
754 }
755
756 if (prefix_i)
757 d->type = DISKIMAGE_IDE;
758 if (prefix_f)
759 d->type = DISKIMAGE_FLOPPY;
760 if (prefix_s)
761 d->type = DISKIMAGE_SCSI;
762
763 /* Special case: Add an overlay for an already added disk image: */
764 if (prefix_V) {
765 struct diskimage *dx = machine->first_diskimage;
766
767 if (prefix_id < 0) {
768 fprintf(stderr, "The 'V' disk image prefix requires"
769 " a disk ID to also be supplied.\n");
770 exit(1);
771 }
772
773 while (dx != NULL) {
774 if (d->type == dx->type && prefix_id == dx->id)
775 break;
776 dx = dx->next;
777 }
778
779 if (dx == NULL) {
780 fprintf(stderr, "Bad ID supplied for overlay?\n");
781 exit(1);
782 }
783
784 diskimage_add_overlay(dx, fname);
785
786 /* Free the preliminary d struct: */
787 free(d);
788
789 /* Don't add any disk image. This is an overlay! */
790 return -1;
791 }
792
793 /* Add the new disk image in the disk image chain: */
794 d2 = machine->first_diskimage;
795 if (d2 == NULL) {
796 machine->first_diskimage = d;
797 } else {
798 while (d2->next != NULL)
799 d2 = d2->next;
800 d2->next = d;
801 }
802
803 if (prefix_o)
804 d->override_base_offset = override_base_offset;
805
806 CHECK_ALLOCATION(d->fname = strdup(fname));
807
808 d->logical_block_size = 512;
809
810 /*
811 * Is this a tape, CD-ROM or a normal disk?
812 *
813 * An intelligent guess, if no prefixes are used, would be that
814 * filenames ending with .iso or .cdr are CD-ROM images.
815 */
816 if (prefix_t) {
817 d->is_a_tape = 1;
818 } else {
819 if (prefix_c ||
820 ((strlen(d->fname) > 4 &&
821 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
822 strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
823 && !prefix_d)
824 ) {
825 d->is_a_cdrom = 1;
826
827 /*
828 * This is tricky. Should I use 512 or 2048 here?
829 * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
830 * per sector, but NetBSD 2.0_BETA suddenly ignores
831 * this value and uses 2048 instead.
832 *
833 * OpenBSD/arc doesn't like 2048, it requires 512
834 * to work correctly.
835 *
836 * TODO
837 */
838
839 #if 0
840 if (machine->machine_type == MACHINE_PMAX)
841 d->logical_block_size = 512;
842 else
843 d->logical_block_size = 2048;
844 #endif
845 d->logical_block_size = 512;
846 }
847 }
848
849 diskimage_recalc_size(d);
850
851 if ((d->total_size == 720*1024 || d->total_size == 1474560
852 || d->total_size == 2949120 || d->total_size == 1228800)
853 && !prefix_i && !prefix_s)
854 d->type = DISKIMAGE_FLOPPY;
855
856 switch (d->type) {
857 case DISKIMAGE_FLOPPY:
858 if (d->total_size < 737280) {
859 fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
860 exit(1);
861 }
862 d->cylinders = 80;
863 d->heads = 2;
864 d->sectors_per_track = d->total_size / (d->cylinders *
865 d->heads * 512);
866 break;
867 default:/* Non-floppies: */
868 d->heads = 16;
869 d->sectors_per_track = 63;
870 if (prefix_g) {
871 d->chs_override = 1;
872 d->heads = override_heads;
873 d->sectors_per_track = override_spt;
874 }
875 bytespercyl = d->heads * d->sectors_per_track * 512;
876 d->cylinders = d->total_size / bytespercyl;
877 if (d->cylinders * bytespercyl < d->total_size)
878 d->cylinders ++;
879 }
880
881 d->rpms = 3600;
882
883 if (prefix_b)
884 d->is_boot_device = 1;
885
886 d->writable = access(fname, W_OK) == 0? 1 : 0;
887
888 if (d->is_a_cdrom || prefix_r)
889 d->writable = 0;
890
891 d->f = fopen(fname, d->writable? "r+" : "r");
892 if (d->f == NULL) {
893 perror(fname);
894 exit(1);
895 }
896
897 /* Calculate which ID to use: */
898 if (prefix_id == -1) {
899 int free = 0, collision = 1;
900
901 while (collision) {
902 collision = 0;
903 d2 = machine->first_diskimage;
904 while (d2 != NULL) {
905 /* (don't compare against ourselves :) */
906 if (d2 == d) {
907 d2 = d2->next;
908 continue;
909 }
910 if (d2->id == free && d2->type == d->type) {
911 collision = 1;
912 break;
913 }
914 d2 = d2->next;
915 }
916 if (!collision)
917 id = free;
918 else
919 free ++;
920 }
921 } else {
922 id = prefix_id;
923 d2 = machine->first_diskimage;
924 while (d2 != NULL) {
925 /* (don't compare against ourselves :) */
926 if (d2 == d) {
927 d2 = d2->next;
928 continue;
929 }
930 if (d2->id == id && d2->type == d->type) {
931 fprintf(stderr, "disk image id %i "
932 "already in use\n", id);
933 exit(1);
934 }
935 d2 = d2->next;
936 }
937 }
938
939 d->id = id;
940
941 return id;
942 }
943
944
945 /*
946 * diskimage_bootdev():
947 *
948 * Returns the disk id of the device which we're booting from. If typep is
949 * non-NULL, the type is returned as well.
950 *
951 * If no disk was used as boot device, then -1 is returned. (In practice,
952 * this is used to fake network (tftp) boot.)
953 */
954 int diskimage_bootdev(struct machine *machine, int *typep)
955 {
956 struct diskimage *d;
957
958 d = machine->first_diskimage;
959 while (d != NULL) {
960 if (d->is_boot_device) {
961 if (typep != NULL)
962 *typep = d->type;
963 return d->id;
964 }
965 d = d->next;
966 }
967
968 d = machine->first_diskimage;
969 if (d != NULL) {
970 if (typep != NULL)
971 *typep = d->type;
972 return d->id;
973 }
974
975 return -1;
976 }
977
978
979 /*
980 * diskimage_getname():
981 *
982 * Returns 1 if a valid disk image name was returned, 0 otherwise.
983 */
984 int diskimage_getname(struct machine *machine, int id, int type,
985 char *buf, size_t bufsize)
986 {
987 struct diskimage *d = machine->first_diskimage;
988
989 if (buf == NULL)
990 return 0;
991
992 while (d != NULL) {
993 if (d->type == type && d->id == id) {
994 char *p = strrchr(d->fname, '/');
995 if (p == NULL)
996 p = d->fname;
997 else
998 p ++;
999 snprintf(buf, bufsize, "%s", p);
1000 return 1;
1001 }
1002 d = d->next;
1003 }
1004 return 0;
1005 }
1006
1007
1008 /*
1009 * diskimage_is_a_cdrom():
1010 *
1011 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1012 */
1013 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1014 {
1015 struct diskimage *d = machine->first_diskimage;
1016
1017 while (d != NULL) {
1018 if (d->type == type && d->id == id)
1019 return d->is_a_cdrom;
1020 d = d->next;
1021 }
1022 return 0;
1023 }
1024
1025
1026 /*
1027 * diskimage_is_a_tape():
1028 *
1029 * Returns 1 if a disk image is a tape, 0 otherwise.
1030 *
1031 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1032 * boot strings.)
1033 */
1034 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1035 {
1036 struct diskimage *d = machine->first_diskimage;
1037
1038 while (d != NULL) {
1039 if (d->type == type && d->id == id)
1040 return d->is_a_tape;
1041 d = d->next;
1042 }
1043 return 0;
1044 }
1045
1046
1047 /*
1048 * diskimage_dump_info():
1049 *
1050 * Debug dump of all diskimages that are loaded for a specific machine.
1051 */
1052 void diskimage_dump_info(struct machine *machine)
1053 {
1054 int i, iadd = DEBUG_INDENTATION;
1055 struct diskimage *d = machine->first_diskimage;
1056
1057 while (d != NULL) {
1058 debug("diskimage: %s\n", d->fname);
1059 debug_indentation(iadd);
1060
1061 switch (d->type) {
1062 case DISKIMAGE_SCSI:
1063 debug("SCSI");
1064 break;
1065 case DISKIMAGE_IDE:
1066 debug("IDE");
1067 break;
1068 case DISKIMAGE_FLOPPY:
1069 debug("FLOPPY");
1070 break;
1071 default:
1072 debug("UNKNOWN type %i", d->type);
1073 }
1074
1075 debug(" %s", d->is_a_tape? "TAPE" :
1076 (d->is_a_cdrom? "CD-ROM" : "DISK"));
1077 debug(" id %i, ", d->id);
1078 debug("%s, ", d->writable? "read/write" : "read-only");
1079
1080 if (d->type == DISKIMAGE_FLOPPY)
1081 debug("%lli KB", (long long) (d->total_size / 1024));
1082 else
1083 debug("%lli MB", (long long) (d->total_size / 1048576));
1084
1085 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1086 debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1087 d->sectors_per_track);
1088 else
1089 debug(" (%lli sectors)", (long long)
1090 (d->total_size / 512));
1091
1092 if (d->is_boot_device)
1093 debug(" (BOOT)");
1094 debug("\n");
1095
1096 for (i=0; i<d->nr_of_overlays; i++) {
1097 debug("overlay %i: %s\n",
1098 i, d->overlays[i].overlay_basename);
1099 }
1100
1101 debug_indentation(-iadd);
1102
1103 d = d->next;
1104 }
1105 }
1106

  ViewVC Help
Powered by ViewVC 1.1.26