]> git.ozlabs.org Git - yaboot.git/blobdiff - second/partition.c
Double block_buffer
[yaboot.git] / second / partition.c
index c8663497927b04dc851a190fb5c3ae518be0a3b9..1ade1b671f4e422b28546407ecfc5aace2a2ebe9 100644 (file)
@@ -1,6 +1,10 @@
 /*
  *  partition.c - partition table support
  *
+ *  Copyright (C) 2013 Dinar Valeev
+ *
+ *  Copyright (C) 2004 Sven Luther
+ *
  *  Copyright (C) 2001, 2002 Ethan Benson
  *
  *  Copyright (C) 1999 Benjamin Herrenschmidt
 #include "stdlib.h"
 #include "mac-part.h"
 #include "fdisk-part.h"
+#include "amiga-part.h"
+#include "gpt-part.h"
 #include "partition.h"
 #include "prom.h"
 #include "string.h"
 #include "linux/iso_fs.h"
 #include "debug.h"
 #include "errors.h"
+#include "bootinfo.h"
+#include "byteorder.h"
 
 /* We currently don't check the partition type, some users
  * are putting crap there and still expect it to work...
@@ -53,28 +61,26 @@ static const char *valid_mac_partition_types[] = {
      NULL
 };
 #endif
-    
 
-/* Local functions */
-static unsigned long swab32(unsigned long value);
 
-#define MAX_BLOCK_SIZE 2048
-static unsigned char block_buffer[MAX_BLOCK_SIZE];
+#define MAX_BLOCK_SIZE 4096
+static unsigned char block_buffer[MAX_BLOCK_SIZE * 2];
 
 static void
 add_new_partition(struct partition_t** list, int part_number, const char *part_type,
-                 const char *part_name, unsigned long part_start, unsigned long part_size,
-                 unsigned short part_blocksize)
+                 const char *part_name, unsigned long long part_start, unsigned long long part_size,
+                 unsigned short part_blocksize, int sys_ind)
 {
      struct partition_t*       part;
      part = (struct partition_t*)malloc(sizeof(struct partition_t));
-       
+
      part->part_number = part_number;
      strncpy(part->part_type, part_type, MAX_PART_NAME);
      strncpy(part->part_name, part_name, MAX_PART_NAME);
      part->part_start = part_start;
      part->part_size = part_size;
      part->blocksize = part_blocksize;
+     part->sys_ind = sys_ind;
 
      /* Tack this entry onto the list */
      part->next = *list;
@@ -95,7 +101,7 @@ partition_mac_lookup( const char *dev_name, prom_handle disk,
      struct mac_partition* part = (struct mac_partition *)block_buffer;
      unsigned short ptable_block_size =
          ((struct mac_driver_desc *)block_buffer)->block_size;
-       
+
      map_size = 1;
      for (block=1; block < map_size + 1; block++)
      {
@@ -115,9 +121,9 @@ partition_mac_lookup( const char *dev_name, prom_handle disk,
          }
          if (block == 1)
               map_size = part->map_count;
-               
+
 #ifdef CHECK_FOR_VALID_MAC_PARTITION_TYPE
-         /* We don't bother looking at swap partitions of any type, 
+         /* We don't bother looking at swap partitions of any type,
           * and the rest are the ones we know about */
          for (ptype = valid_mac_partition_types; ptype; ptype++)
               if (!strcmp (part->type, ptype))
@@ -146,11 +152,12 @@ partition_mac_lookup( const char *dev_name, prom_handle disk,
                    part->name, /* name */
                    part->start_block + part->data_start, /* start */
                    part->data_count, /* size */
-                   ptable_block_size );
+                   ptable_block_size,
+                   0);
      }
 }
 
-/* 
+/*
  * Same function as partition_mac_lookup(), except for fdisk
  * partitioned disks.
  */
