handle RAID partitions on x86 partition tables
[yaboot.git] / second / partition.c
1 /*
2  *  partition.c - partition table support
3  *
4  *  Copyright (C) 2004 Sven Luther
5  *
6  *  Copyright (C) 2001, 2002 Ethan Benson
7  *
8  *  Copyright (C) 1999 Benjamin Herrenschmidt
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  *  Todo: Add disklabel (or let OF do it ?). Eventually think about
27  *        fixing CDROM handling by directly using the ATAPI layer.
28  */
29
30 #include "ctype.h"
31 #include "types.h"
32 #include "stddef.h"
33 #include "stdlib.h"
34 #include "mac-part.h"
35 #include "fdisk-part.h"
36 #include "amiga-part.h"
37 #include "partition.h"
38 #include "prom.h"
39 #include "string.h"
40 #include "linux/iso_fs.h"
41 #include "debug.h"
42 #include "errors.h"
43
44 /* We currently don't check the partition type, some users
45  * are putting crap there and still expect it to work...
46  */
47 #undef CHECK_FOR_VALID_MAC_PARTITION_TYPE
48
49 #ifdef CHECK_FOR_VALID_MAC_PARTITION_TYPE
50 static const char *valid_mac_partition_types[] = {
51      "apple_unix_svr2",
52      "linux",
53      "apple_hfs",
54      "apple_boot",
55      "apple_bootstrap",
56      NULL
57 };
58 #endif
59     
60
61 /* Local functions */
62 static unsigned long swab32(unsigned long value);
63
64 #define MAX_BLOCK_SIZE  2048
65 static unsigned char block_buffer[MAX_BLOCK_SIZE];
66
67 static void
68 add_new_partition(struct partition_t**  list, int part_number, const char *part_type,
69                   const char *part_name, unsigned long part_start, unsigned long part_size,
70                   unsigned short part_blocksize, int sys_ind)
71 {
72      struct partition_t*        part;
73      part = (struct partition_t*)malloc(sizeof(struct partition_t));
74         
75      part->part_number = part_number;
76      strncpy(part->part_type, part_type, MAX_PART_NAME);
77      strncpy(part->part_name, part_name, MAX_PART_NAME);
78      part->part_start = part_start;
79      part->part_size = part_size;
80      part->blocksize = part_blocksize;
81      part->sys_ind = sys_ind;
82
83      /* Tack this entry onto the list */
84      part->next = *list;
85      *list = part;
86 }
87
88 /* Note, we rely on partitions being dev-block-size aligned,
89  * I have to check if it's true. If it's not, then things will get
90  * a bit more complicated
91  */
92 static void
93 partition_mac_lookup( const char *dev_name, prom_handle disk,
94                       unsigned int prom_blksize, struct partition_t** list )
95 {
96      int block, map_size;
97
98      /* block_buffer contains block 0 from the partitions_lookup() stage */
99      struct mac_partition* part = (struct mac_partition *)block_buffer;
100      unsigned short ptable_block_size =
101           ((struct mac_driver_desc *)block_buffer)->block_size;
102         
103      map_size = 1;
104      for (block=1; block < map_size + 1; block++)
105      {
106 #ifdef CHECK_FOR_VALID_MAC_PARTITION_TYPE
107           int valid = 0;
108           const char *ptype;
109 #endif
110           if (prom_readblocks(disk, block, 1, block_buffer) != 1) {
111                prom_printf("Can't read partition %d\n", block);
112                break;
113           }
114           if (part->signature != MAC_PARTITION_MAGIC) {
115 #if 0
116                prom_printf("Wrong partition %d signature\n", block);
117 #endif
118                break;
119           }
120           if (block == 1)
121                map_size = part->map_count;
122                 
123 #ifdef CHECK_FOR_VALID_MAC_PARTITION_TYPE
124           /* We don't bother looking at swap partitions of any type, 
125            * and the rest are the ones we know about */
126           for (ptype = valid_mac_partition_types; ptype; ptype++)
127                if (!strcmp (part->type, ptype))
128                {
129                     valid = 1;
130                     break;
131                }
132 #if DEBUG
133           if (!valid)
134                prom_printf( "MAC: Unsupported partition #%d; type=%s\n",
135                             block, part->type );
136 #endif
137 #endif
138
139
140 #ifdef CHECK_FOR_VALID_MAC_PARTITION_TYPE
141           if (valid)
142 #endif
143                /* We use the partition block size from the partition table.
144                 * The filesystem implmentations are responsible for mapping
145                 * to their own fs blocksize */
146                add_new_partition(
147                     list, /* partition list */
148                     block, /* partition number */
149                     part->type, /* type */
150                     part->name, /* name */
151                     part->start_block + part->data_start, /* start */
152                     part->data_count, /* size */
153                     ptable_block_size,
154                     0);
155      }
156 }
157
158 /* 
159  * Same function as partition_mac_lookup(), except for fdisk
160  * partitioned disks.
161  */
162 static void
163 partition_fdisk_lookup( const char *dev_name, prom_handle disk,
164                         unsigned int prom_blksize, struct partition_t** list )
165 {
166      int partition;
167
168      /* fdisk partition tables start at offset 0x1be
169       * from byte 0 of the boot drive.
170       */
171      struct fdisk_partition* part = 
172           (struct fdisk_partition *) (block_buffer + 0x1be);
173
174      for (partition=1; partition <= 4 ;partition++, part++) {
175           if (part->sys_ind == LINUX_NATIVE || part->sys_ind == LINUX_RAID) {
176                add_new_partition(  list,
177                                    partition,
178                                    "Linux", /* type */
179                                    '\0', /* name */
180                                    swab32(*(unsigned int *)(part->start4)),
181                                    swab32(*(unsigned int *)(part->size4)),
182                                    512 /*blksize*/,
183                                    part->sys_ind /* partition type */ );
184           }
185      }
186 }
187
188 /* I don't know if it's possible to handle multisession and other multitrack
189  * stuffs with the current OF disklabel package. This can still be implemented
190  * with direct calls to atapi stuffs.
191  * Currently, we enter this code for any device of block size 0x2048 who lacks
192  * a MacOS partition map signature.
193  */
194 static int
195 identify_iso_fs(ihandle device, unsigned int *iso_root_block)
196 {
197      int block;
198
199      for (block = 16; block < 100; block++) {
200           struct iso_volume_descriptor  * vdp;
201
202           if (prom_readblocks(device, block, 1, block_buffer) != 1) {
203                prom_printf("Can't read volume desc block %d\n", block);
204                break;
205           }
206                 
207           vdp = (struct iso_volume_descriptor *)block_buffer;
208             
209           /* Due to the overlapping physical location of the descriptors, 
210            * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure 
211            * proper identification in this case, we first check for ISO.
212            */
213           if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
214                *iso_root_block = block;
215                return 1;
216           }
217      }
218         
219      return 0;
220 }
221
222 /* 
223  * Detects and read amiga partition tables.
224  */
225
226 static int
227 _amiga_checksum (unsigned int blk_size)
228 {
229         unsigned int sum;
230         int i, end;
231         unsigned int *amiga_block = (unsigned int *) block_buffer;
232
233         sum = amiga_block[0];
234         end = amiga_block[AMIGA_LENGTH];
235
236         if (end > blk_size) end = blk_size;
237
238         for (i = 1; i < end; i++) sum += amiga_block[i];
239
240         return sum;
241 }
242
243 static int
244 _amiga_find_rdb (const char *dev_name, prom_handle disk, unsigned int prom_blksize)
245 {
246         int i;
247         unsigned int *amiga_block = (unsigned int *) block_buffer;
248
249         for (i = 0; i<AMIGA_RDB_MAX; i++) {
250                 if (i != 0) {
251                         if (prom_readblocks(disk, i, 1, block_buffer) != 1) {
252                                 prom_printf("Can't read boot block %d\n", i);
253                                 break;
254                         }       
255                 }
256                 if ((amiga_block[AMIGA_ID] == AMIGA_ID_RDB) && (_amiga_checksum (prom_blksize) == 0))
257                         return 1;
258         }
259         /* Amiga partition table not found, let's reread block 0 */
260         if (prom_readblocks(disk, 0, 1, block_buffer) != 1) {
261                 prom_printf("Can't read boot blocks\n");
262                 return 0; /* TODO: something bad happened, should fail more verbosely */
263         }       
264         return 0;
265 }
266
267 static void
268 partition_amiga_lookup( const char *dev_name, prom_handle disk,
269                         unsigned int prom_blksize, struct partition_t** list )
270 {
271         int partition, part;
272         unsigned int blockspercyl;
273         unsigned int *amiga_block = (unsigned int *) block_buffer;
274         unsigned int *used = NULL;
275         unsigned int possible;
276         int checksum;
277         int i;
278
279         blockspercyl = amiga_block[AMIGA_SECT] * amiga_block[AMIGA_HEADS];
280         possible = amiga_block[AMIGA_RDBLIMIT]/32 +1;
281
282         used = (unsigned int *) malloc (sizeof (unsigned int) * (possible + 1));
283
284         for (i=0; i < possible; i++) used[i] = 0;
285
286
287         for (part = amiga_block[AMIGA_PARTITIONS], partition = 0;
288                 part != AMIGA_END;
289                 part = amiga_block[AMIGA_PART_NEXT], partition++)
290         {
291                 if (prom_readblocks(disk, part, 1, block_buffer) != 1) {
292                         prom_printf("Can't read partition block %d\n", part);
293                         break;
294                 }       
295                 checksum = _amiga_checksum (prom_blksize);
296                 if ((amiga_block[AMIGA_ID] == AMIGA_ID_PART) &&
297                         (checksum == 0) &&
298                         ((used[part/32] & (0x1 << (part % 32))) == 0))
299                 {
300                         used[part/32] |= (0x1 << (part % 32));
301                 } else {
302                         prom_printf("Amiga partition table corrupted at block %d\n", part);
303                         if (amiga_block[AMIGA_ID] != AMIGA_ID_PART)
304                                 prom_printf ("block type is not partition but %08x\n", amiga_block[AMIGA_ID]);
305                         if (checksum != 0)
306                                 prom_printf ("block checsum is bad : %d\n", checksum);
307                         if ((used[part/32] & (0x1 << (part % 32))) != 0)
308                                 prom_printf ("partition table is looping, block %d already traveled\n", part);
309                         break;
310                 }
311
312                /* We use the partition block size from the partition table.
313                 * The filesystem implmentations are responsible for mapping
314                 * to their own fs blocksize */
315                add_new_partition(
316                     list, /* partition list */
317                     partition, /* partition number */
318                     "Linux", /* type */
319                     '\0', /* name */
320                     blockspercyl * amiga_block[AMIGA_PART_LOWCYL], /* start */
321                     blockspercyl * (amiga_block[AMIGA_PART_HIGHCYL] - amiga_block[AMIGA_PART_LOWCYL] + 1), /* size */
322                     prom_blksize,
323                     0 );
324         }
325 }
326
327 struct partition_t*
328 partitions_lookup(const char *device)
329 {
330      ihandle    disk;
331      struct mac_driver_desc *desc = (struct mac_driver_desc *)block_buffer;
332      struct partition_t* list = NULL;
333      unsigned int prom_blksize, iso_root_block;
334         
335      strncpy(block_buffer, device, 2040);
336      strcat(block_buffer, ":0");
337         
338      /* Open device */
339      disk = prom_open(block_buffer);
340      if (disk == NULL) {
341           prom_printf("Can't open device <%s>\n", block_buffer);
342           goto bail;
343      }
344      prom_blksize = prom_getblksize(disk);
345      DEBUG_F("block size of device is %d\n", prom_blksize);
346
347      if (prom_blksize <= 1)
348           prom_blksize = 512;
349      if (prom_blksize > MAX_BLOCK_SIZE) {
350           prom_printf("block_size %d not supported !\n", prom_blksize);
351           goto bail;
352      }
353         
354      /* Read boot blocs */
355      if (prom_readblocks(disk, 0, 1, block_buffer) != 1) {
356           prom_printf("Can't read boot blocks\n");
357           goto bail;
358      }  
359      if (desc->signature == MAC_DRIVER_MAGIC) {
360           /* pdisk partition format */
361           partition_mac_lookup(device, disk, prom_blksize, &list);
362      } else if ((block_buffer[510] == 0x55) && (block_buffer[511] == 0xaa)) {
363           /* fdisk partition format */
364           partition_fdisk_lookup(device, disk, prom_blksize, &list);
365      } else if (prom_blksize == 2048 && identify_iso_fs(disk, &iso_root_block)) {
366           add_new_partition(&list,
367                             0,
368                             '\0',
369                             '\0',
370                             iso_root_block,
371                             0,
372                             prom_blksize,
373                             0);
374           prom_printf("ISO9660 disk\n");
375      } else if (_amiga_find_rdb(device, disk, prom_blksize) != -1) {
376           /* amiga partition format */
377           partition_amiga_lookup(device, disk, prom_blksize, &list);
378      } else {
379           prom_printf("No supported partition table detected\n");
380           goto bail;
381      }
382
383 bail:
384      prom_close(disk);
385         
386      return list;
387 }
388
389 char *
390 get_part_type(char *device, int partition)
391 {
392      struct partition_t*        parts;
393      struct partition_t*        p;
394      struct partition_t*        found;
395      char *type = NULL;
396
397      if (prom_get_devtype(device) != FILE_DEVICE_BLOCK)
398           return NULL;
399
400      parts = partitions_lookup(device);
401      found = NULL;
402
403      if (!parts)
404           return '\0';
405
406      for (p = parts; p && !found; p=p->next) {
407           DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx, type: %s, name: %s\n",
408                   p->part_number, p->part_start, p->part_size, p->part_type, p->part_name);
409           if ((partition >= 0) && (partition == p->part_number)) {
410                type = strdup(p->part_type);
411                break;
412           }       
413      }
414      if (parts)
415           partitions_free(parts);
416      return type;
417 }
418
419 /* Freed in reverse order of allocation to help malloc'ator */
420 void
421 partitions_free(struct partition_t* list)
422 {
423      struct partition_t*        next;
424         
425      while(list) {
426           next = list->next;
427           free(list);
428           list = next;
429      }
430 }
431 unsigned long
432 swab32(unsigned long value)
433 {
434      __u32 result;
435
436      __asm__("rlwimi %0,%1,24,16,23\n\t"
437              "rlwimi %0,%1,8,8,15\n\t"
438              "rlwimi %0,%1,24,0,7"
439              : "=r" (result)
440              : "r" (value), "0" (value >> 24));
441      return result;
442 }
443
444
445 /* 
446  * Local variables:
447  * c-file-style: "k&r"
448  * c-basic-offset: 5
449  * End:
450  */