/* Copyright (C) 2005 Thomas Petazzoni This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include /** * This structure defines the structure of a partition entry * (determined by the PC architecture) */ typedef struct partition_entry { sos_ui8_t active; sos_ui8_t start_dl; sos_ui16_t start_cylinder; sos_ui8_t type; sos_ui8_t end_dl; sos_ui16_t end_cylinder; sos_ui32_t lba; sos_ui32_t size; } partition_entry_t; /** * Offset of the partition table inside the 512-byte sector. */ #define PART_TABLE_OFFSET 446 /** * The most common partition types. For a complete list, you can for * example refer to * http://www.win.tue.nl/~aeb/partitions/partition_types-1.html */ #define PART_TYPE_EXTENDED 0x5 #define PART_TYPE_FAT16_1 0xe #define PART_TYPE_FAT16_2 0x6 #define PART_TYPE_FAT32_1 0xb #define PART_TYPE_FAT32_2 0xc #define PART_TYPE_LINUX_SWAP 0x82 #define PART_TYPE_LINUX 0x83 /** * Converts a partition type to a string */ static const char * sos_part_type_str (unsigned int type) { switch (type) { case PART_TYPE_EXTENDED: return "Extended"; case PART_TYPE_FAT16_1: case PART_TYPE_FAT16_2: return "FAT16"; case PART_TYPE_FAT32_1: case PART_TYPE_FAT32_2: return "FAT32"; case PART_TYPE_LINUX_SWAP: return "Linux Swap"; case PART_TYPE_LINUX: return "Linux"; default: return "Unknown"; } } /** * Detect the partitions on the given device, and registers them to * the block device infrastructure. */ sos_ret_t sos_part_detect (sos_ui32_t disk_class, sos_ui32_t disk_instance, sos_size_t block_size, const char *name) { sos_vaddr_t buffer; struct sos_blockdev_instance *blkdev; struct partition_entry *part_entry; unsigned int extstart = 0, extsup = 0; sos_ret_t ret; sos_size_t size = block_size; unsigned int partnum; blkdev = sos_blockdev_ref_instance (disk_class, disk_instance); if (blkdev == NULL) return -SOS_ENOENT; buffer = (sos_vaddr_t) sos_kmalloc (block_size, 0); if (buffer == 0) { sos_blockdev_release_instance (blkdev); return -SOS_ENOMEM; } ret = sos_blockdev_kernel_read (blkdev, 0, buffer, & size); if (ret != SOS_OK) { sos_blockdev_release_instance (blkdev); sos_kfree (buffer); return ret; } if (size != block_size) { sos_blockdev_release_instance (blkdev); sos_kfree (buffer); return -SOS_EIO; } part_entry = (struct partition_entry *) (buffer + PART_TABLE_OFFSET); /* Handle primary partitions */ for (partnum = 0; partnum < 4; partnum++) { if (part_entry [partnum].size == 0) continue; sos_bochs_printf ("%s%d (%lu Mb, %s) ", name, partnum+1, (part_entry[partnum].size * block_size >> 20), sos_part_type_str(part_entry[partnum].type)); if (part_entry [partnum].type == PART_TYPE_EXTENDED) { if (extstart != 0) sos_bochs_printf ("Warning: two extended partitions detected\n"); extstart = part_entry [partnum].lba; continue; } ret = sos_blockdev_register_partition (disk_class, disk_instance + partnum + 1, blkdev, part_entry[partnum].lba, part_entry[partnum].size, NULL); if (ret != SOS_OK) { sos_bochs_printf ("Could not register partition %d for disk %lu:%lu: error %d\n", partnum, disk_class, disk_instance, ret); continue; } } while (extstart != 0 && partnum < 15) { ret = sos_blockdev_kernel_read (blkdev, (extstart + extsup) * block_size, (sos_luoffset_t) buffer, & size); if (ret != SOS_OK) { sos_blockdev_release_instance (blkdev); sos_kfree (buffer); return ret; } if (size != block_size) { sos_blockdev_release_instance (blkdev); sos_kfree (buffer); return -SOS_EIO; } sos_bochs_printf ("%s%d (%lu Mb, %s) ", name, partnum+1, (part_entry[0].size * block_size >> 20), sos_part_type_str(part_entry[0].type)); ret = sos_blockdev_register_partition (disk_class, disk_instance + partnum + 1, blkdev, extstart + part_entry[0].lba, part_entry[0].size, NULL); if (ret != SOS_OK) sos_bochs_printf ("Could not register partition %d for disk %lu:%lu: error %d\n", partnum, disk_class, disk_instance, ret); extsup = part_entry[1].lba; partnum ++; if (extsup == 0) break; } sos_blockdev_release_instance (blkdev); sos_kfree (buffer); return SOS_OK; } /** * Remove all detected partitions on given device */ sos_ret_t sos_part_undetect (sos_ui32_t disk_class, sos_ui32_t disk_instance) { int partnum; sos_ret_t ret; for (partnum = 0; partnum < 15; partnum++) { ret = sos_blockdev_unregister_device (disk_class, disk_instance + partnum + 1); if (ret != SOS_OK) sos_bochs_printf ("Warning: could not unregister partition %d of device %lu:%lu\n", partnum, disk_class, disk_instance); } return SOS_OK; }