@@ -163,22 +170,58 @@ partition_fdisk_lookup( const char *dev_name, prom_handle disk,
      /* fdisk partition tables start at offset 0x1be
       * from byte 0 of the boot drive.
       */
-     struct fdisk_partition* part = 
+     struct fdisk_partition* part =
          (struct fdisk_partition *) (block_buffer + 0x1be);
 
      for (partition=1; partition <= 4 ;partition++, part++) {
-         if (part->sys_ind == LINUX_NATIVE) {
+         if (part->sys_ind == LINUX_NATIVE || part->sys_ind == LINUX_RAID) {
               add_new_partition(  list,
                                   partition,
                                   "Linux", /* type */
                                   '\0', /* name */
-                                  swab32(*(unsigned int *)(part->start4)),
-                                  swab32(*(unsigned int *)(part->size4)),
-                                  512 /*blksize*/ );
+                                  le32_to_cpu(*(unsigned int *)part->start4),
+                                  le32_to_cpu(*(unsigned int *)part->size4),
+                                  512 /*blksize*/,
+                                  part->sys_ind /* partition type */ );
          }
      }
 }
 
+/* GPT partition format */
+static int gpt_magic_present(unsigned char *block_buffer, unsigned int prom_blksize)
+{
+       struct gpt_header *header = (struct gpt_header *)(block_buffer + prom_blksize);
+       return cpu_to_le64(header->signature) == GPT_HEADER_SIGNATURE;
+}
+
+static inline int
+guid_cmp (gpt_part_type_t left, gpt_part_type_t right)
+{
+       return memcmp(&left, &right, sizeof (gpt_part_type_t));
+}
+
+static void partition_gpt_lookup(prom_handle disk, unsigned int prom_blksize,
+                                                       struct partition_t **list)
+{
+       int partition;
+       struct gpt_header *header = (struct gpt_header *)(block_buffer + prom_blksize);
+
+       if (prom_readblocks(disk, cpu_to_le64(header->partitions), 1, block_buffer) != 1) {
+               prom_printf("Can't read GPT partition table %Lu\n", header->partitions);
+               return;
+       }
+       struct gpt_partition *part = (struct gpt_partition *)(block_buffer);
+
+       for (partition = 1; partition <= cpu_to_le32(header->maxpart); partition++, part++) {
+               if ((!guid_cmp(part->type, GPT_BASIC_DATA))||(!guid_cmp(part->type, GPT_LINUX_NATIVE))||(!guid_cmp(part->type, GPT_LINUX_RAID))) {
+                       add_new_partition(list, partition, "Linux", 0, le64_to_cpu(part->start), \
+                                               cpu_to_le64(part->end) - cpu_to_le64(part->start) + 1, \
+                                               prom_blksize, 1);
+               }
+       }
+}
+
+
 /* I don't know if it's possible to handle multisession and other multitrack
  * stuffs with the current OF disklabel package. This can still be implemented
  * with direct calls to atapi stuffs.
@@ -197,11 +240,11 @@ identify_iso_fs(ihandle device, unsigned int *iso_root_block)
               prom_printf("Can't read volume desc block %d\n", block);
               break;
          }
-               
+
          vdp = (struct iso_volume_descriptor *)block_buffer;
-           
-         /* Due to the overlapping physical location of the descriptors, 
-          * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure 
+
+         /* Due to the overlapping physical location of the descriptors,
+          * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure
           * proper identification in this case, we first check for ISO.
           */
          if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
@@ -209,10 +252,121 @@ identify_iso_fs(ihandle device, unsigned int *iso_root_block)
               return 1;
          }
      }
-       
+
      return 0;
 }
 
