blob: 3dced355b6e8b38d89d1763c5926e03ca4ce20c0 [file] [log] [blame]
Dave Chapman56780e32007-06-16 22:32:57 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 *
11 * FAT32 formatting functions. Based on:
12 *
13 * Fat32 formatter version 1.03
14 * (c) Tom Thornhill 2005
15 * This software is covered by the GPL.
16 * By using this tool, you agree to absolve Ridgecrop of an liabilities for
17 * lost data.
18 * Please backup any data you value before using this tool.
19 *
20 *
21 * Modified June 2007 by Dave Chapman for use in ipodpatcher
22 *
23 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000024 * This program is free software; you can redistribute it and/or
25 * modify it under the terms of the GNU General Public License
26 * as published by the Free Software Foundation; either version 2
27 * of the License, or (at your option) any later version.
Dave Chapman56780e32007-06-16 22:32:57 +000028 *
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
31 *
32 ****************************************************************************/
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <stdbool.h>
38#include <stdint.h>
39
40#include "ipodio.h"
41
42static inline uint16_t swap16(uint16_t value)
43{
44 return (value >> 8) | (value << 8);
45}
46
47static inline uint32_t swap32(uint32_t value)
48{
49 uint32_t hi = swap16(value >> 16);
50 uint32_t lo = swap16(value & 0xffff);
51 return (lo << 16) | hi;
52}
53
54/* The following functions are not the most efficient, but are
55 self-contained and don't require needing to know endianness of CPU
56 at compile-time.
Dave Chapman71f08142009-08-04 20:03:17 +000057
58 Note that htole16/htole32 exist on some platforms, so for
59 simplicity we use different names.
60
Dave Chapman56780e32007-06-16 22:32:57 +000061*/
62
Dave Chapman71f08142009-08-04 20:03:17 +000063static uint16_t rb_htole16(uint16_t x)
Dave Chapman56780e32007-06-16 22:32:57 +000064{
65 uint16_t test = 0x1234;
66 unsigned char* p = (unsigned char*)&test;
67
68 if (p[0]==0x12) {
69 /* Big-endian */
70 return swap16(x);
71 } else {
72 return x;
73 }
74}
75
Dave Chapman71f08142009-08-04 20:03:17 +000076static uint32_t rb_htole32(uint32_t x)
Dave Chapman56780e32007-06-16 22:32:57 +000077{
78 uint32_t test = 0x12345678;
79 unsigned char* p = (unsigned char*)&test;
80
81 if (p[0]==0x12) {
82 /* Big-endian */
83 return swap32(x);
84 } else {
85 return x;
86 }
87}
88
89
90/* A large aligned buffer for disk I/O */
Dominik Riebelingd131a312008-06-17 17:52:13 +000091extern unsigned char* ipod_sectorbuf;
Dave Chapman56780e32007-06-16 22:32:57 +000092
93/* TODO: Pass these as parameters to the various create_ functions */
94
95/* can be zero for default or 1,2,4,8,16,32 or 64 */
96static int sectors_per_cluster = 0;
97
98/* Recommended values */
99static uint32_t ReservedSectCount = 32;
100static uint32_t NumFATs = 2;
101static uint32_t BackupBootSect = 6;
102static uint32_t VolumeId=0; /* calculated before format */
103
104/* Calculated later */
105static uint32_t FatSize=0;
106static uint32_t BytesPerSect=0;
107static uint32_t SectorsPerCluster=0;
108static uint32_t TotalSectors=0;
109static uint32_t SystemAreaSize=0;
110static uint32_t UserAreaSize=0;
111static uint8_t VolId[12] = "NO NAME ";
112
113
114struct FAT_BOOTSECTOR32
115{
116 /* Common fields. */
117 uint8_t sJmpBoot[3];
118 char sOEMName[8];
119 uint16_t wBytsPerSec;
120 uint8_t bSecPerClus;
121 uint16_t wRsvdSecCnt;
122 uint8_t bNumFATs;
123 uint16_t wRootEntCnt;
124 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */
125 uint8_t bMedia;
126 uint16_t wFATSz16;
127 uint16_t wSecPerTrk;
128 uint16_t wNumHeads;
129 uint32_t dHiddSec;
130 uint32_t dTotSec32;
131
132 /* Fat 32/16 only */
133 uint32_t dFATSz32;
134 uint16_t wExtFlags;
135 uint16_t wFSVer;
136 uint32_t dRootClus;
137 uint16_t wFSInfo;
138 uint16_t wBkBootSec;
139 uint8_t Reserved[12];
140 uint8_t bDrvNum;
141 uint8_t Reserved1;
142 uint8_t bBootSig; /* == 0x29 if next three fields are ok */
143 uint32_t dBS_VolID;
144 uint8_t sVolLab[11];
145 uint8_t sBS_FilSysType[8];
146} __attribute__((packed));
147
148struct FAT_FSINFO {
149 uint32_t dLeadSig; // 0x41615252
150 uint8_t sReserved1[480]; // zeros
151 uint32_t dStrucSig; // 0x61417272
152 uint32_t dFree_Count; // 0xFFFFFFFF
153 uint32_t dNxt_Free; // 0xFFFFFFFF
154 uint8_t sReserved2[12]; // zeros
155 uint32_t dTrailSig; // 0xAA550000
156} __attribute__((packed));
157
158
159/* Write "count" zero sectors, starting at sector "sector" */
160static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count)
161{
162 int n;
163
164 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) {
165 fprintf(stderr,"[ERR] Seek failed\n");
166 return -1;
167 }
168
Dominik Riebelingd131a312008-06-17 17:52:13 +0000169 memset(ipod_sectorbuf, 0, 128 * ipod->sector_size);
Dave Chapman56780e32007-06-16 22:32:57 +0000170
171 /* Write 128 sectors at a time */
172 while (count) {
173 if (count >= 128)
174 n = 128;
175 else
176 n = count;
177
Dominik Riebelingd131a312008-06-17 17:52:13 +0000178 if (ipod_write(ipod,ipod_sectorbuf,n * ipod->sector_size) < 0) {
Dave Chapman56780e32007-06-16 22:32:57 +0000179 perror("[ERR] Write failed in zero_sectors\n");
180 return -1;
181 }
182
183 count -= n;
184 }
185
186 return 0;
187}
188
189
190/*
19128.2 CALCULATING THE VOLUME SERIAL NUMBER
192
193For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94
194seconds. DOS takes the date and time just before it writes it to the
195disk.
196
197Low order word is calculated: Volume Serial Number is:
198 Month & Day 12/26 0c1ah
199 Sec & Hundrenths 41:94 295eh 3578:1d02
200 -----
201 3578h
202
203High order word is calculated:
204 Hours & Minutes 21:55 1537h
205 Year 1995 07cbh
206 -----
207 1d02h
208*/
209static uint32_t get_volume_id ( )
210{
211 /* TODO */
212#if 0
213 SYSTEMTIME s;
214 uint32_t d;
215 uint16_t lo,hi,tmp;
216
217 GetLocalTime( &s );
218
219 lo = s.wDay + ( s.wMonth << 8 );
220 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 );
221 lo += tmp;
222
223 hi = s.wMinute + ( s.wHour << 8 );
224 hi += s.wYear;
225
226 d = lo + (hi << 16);
227 return(d);
228#endif
229 return(0);
230}
231
232/*
233This is the Microsoft calculation from FATGEN
234
235 uint32_t RootDirSectors = 0;
236 uint32_t TmpVal1, TmpVal2, FATSz;
237
238 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors);
239 TmpVal2 = (256 * SecPerClus) + NumFATs;
240 TmpVal2 = TmpVal2 / 2;
241 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
242
243 return( FatSz );
244*/
245
246
247static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt,
248 uint32_t SecPerClus, uint32_t NumFATs,
249 uint32_t BytesPerSect)
250{
251 uint64_t Numerator, Denominator;
252 uint64_t FatElementSize = 4;
253 uint64_t FatSz;
254
255 /* This is based on
256 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html
257 I've made the obvious changes for FAT32
258 */
259
260 Numerator = FatElementSize * ( DskSize - ReservedSecCnt );
261 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs );
262 FatSz = Numerator / Denominator;
263
264 /* round up */
265 FatSz += 1;
266
267 return((uint32_t)FatSz);
268}
269
270static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect)
271{
272 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect;
273 return( (uint8_t) spc );
274}
275
276static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors,
277 uint32_t BytesPerSect)
278{
279 uint8_t ret = 0x01; /* 1 sector per cluster */
280 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect;
281 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 );
282
283 /* 512 MB to 8,191 MB 4 KB */
284 if ( DiskSizeMB > 512 )
285 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */
286
287 /* 8,192 MB to 16,383 MB 8 KB */
288 if ( DiskSizeMB > 8192 )
289 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */
290
291 /* 16,384 MB to 32,767 MB 16 KB */
292 if ( DiskSizeMB > 16384 )
293 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */
294
295 /* Larger than 32,768 MB 32 KB */
296 if ( DiskSizeMB > 32768 )
297 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */
298
299 return( ret );
300
301}
302
303static void create_boot_sector(unsigned char* buf,
304 struct ipod_t* ipod, int partition)
305{
306 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf;
307
308 /* fill out the boot sector and fs info */
309 pFAT32BootSect->sJmpBoot[0]=0xEB;
310 pFAT32BootSect->sJmpBoot[1]=0x5A;
311 pFAT32BootSect->sJmpBoot[2]=0x90;
312 strcpy( pFAT32BootSect->sOEMName, "MSWIN4.1" );
Dave Chapman71f08142009-08-04 20:03:17 +0000313 pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect);
Dave Chapman56780e32007-06-16 22:32:57 +0000314 pFAT32BootSect->bSecPerClus = SectorsPerCluster ;
Dave Chapman71f08142009-08-04 20:03:17 +0000315 pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount);
Dave Chapman56780e32007-06-16 22:32:57 +0000316 pFAT32BootSect->bNumFATs = NumFATs;
Dave Chapman71f08142009-08-04 20:03:17 +0000317 pFAT32BootSect->wRootEntCnt = rb_htole16(0);
318 pFAT32BootSect->wTotSec16 = rb_htole16(0);
Dave Chapman56780e32007-06-16 22:32:57 +0000319 pFAT32BootSect->bMedia = 0xF8;
Dave Chapman71f08142009-08-04 20:03:17 +0000320 pFAT32BootSect->wFATSz16 = rb_htole16(0);
321 pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track);
322 pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads);
323 pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start);
324 pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors);
325 pFAT32BootSect->dFATSz32 = rb_htole32(FatSize);
326 pFAT32BootSect->wExtFlags = rb_htole16(0);
327 pFAT32BootSect->wFSVer = rb_htole16(0);
328 pFAT32BootSect->dRootClus = rb_htole32(2);
329 pFAT32BootSect->wFSInfo = rb_htole16(1);
330 pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect);
Dave Chapman56780e32007-06-16 22:32:57 +0000331 pFAT32BootSect->bDrvNum = 0x80;
332 pFAT32BootSect->Reserved1 = 0;
333 pFAT32BootSect->bBootSig = 0x29;
Dave Chapman71f08142009-08-04 20:03:17 +0000334 pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId);
Dave Chapman56780e32007-06-16 22:32:57 +0000335 memcpy(pFAT32BootSect->sVolLab, VolId, 11);
336 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 );
337
338 buf[510] = 0x55;
339 buf[511] = 0xaa;
340}
341
Dave Chapman2cc80f52007-07-29 21:19:14 +0000342static void create_fsinfo(unsigned char* buf)
Dave Chapman56780e32007-06-16 22:32:57 +0000343{
344 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf;
345
346 /* FSInfo sect */
Dave Chapman71f08142009-08-04 20:03:17 +0000347 pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252);
348 pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272);
349 pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1);
350 pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1);
351 pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000);
352 pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1);
Dave Chapman56780e32007-06-16 22:32:57 +0000353
354 /* clusters 0-1 reserved, we used cluster 2 for the root dir */
Dave Chapman71f08142009-08-04 20:03:17 +0000355 pFAT32FsInfo->dNxt_Free = rb_htole32(3);
Dave Chapman56780e32007-06-16 22:32:57 +0000356}
357
Dave Chapman2cc80f52007-07-29 21:19:14 +0000358static void create_firstfatsector(unsigned char* buf)
Dave Chapman56780e32007-06-16 22:32:57 +0000359{
360 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */
361
362 /* First FAT Sector */
Dave Chapman71f08142009-08-04 20:03:17 +0000363 p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */
364 p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */
365 p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */
Dave Chapman56780e32007-06-16 22:32:57 +0000366}
367
368int format_partition(struct ipod_t* ipod, int partition)
369{
370 uint32_t i;
371 uint64_t qTotalSectors=0;
372 uint64_t FatNeeded;
373
374 VolumeId = get_volume_id( );
375
376 /* Only support hard disks at the moment */
377 if ( ipod->sector_size != 512 )
378 {
379 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n");
380 return -1;
381 }
382 BytesPerSect = ipod->sector_size;
383
384 /* Checks on Disk Size */
385 qTotalSectors = ipod->pinfo[partition].size;
386
387 /* low end limit - 65536 sectors */
388 if ( qTotalSectors < 65536 )
389 {
390 /* I suspect that most FAT32 implementations would mount this
391 volume just fine, but the spec says that we shouldn't do
392 this, so we won't */
393
394 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" );
395 return -1;
396 }
397
398 if ( qTotalSectors >= 0xffffffff )
399 {
400 /* This is a more fundamental limitation on FAT32 - the total
Nicolas Pennequin367aec32008-05-05 10:49:31 +0000401 sector count in the root dir is 32bit. With a bit of
Dave Chapman56780e32007-06-16 22:32:57 +0000402 creativity, FAT32 could be extended to handle at least 2^28
403 clusters There would need to be an extra field in the
404 FSInfo sector, and the old sector count could be set to
405 0xffffffff. This is non standard though, the Windows FAT
406 driver FASTFAT.SYS won't understand this. Perhaps a future
407 version of FAT32 and FASTFAT will handle this. */
408
409 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n");
410 }
411
412 if ( sectors_per_cluster ) {
413 SectorsPerCluster = sectors_per_cluster;
414 } else {
415 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size,
416 BytesPerSect );
417 }
418
419 TotalSectors = (uint32_t) qTotalSectors;
420
421 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount,
422 SectorsPerCluster, NumFATs, BytesPerSect );
423
424 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize);
425
426 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */
427 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster);
428
429 /* Work out the Cluster count */
430 FatNeeded = UserAreaSize/SectorsPerCluster;
431
432 /* check for a cluster count of >2^28, since the upper 4 bits of
433 the cluster values in the FAT are reserved. */
434 if (FatNeeded > 0x0FFFFFFF) {
435 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" );
436 return -1;
437 }
438
439 /* Sanity check, make sure the fat is big enough.
440 Convert the cluster count into a Fat sector count, and check
441 the fat size value we calculated earlier is OK. */
442
443 FatNeeded *=4;
444 FatNeeded += (BytesPerSect-1);
445 FatNeeded /= BytesPerSect;
446
447 if ( FatNeeded > FatSize ) {
448 fprintf(stderr,"[ERR] Drive too big to format\n");
449 return -1;
450 }
451
452 /*
453 Write boot sector, fats
454 Sector 0 Boot Sector
455 Sector 1 FSInfo
456 Sector 2 More boot code - we write zeros here
457 Sector 3 unused
458 Sector 4 unused
459 Sector 5 unused
460 Sector 6 Backup boot sector
461 Sector 7 Backup FSInfo sector
462 Sector 8 Backup 'more boot code'
463 zero'd sectors upto ReservedSectCount
464 FAT1 ReservedSectCount to ReservedSectCount + FatSize
465 ...
466 FATn ReservedSectCount to ReservedSectCount + FatSize
467 RootDir - allocated to cluster2
468 */
469
470 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track);
471 fprintf(stderr,"[INFO] Size : %lluGB %u sectors\n", ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors );
472 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect );
473 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff );
474 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs );
475 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster );
476
477 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition);
478
479 /* Once zero_sectors has run, any data on the drive is basically lost... */
480 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize );
481
482 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize);
483
484 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" );
485
486 /* Create the boot sector structure */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000487 create_boot_sector(ipod_sectorbuf, ipod, partition);
488 create_fsinfo(ipod_sectorbuf + 512);
Dave Chapman56780e32007-06-16 22:32:57 +0000489
490 /* Write boot sector and fsinfo at start of partition */
491 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) {
492 fprintf(stderr,"[ERR] Seek failed\n");
493 return -1;
494 }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000495 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
Dave Chapman56780e32007-06-16 22:32:57 +0000496 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
497 return -1;
498 }
499
500 /* Write backup copy of boot sector and fsinfo */
501 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) {
502 fprintf(stderr,"[ERR] Seek failed\n");
503 return -1;
504 }
Dominik Riebelingd131a312008-06-17 17:52:13 +0000505 if (ipod_write(ipod,ipod_sectorbuf,512 * 2) < 0) {
Dave Chapman56780e32007-06-16 22:32:57 +0000506 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
507 return -1;
508 }
509
510 /* Create the first FAT sector */
Dominik Riebelingd131a312008-06-17 17:52:13 +0000511 create_firstfatsector(ipod_sectorbuf);
Dave Chapman56780e32007-06-16 22:32:57 +0000512
513 /* Write the first fat sector in the right places */
514 for ( i=0; i<NumFATs; i++ ) {
515 int SectorStart = ReservedSectCount + (i * FatSize );
516
517 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) {
518 fprintf(stderr,"[ERR] Seek failed\n");
519 return -1;
520 }
521
Dominik Riebelingd131a312008-06-17 17:52:13 +0000522 if (ipod_write(ipod,ipod_sectorbuf,512) < 0) {
Dave Chapman56780e32007-06-16 22:32:57 +0000523 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n");
524 return -1;
525 }
526 }
527
528 fprintf(stderr,"[INFO] Format successful\n");
529
530 return 0;
531}