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