netzstaub

beatz & funkz

Friday, March 25, 2005

iPod-surgery

I was a pretty early user of the ipod, and have used a first-generation ipod for quite a long while. As I am pretty rough when it comes to hardware, I had to change my first model eight times (!), each time getting a refurbished one which would die a few weeks later. Finally, I switched to a touch-wheel iPod, but pretty soon it fell down on the ground. During the fall, a few sectors of the hard-disk went bad, which would not be really troublesome normally. However, the sectors were destroyed in such a way that even only trying to read them would lock the ipod up. As it seems, it would lock it up at a very low-level, as even the BIOS hard-disk check locked up. Using the iPod in firewire Harddisk mode yielded no better results. However, I came up with a very dirty and low-level hack to make it work again…

I figured the only solution would be to map the defective sectors by hand. A program would read a block, see if the ipod locked up by getting a timeout, reset the ipod, and try the next block. This way, I could get a pretty good image of which block were defective. The second tool would then take these sector numbers, add a few blocks of padding, and mark them as defective in the FAT (and in the FAT backup copy). The ipod would then handle these sectors as defective from the start.

Without the hack, I could load a few songs (about 200 MB) on the ipod before it would lock up. Now, after fixing the FAT entries, I lost only about 300 MB of hard disk space, and can fill the ipod completely. Today, about 8 months later, my ipod shows sign of battery wearing, but I’m pretty sure this is due to wrong parameters in the BIOS, or whatever. A good way to bring such an ipod back to life (it has only 2 h of battery left apparently) is to restore the firmware from scratch. However, this formats the disk, which means I would lose my patched-up FAT. This is why I searched for this code, which I have written in July or August 2004, in my private CVS, and tried to make it work again. Before this “knowledge” gets lost again, I’ll blog extensively about it :). My memory really gets worse with time.

The software I’ll show in this post is really hackish, it was written in a very short time in order to get the ipod back to normal as fast as possible. It is written in C, and opens the disk device under MacOSX (although the ipod is primary a windows ipod, and is thus formatted using FAT32). The first tool just reads a few values in the FAT, in order to try out a few things. It also helped to prototype the application. The program, called “fat.c”, has a few definitions of FAT data structures and partition table data structures.

fat.c

The first data structure is an entry in the partition table, which is stored in the MBR, the first 512-byte block of the harddisk. The MBR also holds 446 bytes of code, which is where the bootstrapper for LILO or GRUB is stored for example. After this boot code, there is a partition table which holds the description of the four primary partitions of the disk. Extended partitions are implemented using linked lists starting from these partition entries, but we won’t go into that here.

typedef unsigned char u08_t;
typedef unsigned short u16_t;
typedef unsigned long u32_t;

typedef struct part_entry_s {
  u08_t state;
  u08_t begin_head;
  u16_t begin_cylsec;
  u08_t type;
  u08_t end_head;
  u16_t end_cylsec;
  u32_t first_sec;
  u32_t size; /* in sectors */
} __attribute__ ((packed)) part_entry_t;

typedef struct mbr_s {
  u08_t boot_code[446];
  part_entry_t part_entry[4] __attribute__ ((packed));
  u16_t signature;
} __attribute__ ((packed)) mbr_t;

