summaryrefslogtreecommitdiffstats
path: root/common/partitions
diff options
context:
space:
mode:
Diffstat (limited to 'common/partitions')
-rw-r--r--common/partitions/Kconfig3
-rw-r--r--common/partitions/Makefile2
-rw-r--r--common/partitions/dos.c380
-rw-r--r--common/partitions/efi.c356
-rw-r--r--common/partitions/efi.h119
-rw-r--r--common/partitions/parser.h39
6 files changed, 644 insertions, 255 deletions
diff --git a/common/partitions/Kconfig b/common/partitions/Kconfig
index be9405a649..3bbcceedc1 100644
--- a/common/partitions/Kconfig
+++ b/common/partitions/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
config PARTITION_DISK
depends on PARTITION
depends on BLOCK
@@ -17,6 +19,7 @@ config PARTITION_DISK_EFI
depends on PARTITION_DISK
select CRC32
select PRINTF_UUID
+ select PARTITION_DISK_DOS if PARTITION_MANIPULATION
bool "EFI: GPT partition support"
help
Add support to handle partitions in GUID Partition Table style.
diff --git a/common/partitions/Makefile b/common/partitions/Makefile
index 2b0c5b4b9c..d304b6f8d5 100644
--- a/common/partitions/Makefile
+++ b/common/partitions/Makefile
@@ -1,2 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-$(CONFIG_PARTITION_DISK_DOS) += dos.o
obj-$(CONFIG_PARTITION_DISK_EFI) += efi.o
diff --git a/common/partitions/dos.c b/common/partitions/dos.c
index 488c2936f7..8e4edd885b 100644
--- a/common/partitions/dos.c
+++ b/common/partitions/dos.c
@@ -15,12 +15,26 @@
#include <common.h>
#include <disks.h>
#include <init.h>
+#include <stdlib.h>
#include <asm/unaligned.h>
-#include <dma.h>
#include <linux/err.h>
+#include <partitions.h>
-#include "parser.h"
+struct dos_partition_desc {
+ struct partition_desc pd;
+ uint32_t signature;
+};
+struct dos_partition {
+ struct partition part;
+ bool extended;
+ bool logical;
+
+ uint8_t boot_indicator;
+ uint8_t chs_begin[3];
+ uint8_t type;
+ uint8_t chs_end[3];
+};
enum {
/* These three have identical behaviour; use the second one if DOS FDISK gets
@@ -37,34 +51,9 @@ static inline int is_extended_partition(struct partition *p)
p->dos_partition_type == LINUX_EXTENDED_PARTITION);
}
-/**
- * Guess the size of the disk, based on the partition table entries
- * @param dev device to create partitions for
- * @param table partition table
- * @return sector count
- */
-static uint64_t disk_guess_size(struct device_d *dev,
- struct partition_entry *table)
-{
- uint64_t size = 0;
- int i;
-
- for (i = 0; i < 4; i++) {
- if (get_unaligned_le32(&table[i].partition_start) != 0) {
- uint64_t part_end = get_unaligned_le32(&table[i].partition_start) +
- get_unaligned_le32(&table[i].partition_size);
-
- if (size < part_end)
- size = part_end;
- }
- }
-
- return size;
-}
-
static void *read_mbr(struct block_device *blk)
{
- void *buf = dma_alloc(SECTOR_SIZE);
+ void *buf = malloc(SECTOR_SIZE);
int ret;
ret = block_read(blk, buf, 0, 1);
@@ -132,17 +121,18 @@ static int dos_get_disk_signature(struct param_d *p, void *_priv)
return 0;
}
-static void dos_extended_partition(struct block_device *blk, struct partition_desc *pd,
+static void dos_extended_partition(struct block_device *blk, struct dos_partition_desc *dpd,
struct partition *partition, uint32_t signature)
{
- uint8_t *buf = dma_alloc(SECTOR_SIZE);
+ uint8_t *buf = malloc(SECTOR_SIZE);
uint32_t ebr_sector = partition->first_sec;
struct partition_entry *table = (struct partition_entry *)&buf[0x1be];
- unsigned partno = 5;
+ unsigned partno = 4;
+ struct dos_partition *dpart;
+ struct partition *pentry;
- while (pd->used_entries < ARRAY_SIZE(pd->parts)) {
+ while (1) {
int rc, i;
- int n = pd->used_entries;
dev_dbg(blk->dev, "expect EBR in sector %x\n", ebr_sector);
@@ -165,15 +155,25 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
}
/* /sanity checks */
+ dpart = xzalloc(sizeof(*dpart));
+ dpart->logical = true;
+ dpart->boot_indicator = table[0].boot_indicator;
+ memcpy(dpart->chs_begin, table[0].chs_begin, sizeof(table[0].chs_begin));
+ dpart->type = table[0].type;
+ memcpy(dpart->chs_end, table[0].chs_end, sizeof(table[0].chs_end));
+
+ pentry = &dpart->part;
+
/* the first entry defines the extended partition */
- pd->parts[n].first_sec = ebr_sector +
+ pentry->first_sec = ebr_sector +
get_unaligned_le32(&table[0].partition_start);
- pd->parts[n].size = get_unaligned_le32(&table[0].partition_size);
- pd->parts[n].dos_partition_type = table[0].type;
- if (signature)
- sprintf(pd->parts[n].partuuid, "%08x-%02u",
- signature, partno);
- pd->used_entries++;
+ pentry->size = get_unaligned_le32(&table[0].partition_size);
+ pentry->dos_partition_type = table[0].type;
+ pentry->num = partno;
+ sprintf(pentry->partuuid, "%08x-%02u", signature, partno + 1);
+
+ list_add_tail(&pentry->list, &dpd->pd.partitions);
+
partno++;
/* the second entry defines the start of the next ebr if != 0 */
@@ -185,10 +185,19 @@ static void dos_extended_partition(struct block_device *blk, struct partition_de
}
out:
- dma_free(buf);
+ free(buf);
return;
}
+static void extract_flags(const struct partition_entry *p,
+ struct partition *pentry)
+{
+ if (p->boot_indicator == 0x80)
+ pentry->flags |= DEVFS_PARTITION_BOOTABLE_LEGACY;
+ if (p->type == 0xef)
+ pentry->flags |= DEVFS_PARTITION_BOOTABLE_ESP;
+}
+
/**
* Check if a DOS like partition describes this block device
* @param blk Block device to register to
@@ -197,57 +206,72 @@ out:
* It seems at least on ARM this routine cannot use temp. stack space for the
* sector. So, keep the malloc/free.
*/
-static void dos_partition(void *buf, struct block_device *blk,
- struct partition_desc *pd)
+static struct partition_desc *dos_partition(void *buf, struct block_device *blk)
{
struct partition_entry *table;
- struct partition pentry;
+ struct dos_partition *dpart;
+ struct partition *pentry;
struct partition *extended_partition = NULL;
uint8_t *buffer = buf;
int i;
struct disk_signature_priv *dsp;
uint32_t signature = get_unaligned_le32(buf + 0x1b8);
+ struct dos_partition_desc *dpd;
+
+ sprintf(blk->cdev.diskuuid, "%08x", signature);
+
+ blk->cdev.flags |= DEVFS_IS_MBR_PARTITIONED;
table = (struct partition_entry *)&buffer[446];
- /* valid for x86 BIOS based disks only */
- if (IS_ENABLED(CONFIG_DISK_BIOS) && blk->num_blocks == 0)
- blk->num_blocks = disk_guess_size(blk->dev, table);
+ dpd = xzalloc(sizeof(*dpd));
+ partition_desc_init(&dpd->pd, blk);
for (i = 0; i < 4; i++) {
- pentry.first_sec = get_unaligned_le32(&table[i].partition_start);
- pentry.size = get_unaligned_le32(&table[i].partition_size);
- pentry.dos_partition_type = table[i].type;
-
- if (pentry.first_sec != 0) {
- int n = pd->used_entries;
- pd->parts[n].first_sec = pentry.first_sec;
- pd->parts[n].size = pentry.size;
- pd->parts[n].dos_partition_type = pentry.dos_partition_type;
- if (signature)
- sprintf(pd->parts[n].partuuid, "%08x-%02d",
- signature, i + 1);
- pd->used_entries++;
-
- if (is_extended_partition(&pentry)) {
- if (!extended_partition)
- extended_partition = &pd->parts[n];
- else
- /*
- * An DOS MBR must only contain a single
- * extended partition. Just ignore all
- * but the first.
- */
- dev_warn(blk->dev, "Skipping additional extended partition\n");
- }
+ uint64_t first_sec = get_unaligned_le32(&table[i].partition_start);
- } else {
+ if (first_sec == 0) {
dev_dbg(blk->dev, "Skipping empty partition %d\n", i);
+ continue;
+ }
+
+ dpart = xzalloc(sizeof(*dpart));
+ dpart->boot_indicator = table[i].boot_indicator;
+ memcpy(dpart->chs_begin, table[i].chs_begin, sizeof(table[i].chs_begin));
+ dpart->type = table[i].type;
+ memcpy(dpart->chs_end, table[i].chs_end, sizeof(table[i].chs_end));
+
+ pentry = &dpart->part;
+
+ pentry->first_sec = first_sec;
+ pentry->size = get_unaligned_le32(&table[i].partition_size);
+ pentry->dos_partition_type = table[i].type;
+ extract_flags(&table[i], pentry);
+ pentry->num = i;
+
+ sprintf(pentry->partuuid, "%08x-%02d", signature, i + 1);
+ dpd->signature = signature;
+
+ if (is_extended_partition(pentry)) {
+ dpart->extended = true;
+ pentry->size = 2;
+
+ if (!extended_partition)
+ extended_partition = pentry;
+ else
+ /*
+ * An DOS MBR must only contain a single
+ * extended partition. Just ignore all
+ * but the first.
+ */
+ dev_warn(blk->dev, "Skipping additional extended partition\n");
}
+
+ list_add_tail(&pentry->list, &dpd->pd.partitions);
}
if (extended_partition)
- dos_extended_partition(blk, pd, extended_partition, signature);
+ dos_extended_partition(blk, dpd, extended_partition, signature);
dsp = xzalloc(sizeof(*dsp));
dsp->blk = blk;
@@ -265,11 +289,219 @@ static void dos_partition(void *buf, struct block_device *blk,
dev_add_param_uint32(blk->dev, "nt_signature",
dos_set_disk_signature, dos_get_disk_signature,
&dsp->signature, "%08x", dsp);
+
+ return &dpd->pd;
+}
+
+static void dos_partition_free(struct partition_desc *pd)
+{
+ struct partition *part, *tmp;
+
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
+ struct dos_partition *dpart = container_of(part, struct dos_partition, part);
+
+ free(dpart);
+ }
+
+ free(pd);
+}
+
+static __maybe_unused struct partition_desc *dos_partition_create_table(struct block_device *blk)
+{
+ struct dos_partition_desc *dpd = xzalloc(512);
+
+ partition_desc_init(&dpd->pd, blk);
+
+ dpd->signature = random32();
+
+ return &dpd->pd;
+}
+
+static int fs_type_to_type(const char *fstype)
+{
+ unsigned long type;
+ int ret;
+
+ if (!strcmp(fstype, "ext2"))
+ return 0x83;
+ if (!strcmp(fstype, "ext3"))
+ return 0x83;
+ if (!strcmp(fstype, "ext4"))
+ return 0x83;
+ if (!strcmp(fstype, "fat16"))
+ return 0xe;
+ if (!strcmp(fstype, "fat32"))
+ return 0xc;
+
+ ret = kstrtoul(fstype, 16, &type);
+ if (ret)
+ return ret;
+
+ if (type > 0xff)
+ return -EINVAL;
+
+ return type;
+}
+
+static __maybe_unused int dos_partition_mkpart(struct partition_desc *pd,
+ const char *name, const char *fs_type,
+ uint64_t start_lba, uint64_t end_lba)
+{
+ struct dos_partition *dpart;
+ struct partition *part, *part_extended = NULL;
+ int npart = 0, npart_logical = 0;
+ uint64_t size;
+ int mask_free = 0xf;
+ int type = fs_type_to_type(fs_type);
+
+ if (type < 0) {
+ pr_err("invalid fs_type \"%s\"\n", fs_type);
+ return -EINVAL;
+ }
+
+ if (start_lba < 1) {
+ pr_err("invalid start LBA, minimum is 1\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+
+ mask_free &= ~(1 << npart);
+
+ if (dpart->extended)
+ part_extended = part;
+ if (dpart->logical)
+ npart_logical++;
+ else
+ npart++;
+ }
+
+ if (!strcmp(name, "extended")) {
+ if (part_extended) {
+ pr_err("extended partition already exists\n");
+ return -ENOSPC;
+ }
+ goto create;
+ } else if (!strcmp(name, "primary")) {
+ if (npart == 4) {
+ pr_err("Can't create any more partitions\n");
+ return -ENOSPC;
+ }
+
+ goto create;
+ } else if (!strcmp(name, "logical")) {
+ if (!part_extended) {
+ pr_err("No extended partition\n");
+ return -EINVAL;
+ }
+
+ if (npart_logical >= 4) {
+ pr_err("Can't create any more partitions\n");
+ return -ENOSPC;
+ }
+
+ pr_err("Can't create logical partitions yet\n");
+ return -EINVAL;
+ } else {
+ pr_err("Invalid name \"%s\"\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+
+create:
+ dpart = xzalloc(sizeof(*dpart));
+ part = &dpart->part;
+
+ if (start_lba > UINT_MAX)
+ return -ENOSPC;
+ size = end_lba - start_lba + 1;
+
+ if (size > UINT_MAX)
+ return -ENOSPC;
+
+ dpart->type = fs_type_to_type(fs_type);
+
+ part->first_sec = start_lba;
+ part->size = size;
+ part->num = ffs(mask_free);
+
+ list_add_tail(&part->list, &pd->partitions);
+
+ return 0;
+}
+
+static __maybe_unused int dos_partition_rmpart(struct partition_desc *pd, struct partition *part)
+{
+ struct dos_partition *dpart = container_of(part, struct dos_partition, part);
+
+ list_del(&part->list);
+ free(dpart);
+
+ return 0;
+}
+
+static __maybe_unused int dos_partition_write(struct partition_desc *pd)
+{
+ struct dos_partition_desc *dpd = container_of(pd, struct dos_partition_desc, pd);
+ struct dos_partition *dpart;
+ struct partition *part;
+ void *buf;
+ struct partition_entry *table, *entry;
+ int ret;
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+ if (dpart->logical) {
+ pr_err("Cannot write tables with logical partitions yet\n");
+ return -EINVAL;
+ }
+ }
+
+ buf = read_mbr(pd->blk);
+ if (!buf)
+ return -EIO;
+
+ put_unaligned_le32(dpd->signature, buf + 0x1b8);
+
+ table = buf + 0x1be;
+ memset(table, 0x0, sizeof(*table) * 4);
+
+ *(u8 *)(buf + 0x1fe) = 0x55;
+ *(u8 *)(buf + 0x1ff) = 0xaa;
+
+ list_for_each_entry(part, &pd->partitions, list) {
+ dpart = container_of(part, struct dos_partition, part);
+
+ entry = &table[part->num - 1];
+
+ entry->boot_indicator = dpart->boot_indicator;
+ memcpy(entry->chs_begin, dpart->chs_begin, sizeof(entry->chs_begin));
+ entry->type = dpart->type;
+ memcpy(entry->chs_end, dpart->chs_end, sizeof(entry->chs_end));
+ put_unaligned_le32(part->first_sec, &entry->partition_start);
+ put_unaligned_le32(part->size, &entry->partition_size);
+ }
+
+ ret = block_write(pd->blk, buf, 0, 1);
+
+ free(buf);
+
+ return ret;
}
static struct partition_parser dos = {
.parse = dos_partition,
+ .partition_free = dos_partition_free,
+#ifdef CONFIG_PARTITION_MANIPULATION
+ .create = dos_partition_create_table,
+ .mkpart = dos_partition_mkpart,
+ .rmpart = dos_partition_rmpart,
+ .write = dos_partition_write,
+#endif
.type = filetype_mbr,
+ .name = "msdos",
};
static int dos_partition_init(void)
diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index f20fd0d9b9..829360da6e 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -16,12 +16,22 @@
#include <disks.h>
#include <init.h>
#include <asm/unaligned.h>
-#include <dma.h>
#include <crc.h>
#include <linux/ctype.h>
-#include "efi.h"
-#include "parser.h"
+#include <efi/partition.h>
+#include <partitions.h>
+
+struct efi_partition_desc {
+ struct partition_desc pd;
+ gpt_header *gpt;
+ gpt_entry *ptes;
+};
+
+struct efi_partition {
+ struct partition part;
+ gpt_entry *pte;
+};
static const int force_gpt = IS_ENABLED(CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE);
@@ -81,6 +91,7 @@ static gpt_entry *alloc_read_gpt_entries(struct block_device *blk,
if (!count)
return NULL;
+
pte = kzalloc(count, GFP_KERNEL);
if (!pte)
return NULL;
@@ -155,8 +166,8 @@ static int is_gpt_valid(struct block_device *blk, u64 lba,
/* Check the GPT header signature */
if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
- dev_dbg(blk->dev, "GUID Partition Table Header signature is wrong:"
- "0x%llX != 0x%llX\n",
+ dev_dbg(blk->dev, "GUID Partition Table Header signature at LBA"
+ "%llu is wrong: 0x%llX != 0x%llX\n", lba,
(unsigned long long)le64_to_cpu((*gpt)->signature),
(unsigned long long)GPT_HEADER_SIGNATURE);
goto fail;
@@ -207,7 +218,8 @@ static int is_gpt_valid(struct block_device *blk, u64 lba,
le32_to_cpu((*gpt)->sizeof_partition_entry));
if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
- dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed.\n");
+ dev_dbg(blk->dev, "GUID Partitition Entry Array CRC check failed: 0x%08x 0x%08x\n",
+ crc, le32_to_cpu((*gpt)->partition_entry_array_crc32));
goto fail_ptes;
}
@@ -233,7 +245,7 @@ static int is_gpt_valid(struct block_device *blk, u64 lba,
static inline int
is_pte_valid(const gpt_entry *pte, const u64 lastlba)
{
- if ((!efi_guidcmp(pte->partition_type_guid, EFI_NULL_GUID)) ||
+ if (guid_is_null(&pte->partition_type_guid) ||
le64_to_cpu(pte->starting_lba) > lastlba ||
le64_to_cpu(pte->ending_lba) > lastlba)
return 0;
@@ -250,7 +262,8 @@ is_pte_valid(const gpt_entry *pte, const u64 lastlba)
*
*/
static void
-compare_gpts(struct device_d *dev, gpt_header *pgpt, gpt_header *agpt, u64 lastlba)
+compare_gpts(struct device *dev, gpt_header *pgpt, gpt_header *agpt,
+ u64 lastlba)
{
int error_found = 0;
if (!pgpt || !agpt)
@@ -287,7 +300,7 @@ compare_gpts(struct device_d *dev, gpt_header *pgpt, gpt_header *agpt, u64 lastl
(unsigned long long)le64_to_cpu(agpt->last_usable_lba));
error_found++;
}
- if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+ if (!guid_equal(&pgpt->disk_guid, &agpt->disk_guid)) {
dev_warn(dev, "GPT:disk_guids don't match.\n");
error_found++;
}
@@ -431,45 +444,342 @@ static void part_set_efi_name(gpt_entry *pte, char *dest)
dest[i] = 0;
}
-static void efi_partition(void *buf, struct block_device *blk,
- struct partition_desc *pd)
+static void extract_flags(const gpt_entry *p, struct partition *pentry)
+{
+ static const guid_t system_guid = PARTITION_SYSTEM_GUID;
+
+ if (p->attributes.required_to_function)
+ pentry->flags |= DEVFS_PARTITION_REQUIRED;
+ if (p->attributes.no_block_io_protocol)
+ pentry->flags |= DEVFS_PARTITION_NO_EXPORT;
+ if (p->attributes.legacy_bios_bootable)
+ pentry->flags |= DEVFS_PARTITION_BOOTABLE_LEGACY;
+
+ if (guid_equal(&p->partition_type_guid, &system_guid))
+ pentry->flags |= DEVFS_PARTITION_BOOTABLE_ESP;
+
+ pentry->typeflags = p->attributes.type_guid_specific;
+}
+
+static void part_get_efi_name(gpt_entry *pte, const char *src)
+{
+ int i;
+ int len = strlen(src);
+
+ for (i = 0; i < GPT_PARTNAME_MAX_SIZE ; i++) {
+ if (i <= len)
+ pte->partition_name[i] = src[i];
+ else
+ pte->partition_name[i] = 0;
+ }
+}
+
+static struct partition_desc *efi_partition(void *buf, struct block_device *blk)
{
gpt_header *gpt = NULL;
gpt_entry *ptes = NULL;
int i = 0;
int nb_part;
+ struct efi_partition *epart;
struct partition *pentry;
+ struct efi_partition_desc *epd;
- if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes) {
- kfree(gpt);
- kfree(ptes);
- return;
- }
+ if (!find_valid_gpt(buf, blk, &gpt, &ptes) || !gpt || !ptes)
+ return NULL;
+
+ snprintf(blk->cdev.diskuuid, sizeof(blk->cdev.diskuuid), "%pUl", &gpt->disk_guid);
+ dev_add_param_string_fixed(blk->dev, "guid", blk->cdev.diskuuid);
+
+ blk->cdev.flags |= DEVFS_IS_GPT_PARTITIONED;
nb_part = le32_to_cpu(gpt->num_partition_entries);
- for (i = 0; i < MAX_PARTITION && i < nb_part; i++) {
+
+ if (nb_part > MAX_PARTITION) {
+ dev_warn(blk->dev, "GPT has more partitions than we support (%d) > max partition number (%d)\n",
+ nb_part, MAX_PARTITION);
+ nb_part = MAX_PARTITION;
+ }
+
+ epd = xzalloc(sizeof(*epd));
+ partition_desc_init(&epd->pd, blk);
+
+ epd->gpt = gpt;
+ epd->ptes = ptes;
+
+ for (i = 0; i < nb_part; i++) {
if (!is_pte_valid(&ptes[i], last_lba(blk))) {
dev_dbg(blk->dev, "Invalid pte %d\n", i);
- return;
+ continue;
}
- pentry = &pd->parts[pd->used_entries];
+ epart = xzalloc(sizeof(*epart));
+ epart->pte = &ptes[i];
+ pentry = &epart->part;
+ extract_flags(&ptes[i], pentry);
pentry->first_sec = le64_to_cpu(ptes[i].starting_lba);
pentry->size = le64_to_cpu(ptes[i].ending_lba) - pentry->first_sec;
pentry->size++;
part_set_efi_name(&ptes[i], pentry->name);
snprintf(pentry->partuuid, sizeof(pentry->partuuid), "%pUl", &ptes[i].unique_partition_guid);
- pd->used_entries++;
+ pentry->typeuuid = ptes[i].partition_type_guid;
+ pentry->num = i;
+ list_add_tail(&pentry->list, &epd->pd.partitions);
}
- if (i > MAX_PARTITION)
- dev_warn(blk->dev, "num_partition_entries (%d) > max partition number (%d)\n",
- nb_part, MAX_PARTITION);
+ return &epd->pd;
+}
+
+static void efi_partition_free(struct partition_desc *pd)
+{
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
+ struct partition *part, *tmp;
+
+ list_for_each_entry_safe(part, tmp, &pd->partitions, list) {
+ struct efi_partition *epart = container_of(part, struct efi_partition, part);
+
+ free(epart);
+ }
+
+ free(epd->ptes);
+ free(epd->gpt);
+ free(epd);
+}
+
+static __maybe_unused struct partition_desc *efi_partition_create_table(struct block_device *blk)
+{
+ struct efi_partition_desc *epd = xzalloc(sizeof(*epd));
+ gpt_header *gpt;
+
+ partition_desc_init(&epd->pd, blk);
+
+ epd->gpt = xzalloc(512);
+ gpt = epd->gpt;
+
+ gpt->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+ gpt->revision = cpu_to_le32(0x100);
+ gpt->header_size = cpu_to_le32(sizeof(*gpt));
+ gpt->my_lba = cpu_to_le64(1);
+ gpt->alternate_lba = cpu_to_le64(last_lba(blk));
+ gpt->first_usable_lba = cpu_to_le64(34);
+ gpt->last_usable_lba = cpu_to_le64(last_lba(blk) - 34);;
+ generate_random_guid((unsigned char *)&gpt->disk_guid);
+ gpt->partition_entry_lba = cpu_to_le64(2);
+ gpt->num_partition_entries = cpu_to_le32(128);
+ gpt->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry));
+
+ pr_info("Created new disk label with GUID %pU\n", &gpt->disk_guid);
+
+ epd->ptes = xzalloc(128 * sizeof(gpt_entry));
+
+ return &epd->pd;
+}
+
+static guid_t partition_linux_data_guid = PARTITION_LINUX_DATA_GUID;
+static guid_t partition_basic_data_guid = PARTITION_BASIC_DATA_GUID;
+static guid_t partition_barebox_env_guid = PARTITION_BAREBOX_ENVIRONMENT_GUID;
+
+static const guid_t *fs_type_to_guid(const char *fstype)
+{
+ if (!strcmp(fstype, "ext2"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "ext3"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "ext4"))
+ return &partition_linux_data_guid;
+ if (!strcmp(fstype, "fat16"))
+ return &partition_basic_data_guid;
+ if (!strcmp(fstype, "fat32"))
+ return &partition_basic_data_guid;
+ if (!strcmp(fstype, "bbenv"))
+ return &partition_barebox_env_guid;
+
+ return NULL;
+}
+
+static __maybe_unused int efi_partition_mkpart(struct partition_desc *pd,
+ const char *name, const char *fs_type,
+ uint64_t start_lba, uint64_t end_lba)
+{
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
+ struct efi_partition *epart;
+ struct partition *part;
+ gpt_header *gpt = epd->gpt;
+ int num_parts = le32_to_cpu(gpt->num_partition_entries);
+ gpt_entry *pte;
+ int i;
+ const guid_t *guid;
+
+ if (start_lba < 34) {
+ pr_err("invalid start LBA %lld, minimum is 34\n", start_lba);
+ return -EINVAL;
+ }
+
+ if (end_lba >= last_lba(pd->blk) - 33) {
+ pr_err("invalid end LBA %lld, maximum is %lld\n", start_lba,
+ last_lba(pd->blk) - 33);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_parts; i++) {
+ if (!is_pte_valid(&epd->ptes[i], last_lba(pd->blk)))
+ break;
+ }
+
+ if (i == num_parts) {
+ pr_err("partition table is full\n");
+ return -ENOSPC;
+ }
+
+ guid = fs_type_to_guid(fs_type);
+ if (!guid) {
+ pr_err("Unknown fs type %s\n", fs_type);
+ return -EINVAL;
+ }
+
+ pte = &epd->ptes[i];
+ epart = xzalloc(sizeof(*epart));
+ part = &epart->part;
+
+ part->first_sec = start_lba;
+ part->size = end_lba - start_lba + 1;
+ part->typeuuid = *guid;
+
+ pte->partition_type_guid = *guid;
+ generate_random_guid((unsigned char *)&pte->unique_partition_guid);
+ pte->starting_lba = cpu_to_le64(start_lba);
+ pte->ending_lba = cpu_to_le64(end_lba);
+ part_get_efi_name(pte, name);
+ part_set_efi_name(pte, part->name);
+ part->num = i;
+
+ list_add_tail(&part->list, &pd->partitions);
+
+ return 0;
+}
+
+static __maybe_unused int efi_partition_rmpart(struct partition_desc *pd, struct partition *part)
+{
+ struct efi_partition *epart = container_of(part, struct efi_partition, part);
+
+ memset(epart->pte, 0, sizeof(*epart->pte));
+
+ list_del(&part->list);
+ free(epart);
+
+ return 0;
+}
+
+static int efi_protective_mbr(struct block_device *blk)
+{
+ struct partition_desc *pdesc;
+ int ret;
+
+ pdesc = partition_table_new(blk, "msdos");
+ if (IS_ERR(pdesc)) {
+ printf("Error: Cannot create partition table: %pe\n", pdesc);
+ return PTR_ERR(pdesc);
+ }
+
+ ret = partition_create(pdesc, "primary", "0xee", 1, last_lba(blk));
+ if (ret) {
+ pr_err("Cannot create partition: %pe\n", ERR_PTR(ret));
+ goto out;
+ }
+
+ ret = partition_table_write(pdesc);
+ if (ret) {
+ pr_err("Cannot write partition: %pe\n", ERR_PTR(ret));
+ goto out;
+ }
+out:
+ partition_table_free(pdesc);
+
+ return ret;
+}
+
+static __maybe_unused int efi_partition_write(struct partition_desc *pd)
+{
+ struct block_device *blk = pd->blk;
+ struct efi_partition_desc *epd = container_of(pd, struct efi_partition_desc, pd);
+ gpt_header *gpt = epd->gpt, *altgpt;
+ int ret;
+ uint32_t count;
+ uint64_t from, size;
+
+ if (le32_to_cpu(gpt->num_partition_entries) != 128) {
+ /*
+ * This is not yet properly implemented. At least writing of the
+ * alternative GPT is not correctly implemented for this case as
+ * we can't assume that the partition entries are written at
+ * last_lba() - 32, we would have to calculate that from the number
+ * of partition entries.
+ */
+ pr_err("num_partition_entries is != 128. This is not yet supported for writing\n");
+ return -EINVAL;
+ }
+
+ count = le32_to_cpu(gpt->num_partition_entries) *
+ le32_to_cpu(gpt->sizeof_partition_entry);
+
+ gpt->my_lba = cpu_to_le64(1);
+ gpt->partition_entry_array_crc32 = cpu_to_le32(efi_crc32(
+ (const unsigned char *)epd->ptes, count));
+ gpt->header_crc32 = 0;
+ gpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)gpt,
+ le32_to_cpu(gpt->header_size)));
+
+ ret = efi_protective_mbr(blk);
+ if (ret)
+ return ret;
+
+ ret = block_write(blk, gpt, 1, 1);
+ if (ret)
+ goto err_block_write;
+
+ from = le64_to_cpu(gpt->partition_entry_lba);
+ size = count / GPT_BLOCK_SIZE;
+
+ ret = block_write(blk, epd->ptes, from, size);
+ if (ret)
+ goto err_block_write;
+
+ altgpt = xmemdup(gpt, SECTOR_SIZE);
+
+ altgpt->alternate_lba = cpu_to_le64(1);
+ altgpt->my_lba = cpu_to_le64(last_lba(blk));
+ altgpt->partition_entry_lba = cpu_to_le64(last_lba(blk) - 32);
+ altgpt->header_crc32 = 0;
+ altgpt->header_crc32 = cpu_to_le32(efi_crc32((const unsigned char *)altgpt,
+ le32_to_cpu(altgpt->header_size)));
+ ret = block_write(blk, altgpt, last_lba(blk), 1);
+
+ free(altgpt);
+
+ if (ret)
+ goto err_block_write;
+ ret = block_write(blk, epd->ptes, last_lba(blk) - 32, 32);
+ if (ret)
+ goto err_block_write;
+
+ return 0;
+
+err_block_write:
+ pr_err("Cannot write to block device: %pe\n", ERR_PTR(ret));
+
+ return ret;
}
static struct partition_parser efi_partition_parser = {
.parse = efi_partition,
+ .partition_free = efi_partition_free,
+#ifdef CONFIG_PARTITION_MANIPULATION
+ .create = efi_partition_create_table,
+ .mkpart = efi_partition_mkpart,
+ .rmpart = efi_partition_rmpart,
+ .write = efi_partition_write,
+#endif
.type = filetype_gpt,
+ .name = "gpt",
};
static int efi_partition_init(void)
diff --git a/common/partitions/efi.h b/common/partitions/efi.h
deleted file mode 100644
index a9b10c1266..0000000000
--- a/common/partitions/efi.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/************************************************************
- * EFI GUID Partition Table
- * Per Intel EFI Specification v1.02
- * http://developer.intel.com/technology/efi/efi.htm
- *
- * By Matt Domsch <Matt_Domsch@dell.com> Fri Sep 22 22:15:56 CDT 2000
- * Copyright 2000,2001 Dell Inc.
- *
- * 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.
- *
- ************************************************************/
-
-#ifndef FS_PART_EFI_H_INCLUDED
-#define FS_PART_EFI_H_INCLUDED
-
-#include <efi.h>
-
-#define MSDOS_MBR_SIGNATURE 0xaa55
-#define EFI_PMBR_OSTYPE_EFI 0xEF
-#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
-
-#define GPT_BLOCK_SIZE 512
-#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
-#define GPT_HEADER_REVISION_V1 0x00010000
-#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
-
-#define PARTITION_SYSTEM_GUID \
- EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \
- 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B)
-#define LEGACY_MBR_PARTITION_GUID \
- EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \
- 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F)
-#define PARTITION_MSFT_RESERVED_GUID \
- EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \
- 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE)
-#define PARTITION_BASIC_DATA_GUID \
- EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \
- 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
-#define PARTITION_LINUX_RAID_GUID \
- EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \
- 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
-#define PARTITION_LINUX_SWAP_GUID \
- EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \
- 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f)
-#define PARTITION_LINUX_LVM_GUID \
- EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
- 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
-
-/* based on linux/include/genhd.h */
-struct legacy_partition {
- unsigned char boot_ind; /* 0x80 - active */
- unsigned char head; /* starting head */
- unsigned char sector; /* starting sector */
- unsigned char cyl; /* starting cylinder */
- unsigned char sys_ind; /* What partition type */
- unsigned char end_head; /* end head */
- unsigned char end_sector; /* end sector */
- unsigned char end_cyl; /* end cylinder */
- __le32 start_sect; /* starting sector counting from 0 */
- __le32 nr_sects; /* nr of sectors in partition */
-} __attribute__ ((packed));
-
-/* based on linux/fs/partitions/efi.h */
-typedef struct _gpt_header {
- __le64 signature;
- __le32 revision;
- __le32 header_size;
- __le32 header_crc32;
- __le32 reserved1;
- __le64 my_lba;
- __le64 alternate_lba;
- __le64 first_usable_lba;
- __le64 last_usable_lba;
- efi_guid_t disk_guid;
- __le64 partition_entry_lba;
- __le32 num_partition_entries;
- __le32 sizeof_partition_entry;
- __le32 partition_entry_array_crc32;
-
- /* The rest of the logical block is reserved by UEFI and must be zero.
- * EFI standard handles this by:
- *
- * uint8_t reserved2[ BlockSize - 92 ];
- */
-} __attribute__ ((packed)) gpt_header;
-
-typedef struct _gpt_entry_attributes {
- u64 required_to_function:1;
- u64 reserved:47;
- u64 type_guid_specific:16;
-} __attribute__ ((packed)) gpt_entry_attributes;
-
-#define GPT_PARTNAME_MAX_SIZE (72 / sizeof (efi_char16_t))
-typedef struct _gpt_entry {
- efi_guid_t partition_type_guid;
- efi_guid_t unique_partition_guid;
- __le64 starting_lba;
- __le64 ending_lba;
- gpt_entry_attributes attributes;
- efi_char16_t partition_name[GPT_PARTNAME_MAX_SIZE];
-} __attribute__ ((packed)) gpt_entry;
-
-typedef struct _legacy_mbr {
- u8 boot_code[440];
- __le32 unique_mbr_signature;
- __le16 unknown;
- struct legacy_partition partition_record[4];
- __le16 signature;
-} __attribute__ ((packed)) legacy_mbr;
-
-#endif /* _DISK_PART_EFI_H */
diff --git a/common/partitions/parser.h b/common/partitions/parser.h
deleted file mode 100644
index 8ad134a9aa..0000000000
--- a/common/partitions/parser.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * Under GPLv2 only
- */
-
-#ifndef __PARTITIONS_PARSER_H__
-#define __PARTITIONS_PARSER_H__
-
-#include <block.h>
-#include <filetype.h>
-#include <linux/list.h>
-
-#define MAX_PARTITION 8
-#define MAX_PARTITION_NAME 38
-
-struct partition {
- char name[MAX_PARTITION_NAME];
- u8 dos_partition_type;
- char partuuid[MAX_PARTUUID_STR];
- uint64_t first_sec;
- uint64_t size;
-};
-
-struct partition_desc {
- int used_entries;
- struct partition parts[MAX_PARTITION];
-};
-
-struct partition_parser {
- void (*parse)(void *buf, struct block_device *blk, struct partition_desc *pd);
- enum filetype type;
-
- struct list_head list;
-};
-
-int partition_parser_register(struct partition_parser *p);
-
-#endif /* __PARTITIONS_PARSER_H__ */