#include #include #include #include #if defined (BIOS) # include #endif #include #include #include #include #include enum { CACHE_NOT_READY = 0, CACHE_READY }; static bool cache_block(struct volume *volume, uint64_t block) { if (volume->cache_status == CACHE_READY && block == volume->cached_block) return true; volume->cache_status = CACHE_NOT_READY; if (volume->cache == NULL) volume->cache = ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size); if (volume->first_sect % (volume->sector_size / 512)) { return false; } uint64_t first_sect = volume->first_sect / (volume->sector_size / 512); uint64_t xfer_size = volume->fastest_xfer_size; for (;;) { int ret = disk_read_sectors(volume, volume->cache, first_sect + block * volume->fastest_xfer_size, xfer_size); switch (ret) { case DISK_NO_MEDIA: return false; case DISK_SUCCESS: goto disk_success; } xfer_size--; if (xfer_size == 0) { return false; } } disk_success: volume->cache_status = CACHE_READY; volume->cached_block = block; return true; } bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t count) { if (volume->pxe) { panic(false, "Attempted volume_read() on pxe"); } uint64_t block_size = volume->fastest_xfer_size * volume->sector_size; uint64_t progress = 0; while (progress < count) { uint64_t block = (loc + progress) / block_size; if (!cache_block(volume, block)) return false; uint64_t chunk = count - progress; uint64_t offset = (loc + progress) % block_size; if (chunk > block_size - offset) chunk = block_size - offset; memcpy(buffer + progress, &volume->cache[offset], chunk); progress += chunk; } return true; } struct gpt_table_header { // the head char signature[8]; uint32_t revision; uint32_t header_size; uint32_t crc32; uint32_t _reserved0; // the partitioning info uint64_t my_lba; uint64_t alternate_lba; uint64_t first_usable_lba; uint64_t last_usable_lba; // the guid struct guid disk_guid; // entries related uint64_t partition_entry_lba; uint32_t number_of_partition_entries; uint32_t size_of_partition_entry; uint32_t partition_entry_array_crc32; } __attribute__((packed)); struct gpt_entry { struct guid partition_type_guid; struct guid unique_partition_guid; uint64_t starting_lba; uint64_t ending_lba; uint64_t attributes; uint16_t partition_name[36]; } __attribute__((packed)); bool gpt_get_guid(struct guid *guid, struct volume *volume) { struct gpt_table_header header = {0}; int lb_guesses[] = { 512, 4096 }; int lb_size = -1; for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) { // read header, located after the first block volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)); // check the header // 'EFI PART' if (strncmp(header.signature, "EFI PART", 8)) continue; lb_size = lb_guesses[i]; break; } if (lb_size == -1) { return false; } if (header.revision != 0x00010000) return false; *guid = header.disk_guid; return true; } static int gpt_get_part(struct volume *ret, struct volume *volume, int partition) { struct gpt_table_header header = {0}; int lb_guesses[] = { 512, 4096 }; int lb_size = -1; for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) { // read header, located after the first block volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)); // check the header // 'EFI PART' if (strncmp(header.signature, "EFI PART", 8)) continue; lb_size = lb_guesses[i]; break; } if (lb_size == -1) { return INVALID_TABLE; } if (header.revision != 0x00010000) return INVALID_TABLE; // parse the entries if reached here if ((uint32_t)partition >= header.number_of_partition_entries) return END_OF_TABLE; struct gpt_entry entry = {0}; volume_read(volume, &entry, (header.partition_entry_lba * lb_size) + (partition * sizeof(entry)), sizeof(entry)); struct guid empty_guid = {0}; if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid))) return NO_PARTITION; #if defined (UEFI) ret->efi_handle = volume->efi_handle; ret->block_io = volume->block_io; #elif defined (BIOS) ret->drive = volume->drive; #endif ret->fastest_xfer_size = volume->fastest_xfer_size; ret->index = volume->index; ret->is_optical = volume->is_optical; ret->partition = partition + 1; ret->sector_size = volume->sector_size; ret->first_sect = entry.starting_lba * (lb_size / 512); ret->sect_count = ((entry.ending_lba - entry.starting_lba) + 1) * (lb_size / 512); ret->backing_dev = volume; struct guid guid; if (!fs_get_guid(&guid, ret)) { ret->guid_valid = false; } else { ret->guid_valid = true; ret->guid = guid; } char *fslabel = fs_get_label(ret); if (fslabel == NULL) { ret->fslabel_valid = false; } else { ret->fslabel_valid = true; ret->fslabel = fslabel; } ret->part_guid_valid = true; ret->part_guid = entry.unique_partition_guid; return 0; } struct mbr_entry { uint8_t status; uint8_t chs_first_sect[3]; uint8_t type; uint8_t chs_last_sect[3]; uint32_t first_sect; uint32_t sect_count; } __attribute__((packed)); bool is_valid_mbr(struct volume *volume) { // Check if actually valid mbr uint16_t hint = 0; volume_read(volume, &hint, 446, sizeof(uint8_t)); if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) return false; volume_read(volume, &hint, 462, sizeof(uint8_t)); if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) return false; volume_read(volume, &hint, 478, sizeof(uint8_t)); if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) return false; volume_read(volume, &hint, 494, sizeof(uint8_t)); if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80) return false; char hintc[64]; volume_read(volume, hintc, 4, 8); if (memcmp(hintc, "_ECH_FS_", 8) == 0) return false; volume_read(volume, hintc, 3, 4); if (memcmp(hintc, "NTFS", 4) == 0) return false; volume_read(volume, hintc, 54, 3); if (memcmp(hintc, "FAT", 3) == 0) return false; volume_read(volume, hintc, 82, 3); if (memcmp(hintc, "FAT", 3) == 0) return false; volume_read(volume, hintc, 3, 5); if (memcmp(hintc, "FAT32", 5) == 0) return false; volume_read(volume, &hint, 1080, sizeof(uint16_t)); if (hint == 0xef53) return false; return true; } uint32_t mbr_get_id(struct volume *volume) { if (!is_valid_mbr(volume)) { return 0; } uint32_t ret; volume_read(volume, &ret, 0x1b8, sizeof(uint32_t)); return ret; } static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part, int partition) { struct mbr_entry entry; uint64_t ebr_sector = 0; for (int i = 0; i < partition; i++) { uint64_t entry_offset = ebr_sector * 512 + 0x1ce; volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry)); if (entry.type != 0x0f && entry.type != 0x05) { return END_OF_TABLE; } ebr_sector = entry.first_sect; } uint64_t entry_offset = ebr_sector * 512 + 0x1be; volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry)); if (entry.type == 0) return NO_PARTITION; #if defined (UEFI) ret->efi_handle = extended_part->efi_handle; ret->block_io = extended_part->block_io; #elif defined (BIOS) ret->drive = extended_part->drive; #endif ret->fastest_xfer_size = extended_part->fastest_xfer_size; ret->index = extended_part->index; ret->is_optical = extended_part->is_optical; ret->partition = partition + 4 + 1; ret->sector_size = extended_part->sector_size; ret->first_sect = extended_part->first_sect + ebr_sector + entry.first_sect; ret->sect_count = entry.sect_count; ret->backing_dev = extended_part->backing_dev; struct guid guid; if (!fs_get_guid(&guid, ret)) { ret->guid_valid = false; } else { ret->guid_valid = true; ret->guid = guid; } char *fslabel = fs_get_label(ret); if (fslabel == NULL) { ret->fslabel_valid = false; } else { ret->fslabel_valid = true; ret->fslabel = fslabel; } ret->part_guid_valid = false; return 0; } static int mbr_get_part(struct volume *ret, struct volume *volume, int partition) { if (!is_valid_mbr(volume)) { return INVALID_TABLE; } struct mbr_entry entry; if (partition > 3) { for (int i = 0; i < 4; i++) { uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * i; volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry)); if (entry.type != 0x0f && entry.type != 0x05) continue; struct volume extended_part = {0}; #if defined (UEFI) extended_part.efi_handle = volume->efi_handle; extended_part.block_io = volume->block_io; #elif defined (BIOS) extended_part.drive = volume->drive; #endif extended_part.fastest_xfer_size = volume->fastest_xfer_size; extended_part.index = volume->index; extended_part.is_optical = volume->is_optical; extended_part.partition = i + 1; extended_part.sector_size = volume->sector_size; extended_part.first_sect = entry.first_sect; extended_part.sect_count = entry.sect_count; extended_part.backing_dev = volume; return mbr_get_logical_part(ret, &extended_part, partition - 4); } return END_OF_TABLE; } uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * partition; volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry)); if (entry.type == 0) return NO_PARTITION; #if defined (UEFI) ret->efi_handle = volume->efi_handle; ret->block_io = volume->block_io; #elif defined (BIOS) ret->drive = volume->drive; #endif ret->fastest_xfer_size = volume->fastest_xfer_size; ret->index = volume->index; ret->is_optical = volume->is_optical; ret->partition = partition + 1; ret->sector_size = volume->sector_size; ret->first_sect = entry.first_sect; ret->sect_count = entry.sect_count; ret->backing_dev = volume; struct guid guid; if (!fs_get_guid(&guid, ret)) { ret->guid_valid = false; } else { ret->guid_valid = true; ret->guid = guid; } char *fslabel = fs_get_label(ret); if (fslabel == NULL) { ret->fslabel_valid = false; } else { ret->fslabel_valid = true; ret->fslabel = fslabel; } ret->part_guid_valid = false; return 0; } int part_get(struct volume *part, struct volume *volume, int partition) { int ret; ret = gpt_get_part(part, volume, partition); if (ret != INVALID_TABLE) return ret; ret = mbr_get_part(part, volume, partition); if (ret != INVALID_TABLE) return ret; return INVALID_TABLE; } struct volume **volume_index = NULL; size_t volume_index_i = 0; struct volume *volume_get_by_guid(struct guid *guid) { for (size_t i = 0; i < volume_index_i; i++) { if (volume_index[i]->guid_valid && memcmp(&volume_index[i]->guid, guid, 16) == 0) { return volume_index[i]; } if (volume_index[i]->part_guid_valid && memcmp(&volume_index[i]->part_guid, guid, 16) == 0) { return volume_index[i]; } } return NULL; } struct volume *volume_get_by_fslabel(char *fslabel) { for (size_t i = 0; i < volume_index_i; i++) { if (volume_index[i]->fslabel_valid && strcmp(volume_index[i]->fslabel, fslabel) == 0) { return volume_index[i]; } } return NULL; } struct volume *volume_get_by_coord(bool optical, int drive, int partition) { for (size_t i = 0; i < volume_index_i; i++) { if (volume_index[i]->index == drive && volume_index[i]->is_optical == optical && volume_index[i]->partition == partition) { return volume_index[i]; } } return NULL; } #if defined (BIOS) struct volume *volume_get_by_bios_drive(int drive) { for (size_t i = 0; i < volume_index_i; i++) { if (volume_index[i]->drive == drive) { return volume_index[i]; } } return NULL; } #endif