Home PC Virtualized

One of my home PCs has crossed the Singularity. I had an old Windows PC from 2003 that I've kept around because it has lots of important stuff on it, including node-locked commercial software. This weekend I duplicated it into a VMWare image and successfully booted it up inside a newer workstation. I don't need the original physical PC anymore, so after taking out the hard drives I'm carting it off to Google's recycling station.

I wish I'd done this since I was a kid. I could have mirrored all my old Apple II data onto a few Macintosh disks and continued to run Drol and Star Blazer inside an emulator, and then when I lost the Mac faith back in 1997 I could have migrated to whatever Mac-on-Windows emulator must have existed back then. True, there's probably little practical value to reading my Magic Window-formatted book reports from seventh grade, but the geek value is enormous.

Mirroring the PC would probably have been easy with VMWare Converter, but I didn't take that path at first. I was cheaping out and going with free Microsoft Virtual PC 2007, which meant I had to create the drive image myself. Here are the steps I took:


  1. Move all "live" data (stuff I could see myself accessing more than once a decade) onto a file server.

  2. Uninstall soon-to-be inapplicable software, such as video drivers and CD/DVD authoring applications.

  3. Defragment the hard drive.

  4. Boot into a live CD and run GParted to shrink the drive partition as much as possible, leaving just a bit of room so that Windows can still comfortably run.

  5. Copy the drive partition to a file on a second drive using a command roughly like this: dd if=/dev/hda1 of=/mnt/other_drive/hda1.img bs=1M

  6. Also copy the MBR and other fluff: dd if=/dev/hda of=/mnt/other_drive/hda_header.img count=63 (I determined the 63 value by using GParted to figue out where Partition 1 started)

  7. Boot back into Windows and assemble the two parts into a single .vhd file. In retrospect, it would have been much easier to calculate the point where hda1 ended and just copy from block 0 all the way to there, but I was learning as I went.

  8. Try to trick Virtual PC into accepting the new file as a hard drive. After no luck, do a bit of research on the web to find out the format of the last sector in a .vhd. Write a quick C program to generate it (reprinted at end).

  9. Tack on the final sector. Confirm that Virtual PC now lets me create a virtual machine using this as the drive.

  10. Boot the virtual PC. See that it hangs on mup.sys. After finding on the web that a mup.sys hang is a symptom of any of the top 1,000 possible things that could go wrong with your computer, give up on Virtual PC and try VMWare Workstation.

  11. Learn that my particular PC is actually hanging on sptd.sys. Delete that. Try again. Success.

  12. Now fire up VMWare Converter. Import the working .vhd into VMWare. Clean up the PC and shut it down.

  13. 7zip the new VMWare machine into a .7z file (getting about 50% compression!). Drop the .7z file onto a file server for safekeeping.

  14. All done!

Here's the code to generate the .vhd footer for a fixed-size drive (though if you read my instructions start to finish you'll just do what I wish I'd done and avoid Virtual PC):

#include <windows.h>
#include <objbase.h>

typedef struct {
unsigned char cookie[8];
unsigned char features[4];
unsigned char file_format_version[4];
unsigned char data_offset[8];
unsigned char time_stamp[4];
unsigned char creator_application[4];
unsigned char creator_version[4];
unsigned char creator_host_os[4];
unsigned char original_size[8];
unsigned char current_size[8];
unsigned char disk_geometry[4];
unsigned char disk_type[4];
unsigned char checksum[4];
unsigned char unique_id[16];
unsigned char saved_state[1];
unsigned char reserved[427];
} hard_disk_footer;

void write_int16(unsigned char *p, unsigned short val) {
*p++ = (unsigned char)(val>>8);
*p++ = (unsigned char)val;
}

void write_int32(unsigned char *p, unsigned int val) {
*p++ = (unsigned char)(val>>24);
*p++ = (unsigned char)(val>>16);
*p++ = (unsigned char)(val>>8);
*p++ = (unsigned char)val;
}

void write_int64(unsigned char *p, ULONGLONG val) {
*p++ = (unsigned char)(val>>56);
*p++ = (unsigned char)(val>>48);
*p++ = (unsigned char)(val>>40);
*p++ = (unsigned char)(val>>32);
*p++ = (unsigned char)(val>>24);
*p++ = (unsigned char)(val>>16);
*p++ = (unsigned char)(val>>8);
*p++ = (unsigned char)val;
}

int _tmain(int argc, _TCHAR* argv[])
{
hard_disk_footer hdf = { 0 };

memcpy(hdf.cookie, "conectix", 8);
write_int32(hdf.features, 2);
write_int32(hdf.file_format_version, 0x00010000);
write_int32(&hdf.data_offset[4], 0xFFFFFFFF);
memcpy(hdf.creator_application, "vpc ", 4);

unsigned int totalSectors = 62914560; // 30GB
unsigned int sectorsPerTrack;
unsigned int heads;
unsigned int cylinderTimesHeads;
unsigned int cylinders;

if (totalSectors > 65535 * 16 * 255) {
totalSectors = 65535 * 16 * 255;
}


if (totalSectors >= 65535 * 16 * 63) {
sectorsPerTrack = 255;
heads = 16;
cylinderTimesHeads = totalSectors / sectorsPerTrack;
} else {
sectorsPerTrack = 17;
cylinderTimesHeads = totalSectors / sectorsPerTrack;
heads = (cylinderTimesHeads + 1023) / 1024;
if (heads < 4) {
heads = 4;
}
if (cylinderTimesHeads >= (heads * 1024) || heads > 16) {
sectorsPerTrack = 31;
heads = 16;
cylinderTimesHeads = totalSectors / sectorsPerTrack;
}
if (cylinderTimesHeads >= (heads * 1024)) {
sectorsPerTrack = 63;
heads = 16;
cylinderTimesHeads = totalSectors / sectorsPerTrack;
}
}
cylinders = cylinderTimesHeads / heads;

write_int16(hdf.disk_geometry, cylinders);
hdf.disk_geometry[2] = (unsigned char)heads;
hdf.disk_geometry[3] = (unsigned char)sectorsPerTrack;

write_int32(hdf.disk_type, 2); // fixed

ULONGLONG totalSize = UInt32x32To64(totalSectors, 512);
write_int64(hdf.current_size, totalSize);

CoCreateGuid((GUID*)&hdf.unique_id);

unsigned int checksum = 0;
write_int32(hdf.checksum, 0);
unsigned int counter;

unsigned char *p = (unsigned char *)&hdf;
for (counter = 0 ; counter < sizeof(hard_disk_footer); counter++) {
checksum += p[counter];
}
write_int32(hdf.checksum, ~checksum);

FILE *f = fopen("e:\\hdr.out", "wb");
fwrite(&hdf, 1, sizeof(hard_disk_footer), f);
return 0;
}

Leave a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About this Entry

This page contains a single entry by Mike Tsao published on April 20, 2008 4:48 PM.

Your options, your taxes, and you was the previous entry in this blog.

Goodbye, Google. Hello, FSX! is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Powered by Movable Type 4.01