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

Annotation of /trunk/src/disk/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations)
Mon Oct 8 16:22:32 2007 UTC (14 years, 1 month 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 dpavlin 36 /*
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 dpavlin 42 * $Id: diskimage.c,v 1.7 2007/06/15 17:02:39 debug Exp $
29 dpavlin 36 *
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 dpavlin 38 * diskimage_exist():
104 dpavlin 36 *
105 dpavlin 38 * Returns 1 if the specified disk id (for a specific type) exists, 0
106     * otherwise.
107 dpavlin 36 */
108 dpavlin 38 int diskimage_exist(struct machine *machine, int id, int type)
109 dpavlin 36 {
110 dpavlin 38 struct diskimage *d = machine->first_diskimage;
111 dpavlin 36
112 dpavlin 38 while (d != NULL) {
113     if (d->type == type && d->id == id)
114     return 1;
115     d = d->next;
116 dpavlin 36 }
117 dpavlin 38 return 0;
118 dpavlin 36 }
119    
120    
121     /*
122 dpavlin 38 * diskimage_add_overlay():
123 dpavlin 36 *
124 dpavlin 38 * Opens an overlay data file and its corresponding bitmap file, and adds
125     * the overlay to a disk image.
126 dpavlin 36 */
127 dpavlin 38 void diskimage_add_overlay(struct diskimage *d, char *overlay_basename)
128 dpavlin 36 {
129 dpavlin 38 struct diskimage_overlay overlay;
130     size_t bitmap_name_len = strlen(overlay_basename) + 20;
131 dpavlin 42 char *bitmap_name;
132 dpavlin 38
133 dpavlin 42 CHECK_ALLOCATION(bitmap_name = malloc(bitmap_name_len));
134 dpavlin 38 snprintf(bitmap_name, bitmap_name_len, "%s.map", overlay_basename);
135 dpavlin 36
136 dpavlin 42 CHECK_ALLOCATION(overlay.overlay_basename = strdup(overlay_basename));
137 dpavlin 38 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 dpavlin 36
143 dpavlin 38 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 dpavlin 36 }
149    
150 dpavlin 38 d->nr_of_overlays ++;
151 dpavlin 36
152 dpavlin 42 CHECK_ALLOCATION(d->overlays = realloc(d->overlays,
153     sizeof(struct diskimage_overlay) * d->nr_of_overlays));
154    
155 dpavlin 38 d->overlays[d->nr_of_overlays - 1] = overlay;
156 dpavlin 36
157 dpavlin 38 free(bitmap_name);
158 dpavlin 36 }
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 dpavlin 38 void diskimage_recalc_size(struct diskimage *d)
168 dpavlin 36 {
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 dpavlin 38 /* Helper function. */
314     static void overlay_set_block_in_use(struct diskimage *d,
315     int overlay_nr, off_t ofs)
316 dpavlin 36 {
317 dpavlin 38 off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE;
318     off_t bitmap_file_offset = bit_nr / 8;
319 dpavlin 36 int res;
320 dpavlin 38 unsigned char data;
321 dpavlin 36
322 dpavlin 38 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 dpavlin 36 exit(1);
329     }
330    
331 dpavlin 38 /* 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 dpavlin 36
336 dpavlin 38 data |= (1 << (bit_nr & 7));
337 dpavlin 36
338 dpavlin 38 /* 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 dpavlin 36 }
347 dpavlin 38 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 dpavlin 36 }
352     }
353    
354    
355 dpavlin 38 /* Helper function. */
356     static int overlay_has_block(struct diskimage *d, int overlay_nr, off_t ofs)
357 dpavlin 36 {
358 dpavlin 38 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 dpavlin 36
363 dpavlin 38 res = my_fseek(d->overlays[overlay_nr].f_bitmap,
364     bitmap_file_offset, SEEK_SET);
365     if (res != 0)
366 dpavlin 36 return 0;
367    
368 dpavlin 38 /* The seek succeeded, now read the bit: */
369     res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap);
370     if (res != 1)
371 dpavlin 36 return 0;
372    
373 dpavlin 38 if (data & (1 << (bit_nr & 7)))
374     return 1;
375 dpavlin 36
376 dpavlin 38 return 0;
377     }
378 dpavlin 36
379    
380 dpavlin 38 /*
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 dpavlin 36 {
389 dpavlin 38 off_t curofs;
390 dpavlin 36
391 dpavlin 38 /* 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 dpavlin 36 }
399    
400 dpavlin 38 return fwrite(buf, 1, len, d->f);
401     }
402 dpavlin 36
403 dpavlin 38 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 dpavlin 36
415 dpavlin 38 /* Split the write into OVERLAY_BLOCK_SIZE writes: */
416 dpavlin 40 for (curofs = offset; curofs < (off_t) (offset+len);
417     curofs += OVERLAY_BLOCK_SIZE) {
418 dpavlin 38 /* 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 dpavlin 36 }
428    
429 dpavlin 38 lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE,
430     d->overlays[overlay_nr].f_data);
431     buf += OVERLAY_BLOCK_SIZE;
432 dpavlin 36
433 dpavlin 38 /* Mark this block in the last overlay as in use: */
434     overlay_set_block_in_use(d, overlay_nr, curofs);
435     }
436 dpavlin 36
437 dpavlin 38 return len;
438     }
439 dpavlin 36
440    
441 dpavlin 38 /*
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 dpavlin 36
454 dpavlin 38 /* 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 dpavlin 36 }
462    
463 dpavlin 38 return fread(buf, 1, len, d->f);
464     }
465 dpavlin 36
466 dpavlin 38 /* 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 dpavlin 36 }
477    
478 dpavlin 38 lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len;
479 dpavlin 36
480 dpavlin 38 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 dpavlin 36 }
489 dpavlin 38 lenread = fread(buf, 1, lentoread,
490     d->overlays[overlay_nr].f_data);
491 dpavlin 36 } else {
492 dpavlin 38 /* 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 dpavlin 36 }
499 dpavlin 38 lenread = fread(buf, 1, lentoread, d->f);
500 dpavlin 36 }
501    
502 dpavlin 38 if (lenread != lentoread) {
503     fatal("[ INCOMPLETE READ from disk id %i, offset"
504     " %lli ]\n", d->id, (long long)curofs);
505 dpavlin 36 }
506    
507 dpavlin 38 len -= lentoread;
508     totallenread += lenread;
509     buf += OVERLAY_BLOCK_SIZE;
510     }
511 dpavlin 36
512 dpavlin 38 return totallenread;
513     }
514 dpavlin 36
515    
516 dpavlin 38 /*
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 dpavlin 36
528 dpavlin 38 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 dpavlin 36
537 dpavlin 38 if (writeflag) {
538     if (!d->writable)
539     return 0;
540 dpavlin 36
541 dpavlin 38 lendone = fwrite_helper(offset, buf, len, d);
542     } else {
543 dpavlin 36 /*
544 dpavlin 38 * 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 dpavlin 36 */
548 dpavlin 38 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 dpavlin 36
553 dpavlin 38 if (lendone < (ssize_t)len)
554     memset(buf + lendone, 0, len - lendone);
555     }
556 dpavlin 36
557 dpavlin 38 /* 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 dpavlin 36 }
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 dpavlin 38 * V add an overlay to a disk image
630 dpavlin 36 * 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 dpavlin 38 int prefix_o=0, prefix_V=0;
644 dpavlin 36
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 dpavlin 38 case 'V':
726     prefix_V = 1;
727     break;
728 dpavlin 36 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 dpavlin 42 CHECK_ALLOCATION(d = malloc(sizeof(struct diskimage)));
740 dpavlin 36 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 dpavlin 38 /* 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 dpavlin 36 if (prefix_o)
804     d->override_base_offset = override_base_offset;
805    
806 dpavlin 42 CHECK_ALLOCATION(d->fname = strdup(fname));
807 dpavlin 36
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 dpavlin 38 int i, iadd = DEBUG_INDENTATION;
1055 dpavlin 36 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 dpavlin 38 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 dpavlin 36 debug_indentation(-iadd);
1102    
1103     d = d->next;
1104     }
1105     }
1106    

  ViewVC Help
Powered by ViewVC 1.1.26