|
6 | 6 |
|
7 | 7 | #include <stdbool.h> |
8 | 8 | #include <stdint.h> |
| 9 | +#include <stdlib.h> |
9 | 10 | #include <string.h> |
10 | 11 | #include <inttypes.h> |
11 | 12 |
|
@@ -225,6 +226,7 @@ static bool gpt_entry_cmp_name(const struct gpt_entry_t *entry, const void *name |
225 | 226 | static bool gpt_entry_cmp_type(const struct gpt_entry_t *entry, const void *type); |
226 | 227 | static psa_status_t validate_table(struct gpt_t *table, bool is_primary); |
227 | 228 | static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary); |
| 229 | +static psa_status_t sort_partition_array(struct gpt_t *table); |
228 | 230 |
|
229 | 231 | /* PUBLIC API FUNCTIONS */ |
230 | 232 |
|
@@ -819,6 +821,66 @@ psa_status_t gpt_restore(bool is_primary) |
819 | 821 | } |
820 | 822 | } |
821 | 823 |
|
| 824 | +psa_status_t gpt_defragment(void) |
| 825 | +{ |
| 826 | + /* First, sort the partition array according to start LBA. This means that |
| 827 | + * moving partitions towards the start of the flash sequentially is safe |
| 828 | + * and will not result in lost data. |
| 829 | + */ |
| 830 | + psa_status_t ret = sort_partition_array(&primary_gpt); |
| 831 | + if (ret != PSA_SUCCESS) { |
| 832 | + WARN("Unable to defragment flash!\n"); |
| 833 | + return ret; |
| 834 | + } |
| 835 | + |
| 836 | + uint64_t prev_end = primary_gpt.header.first_lba; |
| 837 | + struct gpt_entry_t entry; |
| 838 | + |
| 839 | + for (uint32_t i = 0; i < primary_gpt.num_used_partitions; ++i) { |
| 840 | + ret = read_entry_from_flash(&primary_gpt, i, &entry); |
| 841 | + if (ret != PSA_SUCCESS) { |
| 842 | + return ret; |
| 843 | + } |
| 844 | + |
| 845 | + /* Move to be next to previous entry. Continue if already where it |
| 846 | + * needs to be. |
| 847 | + */ |
| 848 | + if (prev_end == entry.start) { |
| 849 | + prev_end = entry.end + 1; |
| 850 | + continue; |
| 851 | + } |
| 852 | + |
| 853 | + const uint64_t num_blocks = entry.end - entry.start + 1; |
| 854 | + ret = move_partition(entry.start, prev_end, num_blocks); |
| 855 | + if (ret != PSA_SUCCESS) { |
| 856 | + return ret; |
| 857 | + } |
| 858 | + |
| 859 | + /* Update header information */ |
| 860 | + entry.start = prev_end; |
| 861 | + entry.end = entry.start + num_blocks - 1; |
| 862 | + prev_end = entry.end + 1; |
| 863 | + |
| 864 | + /* Write the entry change, skipping header update until every entry |
| 865 | + * written |
| 866 | + */ |
| 867 | + ret = write_entry(i, &entry, true); |
| 868 | + if (ret != PSA_SUCCESS) { |
| 869 | + return ret; |
| 870 | + } |
| 871 | + } |
| 872 | + |
| 873 | + /* Write everything to flash after defragmentation if not done so already. |
| 874 | + * The previous loop will write the last entry to the LBA buffer, which may |
| 875 | + * or not may not be flushed |
| 876 | + */ |
| 877 | + if (write_buffered) { |
| 878 | + return flush_lba_buf(); |
| 879 | + } |
| 880 | + |
| 881 | + return update_header(primary_gpt.num_used_partitions); |
| 882 | +} |
| 883 | + |
822 | 884 | /* Initialises GPT from first block. */ |
823 | 885 | psa_status_t gpt_init(struct gpt_flash_driver_t *flash_driver, uint64_t max_partitions) |
824 | 886 | { |
@@ -1533,6 +1595,134 @@ static psa_status_t restore_table(struct gpt_t *restore_from, bool is_primary) |
1533 | 1595 | return 0; |
1534 | 1596 | } |
1535 | 1597 |
|
| 1598 | +/* Comparison function to pass to qsort */ |
| 1599 | +static int cmp_u64(const void *a, const void *b) |
| 1600 | +{ |
| 1601 | + const uint64_t *a_u64 = (const uint64_t *)a; |
| 1602 | + const uint64_t *b_u64 = (const uint64_t *)b; |
| 1603 | + return (*a_u64 > *b_u64) - (*a_u64 < *b_u64); |
| 1604 | +} |
| 1605 | + |
| 1606 | +/* bsearch but returns the index rather than the item */ |
| 1607 | +static int64_t bsearch_index(uint64_t arr[], uint32_t len, uint64_t key) |
| 1608 | +{ |
| 1609 | + uint32_t l = 0; |
| 1610 | + uint32_t r = len; |
| 1611 | + |
| 1612 | + while (l < r) { |
| 1613 | + uint32_t m = l + (r - l) / 2; |
| 1614 | + uint64_t item = arr[m]; |
| 1615 | + |
| 1616 | + if (item < key) { |
| 1617 | + l = m + 1; |
| 1618 | + } else if (item > key) { |
| 1619 | + r = m; |
| 1620 | + } else { |
| 1621 | + return (int64_t)m; |
| 1622 | + } |
| 1623 | + } |
| 1624 | + |
| 1625 | + return -1; |
| 1626 | +} |
| 1627 | + |
| 1628 | +/* Sorts the partition array for the given table by the start LBA for each |
| 1629 | + * partition. This makes defragmentation easier. |
| 1630 | + */ |
| 1631 | +static psa_status_t sort_partition_array(struct gpt_t *table) |
| 1632 | +{ |
| 1633 | + /* To avoid as much I/O as possible, the LBA's for each entry are sorted in |
| 1634 | + * memory and then the entries rearranged on flash after |
| 1635 | + */ |
| 1636 | + uint64_t lba_arr[table->num_used_partitions]; |
| 1637 | + psa_status_t ret; |
| 1638 | + for (uint32_t i = 0; i < table->num_used_partitions; ++i) { |
| 1639 | + struct gpt_entry_t entry; |
| 1640 | + ret = read_entry_from_flash(table, i, &entry); |
| 1641 | + if (ret != PSA_SUCCESS) { |
| 1642 | + return ret; |
| 1643 | + } |
| 1644 | + lba_arr[i] = entry.start; |
| 1645 | + } |
| 1646 | + |
| 1647 | + qsort(lba_arr, table->num_used_partitions, sizeof(uint64_t), cmp_u64); |
| 1648 | + |
| 1649 | + /* Now read and place the entries in the correct spot, starting with the |
| 1650 | + * first. Each entry is dealt with as it is encountered. When an entry is |
| 1651 | + * found and already in the correct spot, the next smallest index not yet |
| 1652 | + * handled becomes the next. |
| 1653 | + */ |
| 1654 | + struct gpt_entry_t saved_entry = {0}; |
| 1655 | + struct gpt_entry_t curr_entry; |
| 1656 | + uint8_t handled_indices[table->num_used_partitions]; |
| 1657 | + memset(handled_indices, 0, table->num_used_partitions); |
| 1658 | + |
| 1659 | + ret = read_entry_from_flash(table, 0, &curr_entry); |
| 1660 | + if (ret != PSA_SUCCESS) { |
| 1661 | + return ret; |
| 1662 | + } |
| 1663 | + |
| 1664 | + for (uint32_t i = 0; i < table->num_used_partitions; ++i) { |
| 1665 | + const int64_t new_index = bsearch_index( |
| 1666 | + lba_arr, |
| 1667 | + table->num_used_partitions, |
| 1668 | + curr_entry.start); |
| 1669 | + if (new_index < 0) { |
| 1670 | + ERROR("Encountered unknown partition entry!\n"); |
| 1671 | + return PSA_ERROR_STORAGE_FAILURE; |
| 1672 | + } |
| 1673 | + |
| 1674 | + /* For final entry, just write it out */ |
| 1675 | + if (i == table->num_used_partitions - 1) { |
| 1676 | + ret = write_entry((uint32_t)new_index, &curr_entry, false); |
| 1677 | + if (ret != PSA_SUCCESS) { |
| 1678 | + return ret; |
| 1679 | + } |
| 1680 | + break; |
| 1681 | + } |
| 1682 | + |
| 1683 | + /* Replace the entry in the new_index place with the current entry */ |
| 1684 | + ret = read_entry_from_flash(table, (uint32_t)new_index, &saved_entry); |
| 1685 | + if (ret != PSA_SUCCESS) { |
| 1686 | + return ret; |
| 1687 | + } |
| 1688 | + |
| 1689 | + struct efi_guid_t saved_guid = saved_entry.unique_guid; |
| 1690 | + struct efi_guid_t curr_guid = curr_entry.unique_guid; |
| 1691 | + if (efi_guid_cmp(&saved_guid, &curr_guid) == 0) { |
| 1692 | + /* This entry is already where it needs to be, so try the smallest |
| 1693 | + * index not yet handled next |
| 1694 | + */ |
| 1695 | + handled_indices[new_index] = 1; |
| 1696 | + uint32_t next_entry = 0; |
| 1697 | + while(next_entry < table->num_used_partitions && handled_indices[next_entry]) { |
| 1698 | + ++next_entry; |
| 1699 | + } |
| 1700 | + |
| 1701 | + if (next_entry == table->num_used_partitions) { |
| 1702 | + /* Done everything */ |
| 1703 | + break; |
| 1704 | + } |
| 1705 | + |
| 1706 | + ret = read_entry_from_flash(table, next_entry, &saved_entry); |
| 1707 | + if (ret != PSA_SUCCESS) { |
| 1708 | + return ret; |
| 1709 | + } |
| 1710 | + } else { |
| 1711 | + /* Write, skipping header update until very end */ |
| 1712 | + ret = write_entry((uint32_t)new_index, &curr_entry, true); |
| 1713 | + if (ret != PSA_SUCCESS) { |
| 1714 | + return ret; |
| 1715 | + } |
| 1716 | + } |
| 1717 | + |
| 1718 | + /* Ready up for the next loop */ |
| 1719 | + curr_entry = saved_entry; |
| 1720 | + handled_indices[new_index] = 1; |
| 1721 | + } |
| 1722 | + |
| 1723 | + return PSA_SUCCESS; |
| 1724 | +} |
| 1725 | + |
1536 | 1726 | /* Converts unicode string to valid ascii */ |
1537 | 1727 | static psa_status_t unicode_to_ascii(const char *unicode, char *ascii) |
1538 | 1728 | { |
|
0 commit comments