+/*
+ * Detects and read amiga partition tables.
+ */
+
+static int
+_amiga_checksum (unsigned int blk_size)
+{
+       unsigned int sum;
+       int i, end;
+       unsigned int *amiga_block = (unsigned int *) block_buffer;
+
+       sum = amiga_block[0];
+       end = amiga_block[AMIGA_LENGTH];
+
+       if (end > blk_size) end = blk_size;
+
+       for (i = 1; i < end; i++) sum += amiga_block[i];
+
+       return sum;
+}
+
+static int
+_amiga_find_rdb (const char *dev_name, prom_handle disk, unsigned int prom_blksize)
+{
+       int i;
+       unsigned int *amiga_block = (unsigned int *) block_buffer;
+
+       for (i = 0; i<AMIGA_RDB_MAX; i++) {
+               if (i != 0) {
+                       if (prom_readblocks(disk, i, 1, block_buffer) != 1) {
+                               prom_printf("Can't read boot block %d\n", i);
+                               break;
+                       }
+               }
+               if ((amiga_block[AMIGA_ID] == AMIGA_ID_RDB) && (_amiga_checksum (prom_blksize) == 0))
+                       return 1;
+       }
+       /* Amiga partition table not found, let's reread block 0 */
+       if (prom_readblocks(disk, 0, 1, block_buffer) != 1) {
+               prom_printf("Can't read boot blocks\n");
+               return 0; /* TODO: something bad happened, should fail more verbosely */
+       }
+       return 0;
+}
+
+static void
+partition_amiga_lookup( const char *dev_name, prom_handle disk,
+                        unsigned int prom_blksize, struct partition_t** list )
+{
+       int partition, part;
+       unsigned int blockspercyl;
+       unsigned int *amiga_block = (unsigned int *) block_buffer;
+       unsigned int *used = NULL;
+       unsigned int possible;
+       int checksum;
+       int i;
+
+       blockspercyl = amiga_block[AMIGA_SECT] * amiga_block[AMIGA_HEADS];
+       possible = amiga_block[AMIGA_RDBLIMIT]/32 +1;
+
+       used = (unsigned int *) malloc (sizeof (unsigned int) * (possible + 1));
+       if (!used) {
+               prom_printf("Can't allocate memory\n");
+               return;
+       }
+
+       for (i=0; i < possible; i++) used[i] = 0;
+
+
+       for (part = amiga_block[AMIGA_PARTITIONS], partition = 1;
+               part != AMIGA_END;
+               part = amiga_block[AMIGA_PART_NEXT], partition++)
+       {
+               if (prom_readblocks(disk, part, 1, block_buffer) != 1) {
+                       prom_printf("Can't read partition block %d\n", part);
+                       break;
+               }
+               checksum = _amiga_checksum (prom_blksize);
+               if ((amiga_block[AMIGA_ID] == AMIGA_ID_PART) &&
+                       (checksum == 0) &&
+                       ((used[part/32] & (0x1 << (part % 32))) == 0))
+               {
+                       used[part/32] |= (0x1 << (part % 32));
+               } else {
+                       prom_printf("Amiga partition table corrupted at block %d\n", part);
+                       if (amiga_block[AMIGA_ID] != AMIGA_ID_PART)
+                               prom_printf ("block type is not partition but %08x\n", amiga_block[AMIGA_ID]);
+                       if (checksum != 0)
+                               prom_printf ("block checsum is bad : %d\n", checksum);
+                       if ((used[part/32] & (0x1 << (part % 32))) != 0)
+                               prom_printf ("partition table is looping, block %d already traveled\n", part);
+                       break;
+               }
+
+              /* We use the partition block size from the partition table.
+               * The filesystem implmentations are responsible for mapping
+               * to their own fs blocksize */
+              add_new_partition(
+                   list, /* partition list */
+                   partition, /* partition number */
+                   "Linux", /* type */
+                   '\0', /* name */
+                   blockspercyl * amiga_block[AMIGA_PART_LOWCYL], /* start */
+                   blockspercyl * (amiga_block[AMIGA_PART_HIGHCYL] - amiga_block[AMIGA_PART_LOWCYL] + 1), /* size */
+                   prom_blksize,
+                   0 );
+       }
+       if (used)
+               free(used);
+}
+
 struct partition_t*
 partitions_lookup(const char *device)
 {
@@ -220,12 +374,13 @@ partitions_lookup(const char *device)
      struct mac_driver_desc *desc = (struct mac_driver_desc *)block_buffer;
      struct partition_t* list = NULL;
      unsigned int prom_blksize, iso_root_block;
-       
-     strncpy(block_buffer, device, 2040);
-     strcat(block_buffer, ":0");
-       
+
+     strncpy((char *)block_buffer, device, 2040);
+     if (_machine != _MACH_bplan)
+         strcat((char *)block_buffer, ":0");
+
      /* Open device */
-     disk = prom_open(block_buffer);
+     disk = prom_open((char *)block_buffer);
      if (disk == NULL) {
          prom_printf("Can't open device <%s>\n", block_buffer);
          goto bail;
@@ -239,15 +394,18 @@ partitions_lookup(const char *device)
          prom_printf("block_size %d not supported !\n", prom_blksize);
          goto bail;
      }
-       
-     /* Read boot blocs */
-     if (prom_readblocks(disk, 0, 1, block_buffer) != 1) {
+
+     /* Read boot blocks */
+     if (prom_readblocks(disk, 0, 2, block_buffer) != 1) {
          prom_printf("Can't read boot blocks\n");
          goto bail;
-     } 
+     }
      if (desc->signature == MAC_DRIVER_MAGIC) {
          /* pdisk partition format */
          partition_mac_lookup(device, disk, prom_blksize, &list);
+     } else if (gpt_magic_present(block_buffer, prom_blksize)) {
+         /* gpt partition format */
+         partition_gpt_lookup(disk, prom_blksize, &list);
      } else if ((block_buffer[510] == 0x55) && (block_buffer[511] == 0xaa)) {
          /* fdisk partition format */
          partition_fdisk_lookup(device, disk, prom_blksize, &list);
@@ -258,8 +416,12 @@ partitions_lookup(const char *device)
                            '\0',
                            iso_root_block,
                            0,
-                           prom_blksize);
+                           prom_blksize,
+                           0);
          prom_printf("ISO9660 disk\n");
