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