162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 HUAWEI 462306a36Sopenharmony_ci * Author: Cai Zhiyong <caizhiyong@huawei.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Read block device partition table from the command line. 762306a36Sopenharmony_ci * Typically used for fixed block (eMMC) embedded devices. 862306a36Sopenharmony_ci * It has no MBR, so saves storage space. Bootloader can be easily accessed 962306a36Sopenharmony_ci * by absolute address of data on the block device. 1062306a36Sopenharmony_ci * Users can easily change the partition. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The format for the command line is just like mtdparts. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * For further information, see "Documentation/block/cmdline-partition.rst" 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci#include <linux/blkdev.h> 1862306a36Sopenharmony_ci#include <linux/fs.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include "check.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* partition flags */ 2462306a36Sopenharmony_ci#define PF_RDONLY 0x01 /* Device is read only */ 2562306a36Sopenharmony_ci#define PF_POWERUP_LOCK 0x02 /* Always locked after reset */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct cmdline_subpart { 2862306a36Sopenharmony_ci char name[BDEVNAME_SIZE]; /* partition name, such as 'rootfs' */ 2962306a36Sopenharmony_ci sector_t from; 3062306a36Sopenharmony_ci sector_t size; 3162306a36Sopenharmony_ci int flags; 3262306a36Sopenharmony_ci struct cmdline_subpart *next_subpart; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct cmdline_parts { 3662306a36Sopenharmony_ci char name[BDEVNAME_SIZE]; /* block device, such as 'mmcblk0' */ 3762306a36Sopenharmony_ci unsigned int nr_subparts; 3862306a36Sopenharmony_ci struct cmdline_subpart *subpart; 3962306a36Sopenharmony_ci struct cmdline_parts *next_parts; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int parse_subpart(struct cmdline_subpart **subpart, char *partdef) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci int ret = 0; 4562306a36Sopenharmony_ci struct cmdline_subpart *new_subpart; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci *subpart = NULL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci new_subpart = kzalloc(sizeof(struct cmdline_subpart), GFP_KERNEL); 5062306a36Sopenharmony_ci if (!new_subpart) 5162306a36Sopenharmony_ci return -ENOMEM; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (*partdef == '-') { 5462306a36Sopenharmony_ci new_subpart->size = (sector_t)(~0ULL); 5562306a36Sopenharmony_ci partdef++; 5662306a36Sopenharmony_ci } else { 5762306a36Sopenharmony_ci new_subpart->size = (sector_t)memparse(partdef, &partdef); 5862306a36Sopenharmony_ci if (new_subpart->size < (sector_t)PAGE_SIZE) { 5962306a36Sopenharmony_ci pr_warn("cmdline partition size is invalid."); 6062306a36Sopenharmony_ci ret = -EINVAL; 6162306a36Sopenharmony_ci goto fail; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (*partdef == '@') { 6662306a36Sopenharmony_ci partdef++; 6762306a36Sopenharmony_ci new_subpart->from = (sector_t)memparse(partdef, &partdef); 6862306a36Sopenharmony_ci } else { 6962306a36Sopenharmony_ci new_subpart->from = (sector_t)(~0ULL); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (*partdef == '(') { 7362306a36Sopenharmony_ci int length; 7462306a36Sopenharmony_ci char *next = strchr(++partdef, ')'); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!next) { 7762306a36Sopenharmony_ci pr_warn("cmdline partition format is invalid."); 7862306a36Sopenharmony_ci ret = -EINVAL; 7962306a36Sopenharmony_ci goto fail; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci length = min_t(int, next - partdef, 8362306a36Sopenharmony_ci sizeof(new_subpart->name) - 1); 8462306a36Sopenharmony_ci strscpy(new_subpart->name, partdef, length); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci partdef = ++next; 8762306a36Sopenharmony_ci } else 8862306a36Sopenharmony_ci new_subpart->name[0] = '\0'; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci new_subpart->flags = 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!strncmp(partdef, "ro", 2)) { 9362306a36Sopenharmony_ci new_subpart->flags |= PF_RDONLY; 9462306a36Sopenharmony_ci partdef += 2; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!strncmp(partdef, "lk", 2)) { 9862306a36Sopenharmony_ci new_subpart->flags |= PF_POWERUP_LOCK; 9962306a36Sopenharmony_ci partdef += 2; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci *subpart = new_subpart; 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_cifail: 10562306a36Sopenharmony_ci kfree(new_subpart); 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void free_subpart(struct cmdline_parts *parts) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct cmdline_subpart *subpart; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci while (parts->subpart) { 11462306a36Sopenharmony_ci subpart = parts->subpart; 11562306a36Sopenharmony_ci parts->subpart = subpart->next_subpart; 11662306a36Sopenharmony_ci kfree(subpart); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int parse_parts(struct cmdline_parts **parts, const char *bdevdef) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int ret = -EINVAL; 12362306a36Sopenharmony_ci char *next; 12462306a36Sopenharmony_ci int length; 12562306a36Sopenharmony_ci struct cmdline_subpart **next_subpart; 12662306a36Sopenharmony_ci struct cmdline_parts *newparts; 12762306a36Sopenharmony_ci char buf[BDEVNAME_SIZE + 32 + 4]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci *parts = NULL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL); 13262306a36Sopenharmony_ci if (!newparts) 13362306a36Sopenharmony_ci return -ENOMEM; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci next = strchr(bdevdef, ':'); 13662306a36Sopenharmony_ci if (!next) { 13762306a36Sopenharmony_ci pr_warn("cmdline partition has no block device."); 13862306a36Sopenharmony_ci goto fail; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci length = min_t(int, next - bdevdef, sizeof(newparts->name) - 1); 14262306a36Sopenharmony_ci strscpy(newparts->name, bdevdef, length); 14362306a36Sopenharmony_ci newparts->nr_subparts = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci next_subpart = &newparts->subpart; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci while (next && *(++next)) { 14862306a36Sopenharmony_ci bdevdef = next; 14962306a36Sopenharmony_ci next = strchr(bdevdef, ','); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci length = (!next) ? (sizeof(buf) - 1) : 15262306a36Sopenharmony_ci min_t(int, next - bdevdef, sizeof(buf) - 1); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci strscpy(buf, bdevdef, length); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = parse_subpart(next_subpart, buf); 15762306a36Sopenharmony_ci if (ret) 15862306a36Sopenharmony_ci goto fail; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci newparts->nr_subparts++; 16162306a36Sopenharmony_ci next_subpart = &(*next_subpart)->next_subpart; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!newparts->subpart) { 16562306a36Sopenharmony_ci pr_warn("cmdline partition has no valid partition."); 16662306a36Sopenharmony_ci ret = -EINVAL; 16762306a36Sopenharmony_ci goto fail; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci *parts = newparts; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_cifail: 17462306a36Sopenharmony_ci free_subpart(newparts); 17562306a36Sopenharmony_ci kfree(newparts); 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void cmdline_parts_free(struct cmdline_parts **parts) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct cmdline_parts *next_parts; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci while (*parts) { 18462306a36Sopenharmony_ci next_parts = (*parts)->next_parts; 18562306a36Sopenharmony_ci free_subpart(*parts); 18662306a36Sopenharmony_ci kfree(*parts); 18762306a36Sopenharmony_ci *parts = next_parts; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int cmdline_parts_parse(struct cmdline_parts **parts, 19262306a36Sopenharmony_ci const char *cmdline) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci int ret; 19562306a36Sopenharmony_ci char *buf; 19662306a36Sopenharmony_ci char *pbuf; 19762306a36Sopenharmony_ci char *next; 19862306a36Sopenharmony_ci struct cmdline_parts **next_parts; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci *parts = NULL; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci next = pbuf = buf = kstrdup(cmdline, GFP_KERNEL); 20362306a36Sopenharmony_ci if (!buf) 20462306a36Sopenharmony_ci return -ENOMEM; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci next_parts = parts; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci while (next && *pbuf) { 20962306a36Sopenharmony_ci next = strchr(pbuf, ';'); 21062306a36Sopenharmony_ci if (next) 21162306a36Sopenharmony_ci *next = '\0'; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = parse_parts(next_parts, pbuf); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci goto fail; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (next) 21862306a36Sopenharmony_ci pbuf = ++next; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci next_parts = &(*next_parts)->next_parts; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!*parts) { 22462306a36Sopenharmony_ci pr_warn("cmdline partition has no valid partition."); 22562306a36Sopenharmony_ci ret = -EINVAL; 22662306a36Sopenharmony_ci goto fail; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = 0; 23062306a36Sopenharmony_cidone: 23162306a36Sopenharmony_ci kfree(buf); 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cifail: 23562306a36Sopenharmony_ci cmdline_parts_free(parts); 23662306a36Sopenharmony_ci goto done; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts, 24062306a36Sopenharmony_ci const char *bdev) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci while (parts && strncmp(bdev, parts->name, sizeof(parts->name))) 24362306a36Sopenharmony_ci parts = parts->next_parts; 24462306a36Sopenharmony_ci return parts; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic char *cmdline; 24862306a36Sopenharmony_cistatic struct cmdline_parts *bdev_parts; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int add_part(int slot, struct cmdline_subpart *subpart, 25162306a36Sopenharmony_ci struct parsed_partitions *state) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int label_min; 25462306a36Sopenharmony_ci struct partition_meta_info *info; 25562306a36Sopenharmony_ci char tmp[sizeof(info->volname) + 4]; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (slot >= state->limit) 25862306a36Sopenharmony_ci return 1; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci put_partition(state, slot, subpart->from >> 9, 26162306a36Sopenharmony_ci subpart->size >> 9); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci info = &state->parts[slot].info; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci label_min = min_t(int, sizeof(info->volname) - 1, 26662306a36Sopenharmony_ci sizeof(subpart->name)); 26762306a36Sopenharmony_ci strscpy(info->volname, subpart->name, label_min); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), "(%s)", info->volname); 27062306a36Sopenharmony_ci strlcat(state->pp_buf, tmp, PAGE_SIZE); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci state->parts[slot].has_info = true; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size, 27862306a36Sopenharmony_ci struct parsed_partitions *state) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci sector_t from = 0; 28162306a36Sopenharmony_ci struct cmdline_subpart *subpart; 28262306a36Sopenharmony_ci int slot = 1; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci for (subpart = parts->subpart; subpart; 28562306a36Sopenharmony_ci subpart = subpart->next_subpart, slot++) { 28662306a36Sopenharmony_ci if (subpart->from == (sector_t)(~0ULL)) 28762306a36Sopenharmony_ci subpart->from = from; 28862306a36Sopenharmony_ci else 28962306a36Sopenharmony_ci from = subpart->from; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (from >= disk_size) 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (subpart->size > (disk_size - from)) 29562306a36Sopenharmony_ci subpart->size = disk_size - from; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci from += subpart->size; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (add_part(slot, subpart, state)) 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return slot; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int __init cmdline_parts_setup(char *s) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci cmdline = s; 30962306a36Sopenharmony_ci return 1; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci__setup("blkdevparts=", cmdline_parts_setup); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic bool has_overlaps(sector_t from, sector_t size, 31462306a36Sopenharmony_ci sector_t from2, sector_t size2) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci sector_t end = from + size; 31762306a36Sopenharmony_ci sector_t end2 = from2 + size2; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (from >= from2 && from < end2) 32062306a36Sopenharmony_ci return true; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (end > from2 && end <= end2) 32362306a36Sopenharmony_ci return true; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (from2 >= from && from2 < end) 32662306a36Sopenharmony_ci return true; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (end2 > from && end2 <= end) 32962306a36Sopenharmony_ci return true; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return false; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic inline void overlaps_warns_header(void) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci pr_warn("Overlapping partitions are used in command line partitions."); 33762306a36Sopenharmony_ci pr_warn("Don't use filesystems on overlapping partitions:"); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void cmdline_parts_verifier(int slot, struct parsed_partitions *state) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci int i; 34362306a36Sopenharmony_ci bool header = true; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci for (; slot < state->limit && state->parts[slot].has_info; slot++) { 34662306a36Sopenharmony_ci for (i = slot+1; i < state->limit && state->parts[i].has_info; 34762306a36Sopenharmony_ci i++) { 34862306a36Sopenharmony_ci if (has_overlaps(state->parts[slot].from, 34962306a36Sopenharmony_ci state->parts[slot].size, 35062306a36Sopenharmony_ci state->parts[i].from, 35162306a36Sopenharmony_ci state->parts[i].size)) { 35262306a36Sopenharmony_ci if (header) { 35362306a36Sopenharmony_ci header = false; 35462306a36Sopenharmony_ci overlaps_warns_header(); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci pr_warn("%s[%llu,%llu] overlaps with " 35762306a36Sopenharmony_ci "%s[%llu,%llu].", 35862306a36Sopenharmony_ci state->parts[slot].info.volname, 35962306a36Sopenharmony_ci (u64)state->parts[slot].from << 9, 36062306a36Sopenharmony_ci (u64)state->parts[slot].size << 9, 36162306a36Sopenharmony_ci state->parts[i].info.volname, 36262306a36Sopenharmony_ci (u64)state->parts[i].from << 9, 36362306a36Sopenharmony_ci (u64)state->parts[i].size << 9); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* 37062306a36Sopenharmony_ci * Purpose: allocate cmdline partitions. 37162306a36Sopenharmony_ci * Returns: 37262306a36Sopenharmony_ci * -1 if unable to read the partition table 37362306a36Sopenharmony_ci * 0 if this isn't our partition table 37462306a36Sopenharmony_ci * 1 if successful 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ciint cmdline_partition(struct parsed_partitions *state) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci sector_t disk_size; 37962306a36Sopenharmony_ci struct cmdline_parts *parts; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (cmdline) { 38262306a36Sopenharmony_ci if (bdev_parts) 38362306a36Sopenharmony_ci cmdline_parts_free(&bdev_parts); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (cmdline_parts_parse(&bdev_parts, cmdline)) { 38662306a36Sopenharmony_ci cmdline = NULL; 38762306a36Sopenharmony_ci return -1; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci cmdline = NULL; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!bdev_parts) 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci parts = cmdline_parts_find(bdev_parts, state->disk->disk_name); 39662306a36Sopenharmony_ci if (!parts) 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci disk_size = get_capacity(state->disk) << 9; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci cmdline_parts_set(parts, disk_size, state); 40262306a36Sopenharmony_ci cmdline_parts_verifier(1, state); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci strlcat(state->pp_buf, "\n", PAGE_SIZE); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 1; 40762306a36Sopenharmony_ci} 408