| 1 | /* This file is part of the program psim. |
| 2 | |
| 3 | Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au> |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | This program is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with this program; if not, see <http://www.gnu.org/licenses/>. |
| 17 | |
| 18 | */ |
| 19 | |
| 20 | |
| 21 | #ifndef _PK_DISKLABEL_C_ |
| 22 | #define _PK_DISKLABEL_C_ |
| 23 | |
| 24 | #ifndef STATIC_INLINE_PK_DISKLABEL |
| 25 | #define STATIC_INLINE_PK_DISKLABEL STATIC_INLINE |
| 26 | #endif |
| 27 | |
| 28 | #include "device_table.h" |
| 29 | |
| 30 | #include "pk.h" |
| 31 | |
| 32 | #ifdef HAVE_STDLIB_H |
| 33 | #include <stdlib.h> |
| 34 | #endif |
| 35 | |
| 36 | |
| 37 | |
| 38 | /* PACKAGE |
| 39 | |
| 40 | disk-label - all knowing disk I/O package |
| 41 | |
| 42 | DESCRIPTION |
| 43 | |
| 44 | The disk-label package provides a generic interface to disk |
| 45 | devices. It uses the arguments specified when an instance is being |
| 46 | created to determine if the raw disk, a partition, or a file within |
| 47 | a partition should be opened. |
| 48 | |
| 49 | An instance create call to disk-label could result, for instance, |
| 50 | in the opening of a DOS file system contained within a dos |
| 51 | partition contained within a physical disk. |
| 52 | |
| 53 | */ |
| 54 | |
| 55 | /* taken from bfd/ppcboot.c by Michael Meissner */ |
| 56 | |
| 57 | /* PPCbug location structure */ |
| 58 | typedef struct ppcboot_location { |
| 59 | unsigned8 ind; |
| 60 | unsigned8 head; |
| 61 | unsigned8 sector; |
| 62 | unsigned8 cylinder; |
| 63 | } ppcboot_location_t; |
| 64 | |
| 65 | /* PPCbug partition table layout */ |
| 66 | typedef struct ppcboot_partition { |
| 67 | ppcboot_location_t partition_begin; /* partition begin */ |
| 68 | ppcboot_location_t partition_end; /* partition end */ |
| 69 | unsigned8 sector_begin[4]; /* 32-bit start RBA (zero-based), little endian */ |
| 70 | unsigned8 sector_length[4]; /* 32-bit RBA count (one-based), little endian */ |
| 71 | } ppcboot_partition_t; |
| 72 | |
| 73 | #if 0 |
| 74 | /* PPCbug boot layout. */ |
| 75 | typedef struct ppcboot_hdr { |
| 76 | unsigned8 pc_compatibility[446]; /* x86 instruction field */ |
| 77 | ppcboot_partition_t partition[4]; /* partition information */ |
| 78 | unsigned8 signature[2]; /* 0x55 and 0xaa */ |
| 79 | unsigned8 entry_offset[4]; /* entry point offset, little endian */ |
| 80 | unsigned8 length[4]; /* load image length, little endian */ |
| 81 | unsigned8 flags; /* flag field */ |
| 82 | unsigned8 os_id; /* OS_ID */ |
| 83 | char partition_name[32]; /* partition name */ |
| 84 | unsigned8 reserved1[470]; /* reserved */ |
| 85 | } ppcboot_hdr_t; |
| 86 | #endif |
| 87 | |
| 88 | |
| 89 | typedef struct _disklabel { |
| 90 | device_instance *parent; |
| 91 | device_instance *raw_disk; |
| 92 | unsigned_word pos; |
| 93 | unsigned_word sector_begin; |
| 94 | unsigned_word sector_length; |
| 95 | } disklabel; |
| 96 | |
| 97 | |
| 98 | static unsigned_word |
| 99 | sector2uw(unsigned8 s[4]) |
| 100 | { |
| 101 | return ((s[3] << 24) |
| 102 | + (s[2] << 16) |
| 103 | + (s[1] << 8) |
| 104 | + (s[0] << 0)); |
| 105 | } |
| 106 | |
| 107 | |
| 108 | static void |
| 109 | disklabel_delete(device_instance *instance) |
| 110 | { |
| 111 | disklabel *label = device_instance_data(instance); |
| 112 | device_instance_delete(label->raw_disk); |
| 113 | free(label); |
| 114 | } |
| 115 | |
| 116 | |
| 117 | static int |
| 118 | disklabel_read(device_instance *instance, |
| 119 | void *buf, |
| 120 | unsigned_word len) |
| 121 | { |
| 122 | disklabel *label = device_instance_data(instance); |
| 123 | int nr_read; |
| 124 | if (label->pos + len > label->sector_length) |
| 125 | len = label->sector_length - label->pos; |
| 126 | if (device_instance_seek(label->raw_disk, 0, |
| 127 | label->sector_begin + label->pos) < 0) |
| 128 | return -1; |
| 129 | nr_read = device_instance_read(label->raw_disk, buf, len); |
| 130 | if (nr_read > 0) |
| 131 | label->pos += nr_read; |
| 132 | return nr_read; |
| 133 | } |
| 134 | |
| 135 | static int |
| 136 | disklabel_write(device_instance *instance, |
| 137 | const void *buf, |
| 138 | unsigned_word len) |
| 139 | { |
| 140 | disklabel *label = device_instance_data(instance); |
| 141 | int nr_written; |
| 142 | if (label->pos + len > label->sector_length) |
| 143 | len = label->sector_length - label->pos; |
| 144 | if (device_instance_seek(label->raw_disk, 0, |
| 145 | label->sector_begin + label->pos) < 0) |
| 146 | return -1; |
| 147 | nr_written = device_instance_write(label->raw_disk, buf, len); |
| 148 | if (nr_written > 0) |
| 149 | label->pos += nr_written; |
| 150 | return nr_written; |
| 151 | } |
| 152 | |
| 153 | static int |
| 154 | disklabel_seek(device_instance *instance, |
| 155 | unsigned_word pos_hi, |
| 156 | unsigned_word pos_lo) |
| 157 | { |
| 158 | disklabel *label = device_instance_data(instance); |
| 159 | if (pos_lo >= label->sector_length || pos_hi != 0) |
| 160 | return -1; |
| 161 | label->pos = pos_lo; |
| 162 | return 0; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | static const device_instance_callbacks package_disklabel_callbacks = { |
| 167 | disklabel_delete, |
| 168 | disklabel_read, |
| 169 | disklabel_write, |
| 170 | disklabel_seek, |
| 171 | }; |
| 172 | |
| 173 | /* Reconize different types of boot block */ |
| 174 | |
| 175 | static int |
| 176 | block0_is_bpb(const unsigned8 block[]) |
| 177 | { |
| 178 | const char ebdic_ibma[] = { 0xc9, 0xc2, 0xd4, 0xc1 }; |
| 179 | /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ |
| 180 | /* can't start with IBMA */ |
| 181 | if (memcmp(block, ebdic_ibma, sizeof(ebdic_ibma)) == 0) |
| 182 | return 0; |
| 183 | /* must have LE 0xAA55 signature at offset 510 */ |
| 184 | if (block[511] != 0xAA && block[510] != 0x55) |
| 185 | return 0; |
| 186 | /* valid 16 bit LE bytes per sector - 256, 512, 1024 */ |
| 187 | if (block[11] != 0 |
| 188 | || (block[12] != 1 && block[12] != 2 && block[12] != 4)) |
| 189 | return 0; |
| 190 | /* nr fats is 1 or 2 */ |
| 191 | if (block[16] != 1 && block[16] != 2) |
| 192 | return 0; |
| 193 | return 1; |
| 194 | } |
| 195 | |
| 196 | |
| 197 | /* Verify that the device contains an ISO-9660 File system */ |
| 198 | |
| 199 | static int |
| 200 | is_iso9660(device_instance *raw_disk) |
| 201 | { |
| 202 | /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ |
| 203 | unsigned8 block[512]; |
| 204 | if (device_instance_seek(raw_disk, 0, 512 * 64) < 0) |
| 205 | return 0; |
| 206 | if (device_instance_read(raw_disk, block, sizeof(block)) != sizeof(block)) |
| 207 | return 0; |
| 208 | if (block[0] == 0x01 |
| 209 | && block[1] == 'C' |
| 210 | && block[2] == 'D' |
| 211 | && block[3] == '0' |
| 212 | && block[4] == '0' |
| 213 | && block[5] == '1') |
| 214 | return 1; |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | |
| 219 | /* Verify that the disk block contains a valid DOS partition table. |
| 220 | While we're at it have a look around for active partitions etc. |
| 221 | |
| 222 | Return 0: invalid |
| 223 | Return 1..4: valid, value returned is the first active partition |
| 224 | Return -1: no active partition */ |
| 225 | |
| 226 | static int |
| 227 | block0_is_fdisk(const unsigned8 block[]) |
| 228 | { |
| 229 | const int partition_type_fields[] = { 0, 0x1c2, 0x1d2, 0x1e2, 0x1f2 }; |
| 230 | const int partition_active_fields[] = { 0, 0x1be, 0x1ce, 0x1de, 0xee }; |
| 231 | int partition; |
| 232 | int active = -1; |
| 233 | /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ |
| 234 | /* must have LE 0xAA55 signature at offset 510 */ |
| 235 | if (block[511/*0x1ff*/] != 0xAA && block[510/*0x1fe*/] != 0x55) |
| 236 | return 0; |
| 237 | /* must contain valid partition types */ |
| 238 | for (partition = 1; partition <= 4 && active != 0; partition++) { |
| 239 | int partition_type = block[partition_type_fields[partition]]; |
| 240 | int is_active = block[partition_active_fields[partition]] == 0x80; |
| 241 | const char *type; |
| 242 | switch (partition_type) { |
| 243 | case 0x00: |
| 244 | type = "UNUSED"; |
| 245 | break; |
| 246 | case 0x01: |
| 247 | type = "FAT 12 File system"; |
| 248 | break; |
| 249 | case 0x04: |
| 250 | type = "FAT 16 File system"; |
| 251 | break; |
| 252 | case 0x05: |
| 253 | case 0x06: |
| 254 | type = "rejected - extended/chained partition not supported"; |
| 255 | active = 0; |
| 256 | break; |
| 257 | case 0x41: |
| 258 | type = "Single program image"; |
| 259 | break; |
| 260 | case 0x82: |
| 261 | type = "Solaris?"; |
| 262 | break; |
| 263 | case 0x96: |
| 264 | type = "ISO 9660 File system"; |
| 265 | break; |
| 266 | default: |
| 267 | type = "rejected - unknown type"; |
| 268 | active = 0; |
| 269 | break; |
| 270 | } |
| 271 | PTRACE(disklabel, ("partition %d of type 0x%02x - %s%s\n", |
| 272 | partition, |
| 273 | partition_type, |
| 274 | type, |
| 275 | is_active && active != 0 ? " (active)" : "")); |
| 276 | if (partition_type != 0 && is_active && active < 0) |
| 277 | active = partition; |
| 278 | } |
| 279 | return active; |
| 280 | } |
| 281 | |
| 282 | |
| 283 | /* Verify that block0 corresponds to a MAC disk */ |
| 284 | |
| 285 | static int |
| 286 | block0_is_mac_disk(const unsigned8 block[]) |
| 287 | { |
| 288 | /* ref PowerPC Microprocessor CHRP bindings 1.2b - page 47 */ |
| 289 | /* signature - BEx4552 at offset 0 */ |
| 290 | if (block[0] != 0x45 || block[1] != 0x52) |
| 291 | return 0; |
| 292 | return 1; |
| 293 | } |
| 294 | |
| 295 | |
| 296 | /* Open a logical disk/file */ |
| 297 | |
| 298 | device_instance * |
| 299 | pk_disklabel_create_instance(device_instance *raw_disk, |
| 300 | const char *args) |
| 301 | { |
| 302 | int partition; |
| 303 | char *filename; |
| 304 | |
| 305 | /* parse the arguments */ |
| 306 | if (args == NULL) { |
| 307 | partition = 0; |
| 308 | filename = NULL; |
| 309 | } |
| 310 | else { |
| 311 | partition = strtoul((char*)args, &filename, 0); |
| 312 | if (filename == args) |
| 313 | partition = -1; /* not specified */ |
| 314 | if (*filename == ',') |
| 315 | filename++; |
| 316 | if (*filename == '\0') |
| 317 | filename = NULL; /* easier */ |
| 318 | } |
| 319 | |
| 320 | if (partition == 0) { |
| 321 | /* select the raw disk */ |
| 322 | return raw_disk; |
| 323 | } |
| 324 | else { |
| 325 | unsigned8 boot_block[512]; |
| 326 | /* get the boot block for examination */ |
| 327 | if (device_instance_seek(raw_disk, 0, 0) < 0) |
| 328 | device_error(device_instance_device(raw_disk), |
| 329 | "Problem seeking on raw disk"); |
| 330 | if (device_instance_read(raw_disk, &boot_block, sizeof(boot_block)) |
| 331 | != sizeof(boot_block)) |
| 332 | device_error(device_instance_device(raw_disk), "Problem reading boot block"); |
| 333 | |
| 334 | if (partition < 0) { |
| 335 | /* select the active partition */ |
| 336 | if (block0_is_bpb(boot_block)) { |
| 337 | device_error(device_instance_device(raw_disk), "Unimplemented active BPB"); |
| 338 | } |
| 339 | else if (block0_is_fdisk(boot_block)) { |
| 340 | int active = block0_is_fdisk(boot_block); |
| 341 | device_error(device_instance_device(raw_disk), "Unimplemented active FDISK (%d)", |
| 342 | active); |
| 343 | } |
| 344 | else if (is_iso9660(raw_disk)) { |
| 345 | device_error(device_instance_device(raw_disk), "Unimplemented active ISO9660"); |
| 346 | } |
| 347 | else if (block0_is_mac_disk(boot_block)) { |
| 348 | device_error(device_instance_device(raw_disk), "Unimplemented active MAC DISK"); |
| 349 | } |
| 350 | else { |
| 351 | device_error(device_instance_device(raw_disk), "Unreconized bootblock"); |
| 352 | } |
| 353 | } |
| 354 | else { |
| 355 | /* select the specified disk partition */ |
| 356 | if (block0_is_bpb(boot_block)) { |
| 357 | device_error(device_instance_device(raw_disk), "Unimplemented BPB"); |
| 358 | } |
| 359 | else if (block0_is_fdisk(boot_block)) { |
| 360 | /* return an instance */ |
| 361 | ppcboot_partition_t *partition_table = (ppcboot_partition_t*) &boot_block[446]; |
| 362 | ppcboot_partition_t *partition_entry; |
| 363 | disklabel *label; |
| 364 | if (partition > 4) |
| 365 | device_error(device_instance_device(raw_disk), |
| 366 | "Only FDISK partitions 1..4 supported"); |
| 367 | partition_entry = &partition_table[partition - 1]; |
| 368 | label = ZALLOC(disklabel); |
| 369 | label->raw_disk = raw_disk; |
| 370 | label->pos = 0; |
| 371 | label->sector_begin = 512 * sector2uw(partition_entry->sector_begin); |
| 372 | label->sector_length = 512 * sector2uw(partition_entry->sector_length); |
| 373 | PTRACE(disklabel, ("partition %ld, sector-begin %ld, length %ld\n", |
| 374 | (long)partition, |
| 375 | (long)label->sector_begin, |
| 376 | (long)label->sector_length)); |
| 377 | if (filename != NULL) |
| 378 | device_error(device_instance_device(raw_disk), |
| 379 | "FDISK file names not yet supported"); |
| 380 | return device_create_instance_from(NULL, raw_disk, |
| 381 | label, |
| 382 | NULL, args, |
| 383 | &package_disklabel_callbacks); |
| 384 | } |
| 385 | else if (block0_is_mac_disk(boot_block)) { |
| 386 | device_error(device_instance_device(raw_disk), "Unimplemented MAC DISK"); |
| 387 | } |
| 388 | else { |
| 389 | device_error(device_instance_device(raw_disk), |
| 390 | "Unreconized bootblock"); |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | return NULL; |
| 396 | } |
| 397 | |
| 398 | |
| 399 | #endif /* _PK_DISKLABEL_C_ */ |
| 400 | |