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