+     } else if (_amiga_find_rdb(device, disk, prom_blksize) != -1) {
+         /* amiga partition format */
+         partition_amiga_lookup(device, disk, prom_blksize, &list);
      } else {
          prom_printf("No supported partition table detected\n");
          goto bail;
@@ -267,7 +429,7 @@ partitions_lookup(const char *device)
 
 bail:
      prom_close(disk);
-       
+
      return list;
 }
 
@@ -279,7 +441,8 @@ get_part_type(char *device, int partition)
      struct partition_t*       found;
      char *type = NULL;
 
-     if (prom_get_devtype(device) != FILE_DEVICE_BLOCK)
+     int device_kind = prom_get_devtype(device);
+     if (device_kind != FILE_DEVICE_BLOCK && device_kind != FILE_DEVICE_ISCSI)
          return NULL;
 
      parts = partitions_lookup(device);
@@ -289,12 +452,12 @@ get_part_type(char *device, int partition)
          return '\0';
 
      for (p = parts; p && !found; p=p->next) {
-         DEBUG_F("number: %02d, start: 0x%08lx, length: 0x%08lx, type: %s, name: %s\n",
+         DEBUG_F("number: %02d, start: 0x%08Lx, length: 0x%08Lx, type: %s, name: %s\n",
                  p->part_number, p->part_start, p->part_size, p->part_type, p->part_name);
          if ((partition >= 0) && (partition == p->part_number)) {
               type = strdup(p->part_type);
               break;
-         }       
+         }
      }
      if (parts)
          partitions_free(parts);
@@ -306,28 +469,16 @@ void
 partitions_free(struct partition_t* list)
 {
      struct partition_t*       next;
-       
+
      while(list) {
          next = list->next;
          free(list);
          list = next;
      }
 }
-unsigned long
-swab32(unsigned long value)
-{
-     __u32 result;
-
-     __asm__("rlwimi %0,%1,24,16,23\n\t"
-            "rlwimi %0,%1,8,8,15\n\t"
-            "rlwimi %0,%1,24,0,7"
-            : "=r" (result)
-            : "r" (value), "0" (value >> 24));
-     return result;
-}
 
 
-/* 
+/*
  * Local variables:
  * c-file-style: "k&r"
  * c-basic-offset: 5