There are four primary partitions on a normal disk, here is the code in “fat.c” which opens the harddisk file, reads the MBR and “parses” the disk information.

  int fd = open(argv[1], O_RDONLY);
  if (fd 

FAT32 needs to hold a bit more information in the MBR. In order to be downwards compatible, this is implemented by storing the information in the boot code, and by putting a jump to the end of the FAT32 table at the beginning of the MBR boot code. Here is the data structure which describes a disk holding FAT32 partitions:

typedef struct fat32_boot_s {
  u08_t jump[3] __attribute__ ((packed));
  u08_t oemname[8] __attribute__ ((packed));
  u16_t bytes_per_sec;
  u08_t sec_per_cluster;
  u16_t reserved_secs;
  u08_t fat_copies;
  u16_t max_root_dirs_na;
  u16_t smaller_secs_na;
  u08_t desc_f8;
  u16_t secs_per_fat_na;
  u16_t secs_per_track;
  u16_t heads;
  u32_t hidden_secs;
  u32_t secs;
  u32_t secs_per_fat;
  u16_t flags;
  u16_t version;
  u32_t clusters;
  u16_t fsis_sec_num;
  u16_t backup_sec_num;
  u08_t reserved[12] __attribute__ ((packed));
  u08_t logical_drive_num;
  u08_t unused;
  u08_t sig;
  u08_t name[11] __attribute__ ((packed));
  u08_t fat_name[8] __attribute__ ((packed));
  u08_t code[420] __attribute__ ((packed));
  u16_t signature;
} __attribute__ ((packed)) fat32_boot_t;

This the code that reads the FAT32 boot sector (it should be compiled at the same place of the MBR code above, as the two fragments read the same block.

  fat32_boot_t boot;
  int ret = read(fd, &boot, 512);
    printf("ret %d\n", ret);
  if (ret != 512) {
    perror("read");
    return 1;
  }
  boot.bytes_per_sec = (boot.bytes_per_sec / 512) * 512;
  printf("signatured %x, %x\n", boot.signature, boot.sig);
  printf("media %x\n", boot.desc_f8);
  printf("reserved_secs %d\n", boot.reserved_secs);
  printf("bytes_per_sec %d\n", boot.bytes_per_sec);
  printf("sec_per_cluster %d\n", boot.sec_per_cluster);
  printf("fat_copies %d\n", boot.fat_copies);
  printf("heads %d, hidden_secs %d, secs %d, secs_per_fat %d\n",
	 boot.heads, boot.hidden_secs, boot.secs, boot.secs_per_fat);
  printf("clusters %d, fsis_sec_num %d, backup_sec_num %d\n",
	 boot.clusters, boot.fsis_sec_num, boot.backup_sec_num);

"fat.c" has a bit more code at the bottom, but I don't really remember its purpose, and will leave it out :).

touch-sec.c

The software which does all the work is called "touch-sec.c", and takes a list of sectors. By default, it only tries to read these sectors, which will kill the ipod if one of the block is defect. You then have to reset the ipod by hand by plugging it out of firewire and plugging it back again. By the way, when you connect the iPod, MacOSX automatically mounts the disk and opens itunes. What you have to do in order to get the software working is to unmount the ipod "by hand" from the terminal, not by clicking the disconnect icon in iTunes, which would also reset the iPod.

The Powerbook I have written this software on has a PPC processor, which works on integers in the big endian order. However, the FAT32 entries are all stored in little endians, so we first need a few macros to convert from the one order to the other. I do this with macros I have written a long long time ago.

#define LE_UINT16_PACK(ptr, i)				\
  { *(ptr++) = (unsigned char)((i) & 0xFF);	\
    *(ptr++) = (unsigned char)(((i) >> 8) & 0xFF); }
#define LE_UINT32_PACK(ptr, i)				\
  { *(ptr++) = (unsigned char)((i) & 0xFF);	\
    *(ptr++) = (unsigned char)(((i) >> 8)  & 0xFF);	\
    *(ptr++) = (unsigned char)(((i) >> 16) & 0xFF);	\
    *(ptr++) = (unsigned char)(((i) >> 24) & 0xFF); }
#define LE_UINT16_UNPACK(ptr) le_uint16_unpack__(&ptr)
#define LE_UINT32_UNPACK(ptr) le_uint32_unpack__(&ptr)
unsigned int le_uint16_unpack__(/*@out@*/ unsigned char **ptr) {
   int     i = (*((*ptr)++) & 0xFF);
   return (i |= (*((*ptr)++) & 0xFF) 

There are now functions to read a sector on the disk, and to read sectors of the FAT. The FAT routines compare the read sector with the sector in the backup, and warn the user if a difference has been found (thank god the important sectors on the ipod are still readable). The routine to write back FAT sectors writes the sector in both the FAT and the FAT backup. The read routine uses a call to alarm to detect timeouts due to the ipod locking up, but I remember that this didn't work very well (I haven't used it this time, only in the summer).

int read_sector(int fd, unsigned char *buf, unsigned long sector) {
  cur_block = sector;
  alarm(1);
  gettimeofday(&read_start, NULL);
  if (!seek(fd, sector)) 
    return 0;
  if (read(fd, buf, 512) != 512) {
    perror("read");
    printf("read error at %d\n", sector);
    alarm(0);
    return 0;
  }
  alarm(0);
  return 512;
}
int read_block(int fd, unsigned char *buf, unsigned long block) {
  return read_sector(fd, buf, 32 + fat_secs * 2 + block);
}

int read_fat(int fd, unsigned char *buf, unsigned long block) {
  unsigned char buf2[512];
  if (!seek(fd, 32 + block))
    return 0;
  if (read(fd, buf, 512) != 512) {
    perror("read");
    printf("read error fat %d\n", block);
    return 0;
  }
  if (!seek(fd, 32 + fat_secs + block))
    return 0;
  if (read(fd, buf2, 512) != 512) {
    perror("read");
    printf("read error fat %d\n", fat_secs + block);
    return 0;
  }

  if (memcmp(buf, buf2, 512) != 0) {
    printf("fat %d and %d are not the same\n",
           block, fat_secs + block);
    printf("buf\n");
    hexdump(buf, 512);
    printf("buf2\n");
    hexdump(buf2, 512);
    return 1;
  }
  
  return 512;
}

The program itself takes two arguments: the first sector to be read or modified, and the last one. Without any flags, the software tries to read the sectors, and warns if a timeout occurs. After having detected all the defective blocks, you can use the "-b" flag. When the "-b" flag is given as an argument, "touch-sec" will mark the sectors given as argument as defect in the FAT. The ipod won't try to read or write them again. There are a few other options which are there to print the read blocks (a hexdump of their content), or to start from the end of the disk (this way I could approach the bad block area from the top and from the bottom). The defective sectors on my ipods are:

1020000 - 1500000

9411380 - 9596467

17799988 - 17984971

26188596 - 26372667

So I just had to do:

./touch-sec 1020000 1500000
./touch-sec 9410000 9600000
./touch-sec 17790000 17990000
./touch-sec 26180000 26380000

This has brought my iPod back to life (notice the few spare blocks around the defective areas). The software can be downloaded from here, it is very hackish, paths are hardcoded, but sure to know what you do before you use it: fat.tar.gz

posted by manuel at 5:20 pm  

9 Comments »

  1. I am curious how to employ the programs. I have an iPod that acts exactly as you have described. I was able to compile the programs but am unclear where it is that I should change hard coded paths. Any help would be appreciated.

    -SMB

    Comment by SMB — June 19, 2005 @ 9:33 pm

  2. I am in the exact situation that you were in. It is comoforting to know that this problem is able to be resolved, but unfortunately, i only have experiance in java coding. I would appreciate it greatly if you would take some time to walk me through the entire process. Thanks for the great blog. If you decide to help, please email me at ckvoss@gmail.com

    Thanks

    Comment by voss — December 6, 2005 @ 12:14 am

  3. Hi!
    I have the exact problem. Tried it with your program but for some reasons it did not work. I could not run the touch-sec-file. Would be great if you can give me some help.

    Comment by Karl — April 4, 2007 @ 9:57 pm

  4. There are many ipod nano movies sites out there in the market but you may be wondering which are the ones that score well in terms of usability, pricing, etc.

    Comment by pradeep — May 26, 2007 @ 9:55 am

  5. http://www.batteryfast.com/laptop-ac-adapter/hp/PA-1650-02HC-hp-18.5V-3.5A-65w-7.4mm-5.0mm-with-pin.php hp 18.5V 3.5A 65w 7.4mm*5.0mm with pin adapter laptop
    http://www.batteryfast.com/laptop-ac-adapter/hp/EG407AA-hp-18.5V-6.5A-120w-flat-dc.php hp 18.5V 6.5A 120w flat dc adapter laptop

    Comment by baterry — November 15, 2008 @ 6:10 am

  6. http://www.batteryfast.co.uk/hp/dv9100.htm hp dv9100 battery,
    http://www.batteryfast.co.uk/hp/dv9500.htm hp dv9500 battery,

    Comment by baterry — November 20, 2008 @ 11:26 am

  7. http://www.batteryfast.co.uk/acer/as07a41.php AS07A41 Aspire 4310 4520 4710 4920 laptop battery,

    Comment by baterry — November 26, 2008 @ 10:19 am

  8. Do you want to own some betteries which have more function , more economical and long life? Please visit the following Web site: http://www.adapterlist.com/hp/dv9100.htm hp dv9100 battery,it will help you find the ideal battery.

    Comment by laptop bettery — March 3, 2009 @ 5:25 am

  9. http://www.notebook-batteries.org/compaq-319411-001.htm compaq 319411-001 battery
    http://www.notebook-batteries.org/acer-aspire-3000.htm acer aspire 3000 battery
    http://www.notebook-batteries.org/dell-latitude-d610-series.htm dell latitude d610 series battery
    http://www.notebook-batteries.org/dell-inspiron-6000.htm dell inspiron 6000 battery
    http://www.notebook-batteries.org/hp-pavilion-dv1000-series.htm hp pavilion dv1000 series battery

    Comment by notebook-batteries.org — March 18, 2009 @ 4:58 am

RSS feed for comments on this post.

Leave a comment

Powered by WordPress