- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / vmware4.cc
blob81dda3d99a36ec84f7d3e6a55c69c668258b6438
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: vmware4.cc,v 1.3 2007/10/24 23:17:42 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 /*
6 * This file provides support for VMWare's virtual disk image
7 * format version 4 and above.
9 * Author: Sharvil Nanavati
10 * Contact: snrrrub@gmail.com
12 * Copyright (C) 2006 Sharvil Nanavati.
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
30 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
31 // is used to know when we are exporting symbols and when we are importing.
32 #define BX_PLUGGABLE
34 #define NO_DEVICE_INCLUDES
36 #include "iodev.h"
37 #include "hdimage.h"
38 #include "vmware4.h"
40 #define LOG_THIS bx_devices.pluginHardDrive->
41 const off_t vmware4_image_t::INVALID_OFFSET = (off_t)-1;
42 const int vmware4_image_t::SECTOR_SIZE = 512;
44 vmware4_image_t::vmware4_image_t()
45 : file_descriptor(-1),
46 tlb(0),
47 tlb_offset(INVALID_OFFSET),
48 current_offset(INVALID_OFFSET),
49 is_dirty(false)
53 vmware4_image_t::~vmware4_image_t()
55 close();
58 int vmware4_image_t::open(const char * pathname)
60 close();
62 int flags = O_RDWR;
63 #ifdef O_BINARY
64 flags |= O_BINARY;
65 #endif
67 file_descriptor = ::open(pathname, flags);
69 if(!is_open())
70 return -1;
72 if(!read_header())
73 BX_PANIC(("unable to read vmware4 virtual disk header from file '%s'", pathname));
75 tlb = new Bit8u[(unsigned)header.tlb_size_sectors * SECTOR_SIZE];
76 if(tlb == 0)
77 BX_PANIC(("unable to allocate " FMT_LL "d bytes for vmware4 image's tlb", header.tlb_size_sectors * SECTOR_SIZE));
79 tlb_offset = INVALID_OFFSET;
80 current_offset = 0;
81 is_dirty = false;
83 hd_size = header.total_sectors * SECTOR_SIZE;
84 cylinders = (unsigned)hd_size / (16 * 63);
85 heads = 16;
86 sectors = 63;
88 BX_DEBUG(("VMware 4 disk geometry:"));
89 BX_DEBUG((" .size = " FMT_LL "d", hd_size));
90 BX_DEBUG((" .cylinders = %d", cylinders));
91 BX_DEBUG((" .heads = %d", heads));
92 BX_DEBUG((" .sectors = %d", sectors));
94 return 1;
97 void vmware4_image_t::close()
99 if(file_descriptor == -1)
100 return;
102 flush();
103 delete [] tlb; tlb = 0;
105 ::close(file_descriptor);
106 file_descriptor = -1;
109 Bit64s vmware4_image_t::lseek(Bit64s offset, int whence)
111 switch(whence)
113 case SEEK_SET:
114 current_offset = (off_t)offset;
115 return current_offset;
116 case SEEK_CUR:
117 current_offset += (off_t)offset;
118 return current_offset;
119 case SEEK_END:
120 current_offset = header.total_sectors * SECTOR_SIZE + (off_t)offset;
121 return current_offset;
122 default:
123 BX_DEBUG(("unknown 'whence' value (%d) when trying to seek vmware4 image", whence));
124 return INVALID_OFFSET;
128 ssize_t vmware4_image_t::read(void * buf, size_t count)
130 ssize_t total = 0;
131 while(count > 0)
133 off_t readable = perform_seek();
134 if(readable == INVALID_OFFSET)
136 BX_DEBUG(("vmware4 disk image read failed on %d bytes at " FMT_LL "d", count, current_offset));
137 return -1;
140 off_t copysize = (count > readable) ? readable : count;
141 memcpy(buf, tlb + current_offset - tlb_offset, (size_t)copysize);
143 current_offset += copysize;
144 total += (long)copysize;
145 count -= (size_t)copysize;
147 return total;
150 ssize_t vmware4_image_t::write(const void * buf, size_t count)
152 ssize_t total = 0;
153 while(count > 0)
155 off_t writable = perform_seek();
156 if(writable == INVALID_OFFSET)
158 BX_DEBUG(("vmware4 disk image write failed on %d bytes at " FMT_LL "d", count, current_offset));
159 return -1;
162 off_t writesize = (count > writable) ? writable : count;
163 memcpy(tlb + current_offset - tlb_offset, buf, (size_t)writesize);
165 current_offset += writesize;
166 total += (long)writesize;
167 count -= (size_t)writesize;
168 is_dirty = true;
170 return total;
173 bool vmware4_image_t::is_open() const
175 return (file_descriptor != -1);
178 bool vmware4_image_t::is_valid_header() const
180 if(header.id[0] != 'K' || header.id[1] != 'D' || header.id[2] != 'M' ||
181 header.id[3] != 'V')
183 BX_DEBUG(("not a vmware4 image"));
184 return false;
187 if(header.version != 1)
189 BX_DEBUG(("unsupported vmware4 image version"));
190 return false;
193 return true;
196 bool vmware4_image_t::read_header()
198 if(!is_open())
199 BX_PANIC(("attempt to read vmware4 header from a closed file"));
201 if(::read(file_descriptor, &header, sizeof(VM4_Header)) != sizeof(VM4_Header))
202 return false;
204 header.version = dtoh32(header.version);
205 header.flags = dtoh32(header.flags);
206 header.total_sectors = dtoh64(header.total_sectors);
207 header.tlb_size_sectors = dtoh64(header.tlb_size_sectors);
208 header.description_offset_sectors = dtoh64(header.description_offset_sectors);
209 header.description_size_sectors = dtoh64(header.description_size_sectors);
210 header.slb_count = dtoh32(header.slb_count);
211 header.flb_offset_sectors = dtoh64(header.flb_offset_sectors);
212 header.flb_copy_offset_sectors = dtoh64(header.flb_copy_offset_sectors);
213 header.tlb_offset_sectors = dtoh64(header.tlb_offset_sectors);
215 if(!is_valid_header())
216 BX_PANIC(("invalid vmware4 virtual disk image"));
218 BX_DEBUG(("VM4_Header (size=%d)", sizeof(VM4_Header)));
219 BX_DEBUG((" .version = %d", header.version));
220 BX_DEBUG((" .flags = %d", header.flags));
221 BX_DEBUG((" .total_sectors = " FMT_LL "d", header.total_sectors));
222 BX_DEBUG((" .tlb_size_sectors = " FMT_LL "d", header.tlb_size_sectors));
223 BX_DEBUG((" .description_offset_sectors = " FMT_LL "d", header.description_offset_sectors));
224 BX_DEBUG((" .description_size_sectors = " FMT_LL "d", header.description_size_sectors));
225 BX_DEBUG((" .slb_count = %d", header.slb_count));
226 BX_DEBUG((" .flb_offset_sectors = " FMT_LL "d", header.flb_offset_sectors));
227 BX_DEBUG((" .flb_copy_offset_sectors = " FMT_LL "d", header.flb_copy_offset_sectors));
228 BX_DEBUG((" .tlb_offset_sectors = " FMT_LL "d", header.tlb_offset_sectors));
230 return true;
234 // Returns the number of bytes that can be read from the current offset before needing
235 // to perform another seek.
237 off_t vmware4_image_t::perform_seek()
239 if(current_offset == INVALID_OFFSET)
241 BX_DEBUG(("invalid offset specified in vmware4 seek"));
242 return INVALID_OFFSET;
246 // The currently loaded tlb can service the request.
248 if(tlb_offset / (header.tlb_size_sectors * SECTOR_SIZE) == current_offset / (header.tlb_size_sectors * SECTOR_SIZE))
249 return (header.tlb_size_sectors * SECTOR_SIZE) - (current_offset - tlb_offset);
251 flush();
253 Bit64u index = current_offset / (header.tlb_size_sectors * SECTOR_SIZE);
254 Bit32u slb_index = (Bit32u)(index % header.slb_count);
255 Bit32u flb_index = (Bit32u)(index / header.slb_count);
257 Bit32u slb_sector = read_block_index(header.flb_offset_sectors, flb_index);
258 Bit32u slb_copy_sector = read_block_index(header.flb_copy_offset_sectors, flb_index);
260 if(slb_sector == 0 && slb_copy_sector == 0)
262 BX_DEBUG(("loaded vmware4 disk image requires un-implemented feature"));
263 return INVALID_OFFSET;
265 if(slb_sector == 0)
266 slb_sector = slb_copy_sector;
268 Bit32u tlb_sector = read_block_index(slb_sector, slb_index);
269 tlb_offset = index * header.tlb_size_sectors * SECTOR_SIZE;
270 if(tlb_sector == 0)
273 // Allocate a new tlb
275 memset(tlb, 0, (size_t)header.tlb_size_sectors * SECTOR_SIZE);
278 // Instead of doing a write to increase the file size, we could use
279 // ftruncate but it is not portable.
281 off_t eof = ((::lseek(file_descriptor, 0, SEEK_END) + SECTOR_SIZE - 1) / SECTOR_SIZE) * SECTOR_SIZE;
282 ::write(file_descriptor, tlb, (unsigned)header.tlb_size_sectors * SECTOR_SIZE);
283 tlb_sector = (Bit32u)eof / SECTOR_SIZE;
285 write_block_index(slb_sector, slb_index, tlb_sector);
286 write_block_index(slb_copy_sector, slb_index, tlb_sector);
288 ::lseek(file_descriptor, eof, SEEK_SET);
290 else
292 ::lseek(file_descriptor, tlb_sector * SECTOR_SIZE, SEEK_SET);
293 ::read(file_descriptor, tlb, (unsigned)header.tlb_size_sectors * SECTOR_SIZE);
294 ::lseek(file_descriptor, tlb_sector * SECTOR_SIZE, SEEK_SET);
297 return (header.tlb_size_sectors * SECTOR_SIZE) - (current_offset - tlb_offset);
300 void vmware4_image_t::flush()
302 if(!is_dirty)
303 return;
306 // Write dirty sectors to disk first. Assume that the file is already at the
307 // position for the current tlb.
309 ::write(file_descriptor, tlb, (unsigned)header.tlb_size_sectors * SECTOR_SIZE);
310 is_dirty = false;
313 Bit32u vmware4_image_t::read_block_index(Bit64u sector, Bit32u index)
315 Bit32u ret;
317 ::lseek(file_descriptor, sector * SECTOR_SIZE + index * sizeof(Bit32u), SEEK_SET);
318 ::read(file_descriptor, &ret, sizeof(Bit32u));
320 return dtoh32(ret);
323 void vmware4_image_t::write_block_index(Bit64u sector, Bit32u index, Bit32u block_sector)
325 block_sector = htod32(block_sector);
327 ::lseek(file_descriptor, sector * SECTOR_SIZE + index * sizeof(Bit32u), SEEK_SET);
328 ::write(file_descriptor, &block_sector, sizeof(Bit32u));