You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
14 KiB
560 lines
14 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2020-2020. All rights reserved.
|
|
* Description: parse the cmdline parameter
|
|
* Author: Hisilicon
|
|
* Create: 2020-10-15
|
|
*/
|
|
|
|
#include "cmdline_parts.h"
|
|
#include "soc_log.h"
|
|
|
|
#include "uapi_flash.h"
|
|
#include "flash_ext.h"
|
|
#include "nand.h"
|
|
#include "securec.h"
|
|
|
|
#define BDEVNAME_SIZE 32 /* Largest string for a blockdev identifier */
|
|
#undef LOG_MODULE_ID
|
|
#define LOG_MODULE_ID SOC_ID_FLASH
|
|
|
|
struct cmdline_subpart {
|
|
td_char name[BDEVNAME_SIZE]; /* partition name, such as 'rootfs' */
|
|
td_u64 from;
|
|
td_u64 size;
|
|
struct cmdline_subpart *next_subpart;
|
|
};
|
|
|
|
struct cmdline_parts_info {
|
|
td_char part_name[BDEVNAME_SIZE];
|
|
td_u64 start_addr;
|
|
td_u64 part_size;
|
|
};
|
|
|
|
struct cmdline_parts {
|
|
td_char name[BDEVNAME_SIZE]; /* block device, such as 'mmcblk0' */
|
|
td_u64 end_addr;
|
|
struct cmdline_subpart *subpart;
|
|
struct cmdline_parts *next_parts;
|
|
};
|
|
|
|
static struct cmdline_parts_info g_cmd_partition[MAX_PARTS];
|
|
static struct cmdline_parts *g_cmd_parts_head = NULL;
|
|
|
|
static td_u32 min(td_u32 a, td_u32 b)
|
|
{
|
|
return a <= b ? a : b;
|
|
}
|
|
|
|
static td_u64 memparse(const td_char *ptr, td_char **retptr)
|
|
{
|
|
td_char *endptr = NULL; /* local pointer to end of parsed string */
|
|
td_u64 ret = strtoull(ptr, &endptr, 0);
|
|
|
|
switch (*endptr) {
|
|
case 'G':
|
|
case 'g':
|
|
ret <<= 10; /* 10 - left shift bits */
|
|
/* fall-through */
|
|
case 'M':
|
|
case 'm':
|
|
ret <<= 10; /* 10 - left shift bits */
|
|
/* fall-through */
|
|
case 'K':
|
|
case 'k':
|
|
ret <<= 10; /* 10 - left shift bits */
|
|
endptr++;
|
|
/* fall-through */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (retptr != NULL) {
|
|
*retptr = endptr;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 parse_cmdline_ascii_2d(td_char **parse_cmdline,
|
|
struct cmdline_subpart *new_subpart, td_s32 part_len)
|
|
{
|
|
td_char *cmdline = *parse_cmdline;
|
|
|
|
if (part_len <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (*cmdline == '-') {
|
|
new_subpart->size = (td_u64)(~0ULL);
|
|
cmdline++;
|
|
} else {
|
|
new_subpart->size = memparse(cmdline, &cmdline);
|
|
if (new_subpart->size == 0) {
|
|
soc_print("cmdline partition size is invalid.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
*parse_cmdline = cmdline;
|
|
return 0;
|
|
}
|
|
|
|
static td_void parse_cmdline_ascii_40(td_char **parse_cmdline,
|
|
struct cmdline_subpart *new_subpart, td_s32 part_len, struct cmdline_parts *parts)
|
|
{
|
|
td_char *cmdline = *parse_cmdline;
|
|
|
|
if (part_len <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (*cmdline == '@') {
|
|
cmdline++;
|
|
new_subpart->from = memparse(cmdline, &cmdline);
|
|
parts->end_addr = new_subpart->from + new_subpart->size;
|
|
} else {
|
|
new_subpart->from = parts->end_addr;
|
|
parts->end_addr += new_subpart->size;
|
|
}
|
|
|
|
*parse_cmdline = cmdline;
|
|
}
|
|
|
|
static td_s32 parse_cmdline_ascii_28(td_char **parse_cmdline,
|
|
struct cmdline_subpart *new_subpart, td_s32 part_len)
|
|
{
|
|
td_char *cmdline = *parse_cmdline;
|
|
td_u32 length;
|
|
|
|
if (part_len <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (*cmdline == '(') {
|
|
td_char *next = strchr(++cmdline, ')');
|
|
if (next == NULL) {
|
|
soc_print("cmdline partition format is invalid");
|
|
return -1;
|
|
}
|
|
|
|
length = min((td_u32)(next - cmdline), sizeof(new_subpart->name) - 1);
|
|
if (strncpy_s(new_subpart->name, sizeof(new_subpart->name), cmdline, length) != EOK) {
|
|
soc_print("Failed to snprintf_s.\n");
|
|
return -1;
|
|
}
|
|
new_subpart->name[length] = '\0';
|
|
|
|
cmdline = ++next;
|
|
} else {
|
|
new_subpart->name[0] = '\0';
|
|
}
|
|
|
|
*parse_cmdline = cmdline;
|
|
return 0;
|
|
}
|
|
|
|
static td_s32 parse_subpart(struct cmdline_parts *parts,
|
|
struct cmdline_subpart **subpart, td_char *cmdline, td_s32 cmdline_len)
|
|
{
|
|
struct cmdline_subpart *new_subpart = NULL;
|
|
|
|
if (cmdline_len <= 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*subpart = NULL;
|
|
|
|
new_subpart = malloc(sizeof(struct cmdline_subpart));
|
|
if (new_subpart == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
memset_s(new_subpart, sizeof(struct cmdline_subpart), 0, sizeof(struct cmdline_subpart));
|
|
|
|
if (parse_cmdline_ascii_2d(&cmdline, new_subpart, sizeof(struct cmdline_subpart)) < 0) {
|
|
free(new_subpart);
|
|
return -EINVAL;
|
|
}
|
|
|
|
parse_cmdline_ascii_40(&cmdline, new_subpart, sizeof(struct cmdline_subpart), parts);
|
|
|
|
if (parse_cmdline_ascii_28(&cmdline, new_subpart, sizeof(struct cmdline_subpart)) < 0) {
|
|
free(new_subpart);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*subpart = new_subpart;
|
|
return 0;
|
|
}
|
|
|
|
static td_void free_subpart(struct cmdline_parts *parts)
|
|
{
|
|
struct cmdline_subpart *subpart = NULL;
|
|
|
|
while (parts->subpart != NULL) {
|
|
subpart = parts->subpart;
|
|
parts->subpart = subpart->next_subpart;
|
|
free(subpart);
|
|
}
|
|
}
|
|
|
|
static td_void free_parts(struct cmdline_parts **parts)
|
|
{
|
|
struct cmdline_parts *next_parts = NULL;
|
|
|
|
while (*parts) {
|
|
next_parts = (*parts)->next_parts;
|
|
free_subpart(*parts);
|
|
free(*parts);
|
|
*parts = next_parts;
|
|
}
|
|
}
|
|
|
|
static td_s32 parse_cmdline_parts(struct cmdline_parts *newparts, td_char *cmdline)
|
|
{
|
|
td_u32 length;
|
|
td_char buf[BDEVNAME_SIZE + 32 + 4]; /* 32 -, 4 - */
|
|
td_char *next = NULL;
|
|
td_char *cmd_str = cmdline;
|
|
struct cmdline_subpart **next_subpart = NULL;
|
|
|
|
next = strchr(cmd_str, ':');
|
|
if (next == NULL) {
|
|
soc_print("cmdline partition has not block device");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
length = min((td_u32)(next - cmd_str), sizeof(newparts->name) - 1);
|
|
if (strncpy_s(newparts->name, sizeof(newparts->name), cmd_str, length) != EOK) {
|
|
return TD_FAILURE;
|
|
}
|
|
newparts->name[length] = '\0';
|
|
newparts->end_addr = 0;
|
|
next_subpart = &newparts->subpart;
|
|
|
|
while (next != NULL) {
|
|
++next;
|
|
if (*next == 0) {
|
|
break;
|
|
}
|
|
cmd_str = next;
|
|
next = strchr(cmd_str, ',');
|
|
|
|
length = (!next) ? (sizeof(buf) - 1) : min((td_u32)(next - cmd_str), sizeof(buf) - 1);
|
|
if (strncpy_s(buf, sizeof(buf), cmd_str, length) != EOK) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
buf[length] = '\0';
|
|
|
|
if (parse_subpart(newparts, next_subpart, buf, sizeof(buf)) != 0) {
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
next_subpart = &(*next_subpart)->next_subpart;
|
|
}
|
|
|
|
if (newparts->subpart == NULL) {
|
|
soc_print("cmdline partition has not valid partition");
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_s32 parse_parts(struct cmdline_parts **parts, td_char *cmdline)
|
|
{
|
|
struct cmdline_parts *newparts = NULL;
|
|
|
|
if (cmdline == NULL) {
|
|
soc_print("cmdline partition invalid");
|
|
return -EINVAL;
|
|
}
|
|
|
|
newparts = malloc(sizeof(struct cmdline_parts));
|
|
if (newparts == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
memset_s(newparts, sizeof(struct cmdline_parts), 0, sizeof(struct cmdline_parts));
|
|
|
|
if (parse_cmdline_parts(newparts, cmdline) != TD_SUCCESS) {
|
|
free_subpart(newparts);
|
|
free(newparts);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*parts = newparts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static td_s32 parse_cmdline(struct cmdline_parts **parts, const td_char *cmdline)
|
|
{
|
|
td_s32 ret;
|
|
td_char *buf = NULL;
|
|
td_char *pbuf = NULL;
|
|
td_char *next = NULL;
|
|
struct cmdline_parts **next_parts = NULL;
|
|
|
|
*parts = NULL;
|
|
|
|
if (strlen(cmdline) == SIZE_MAX) {
|
|
next = pbuf = buf = strdup(cmdline);
|
|
} else {
|
|
next = pbuf = buf = strndup(cmdline, strlen(cmdline));
|
|
}
|
|
|
|
if (buf == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
next_parts = parts;
|
|
|
|
while ((next != NULL) && (*pbuf != 0)) {
|
|
next = strchr(pbuf, ';');
|
|
if (next != NULL) {
|
|
*next = '\0';
|
|
}
|
|
|
|
ret = parse_parts(next_parts, pbuf);
|
|
if (ret != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
if (next != NULL) {
|
|
pbuf = ++next;
|
|
}
|
|
|
|
next_parts = &(*next_parts)->next_parts;
|
|
}
|
|
|
|
if (!*parts) {
|
|
soc_print("cmdline partition has not valid partition.\n");
|
|
ret = -EINVAL;
|
|
goto fail;
|
|
}
|
|
|
|
ret = 0;
|
|
free(buf);
|
|
return ret;
|
|
|
|
fail:
|
|
free_parts(parts);
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static struct cmdline_parts *get_cmdline_parts(const td_char *cmdline_string, td_u32 cmdline_len)
|
|
{
|
|
td_char *parts_string = NULL;
|
|
struct cmdline_parts *tmp_cmd_parts = NULL;
|
|
|
|
if (cmdline_string == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (cmdline_len <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
parts_string = strstr(cmdline_string, "mmcblk0:");
|
|
if (parts_string == NULL) {
|
|
parts_string = strstr(cmdline_string, "sdd:");
|
|
if (parts_string == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (parse_cmdline(&tmp_cmd_parts, parts_string) != 0) {
|
|
if (tmp_cmd_parts != NULL) {
|
|
free_parts(&tmp_cmd_parts);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
return tmp_cmd_parts;
|
|
}
|
|
|
|
td_s32 cmdline_parts_init(td_char *bootargs)
|
|
{
|
|
td_char *cmdline_string = NULL;
|
|
td_char *pcmdline_buf = NULL;
|
|
td_char *pend = NULL;
|
|
|
|
if (bootargs == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (g_cmd_parts_head != NULL) {
|
|
return 0;
|
|
}
|
|
|
|
cmdline_string = strstr(bootargs, "blkdevparts=");
|
|
if (cmdline_string == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
cmdline_string += sizeof("blkdevparts=") - 1;
|
|
|
|
pcmdline_buf = strdup(cmdline_string);
|
|
if (pcmdline_buf == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pend = strchr(pcmdline_buf, ' ');
|
|
if ((pend != NULL) && ((td_u32)(pend - pcmdline_buf) <= strlen(pcmdline_buf))) {
|
|
*pend = '\0';
|
|
}
|
|
|
|
g_cmd_parts_head = get_cmdline_parts(pcmdline_buf, (td_u32)strlen(pcmdline_buf));
|
|
if (g_cmd_parts_head == NULL) {
|
|
soc_print("Fail to get cmdline parts from: %s\n", pcmdline_buf);
|
|
free(pcmdline_buf);
|
|
pcmdline_buf = NULL;
|
|
return -ENODEV;
|
|
}
|
|
|
|
free(pcmdline_buf);
|
|
pcmdline_buf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static td_s32 find_cmdline_parts(struct cmdline_parts **cmdline_parts, const td_char *media_name)
|
|
{
|
|
td_s32 got;
|
|
size_t len;
|
|
struct cmdline_parts *tmp_cmdline_parts = *cmdline_parts;
|
|
|
|
got = 0;
|
|
while ((tmp_cmdline_parts != NULL) && (got == 0)) {
|
|
len = (strlen(media_name) < sizeof(tmp_cmdline_parts->name)) ?
|
|
(strlen(media_name) + 1) : sizeof(tmp_cmdline_parts->name);
|
|
if (strncmp(tmp_cmdline_parts->name, media_name, len) == 0) {
|
|
got = 1;
|
|
break;
|
|
}
|
|
tmp_cmdline_parts = tmp_cmdline_parts->next_parts;
|
|
}
|
|
|
|
if ((got == 0) || (tmp_cmdline_parts == NULL)) {
|
|
return 0;
|
|
}
|
|
*cmdline_parts = tmp_cmdline_parts;
|
|
return 1;
|
|
}
|
|
|
|
static td_s32 find_cmdline_subpart(struct cmdline_subpart **subpart, const td_char *ptn_name)
|
|
{
|
|
td_s32 got;
|
|
size_t len;
|
|
struct cmdline_subpart *tmp_subpart = *subpart;
|
|
|
|
got = 0;
|
|
while ((tmp_subpart != NULL) && (got == 0)) {
|
|
len = (strlen(ptn_name) < sizeof(tmp_subpart->name)) ?
|
|
(strlen(ptn_name) + 1) : sizeof(tmp_subpart->name);
|
|
if (strncmp(tmp_subpart->name, ptn_name, len) == 0) {
|
|
got = 1;
|
|
break;
|
|
}
|
|
tmp_subpart = tmp_subpart->next_subpart;
|
|
}
|
|
|
|
if ((got == 0) || (tmp_subpart == NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
*subpart = tmp_subpart;
|
|
return 1;
|
|
}
|
|
|
|
/* 1 - find, 0 - no find */
|
|
td_s32 find_flash_part(const td_char *cmdline_string,
|
|
const td_char *media_name, const td_char *ptn_name, td_u64 *start, td_u64 *length)
|
|
{
|
|
struct cmdline_parts *tmp_cmdline_parts = NULL;
|
|
struct cmdline_subpart *tmp_subpart = NULL;
|
|
|
|
if ((media_name == NULL) || (ptn_name == NULL) || (start == NULL) || (length == NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
if (g_cmd_parts_head == NULL) {
|
|
return 0;
|
|
}
|
|
tmp_cmdline_parts = g_cmd_parts_head;
|
|
if (find_cmdline_parts(&tmp_cmdline_parts, media_name) == 0) {
|
|
soc_print("%s not found from: %s\n", media_name, cmdline_string);
|
|
return 0;
|
|
}
|
|
|
|
tmp_subpart = tmp_cmdline_parts->subpart;
|
|
if (find_cmdline_subpart(&tmp_subpart, ptn_name) == 0) {
|
|
soc_print("%s not found from: %s\n", ptn_name, cmdline_string);
|
|
return 0;
|
|
}
|
|
|
|
if (tmp_subpart == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
*start = (td_u64)(tmp_subpart->from);
|
|
*length = (td_u64)(tmp_subpart->size);
|
|
soc_log_dbg("Got partition %s: start=0x%llX,size=%llu\n", ptn_name, *start, *length);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static td_s32 get_cmdline_subpart(td_u32 *part_index)
|
|
{
|
|
td_u32 partindex = 0;
|
|
struct cmdline_subpart *part = NULL;
|
|
|
|
if ((g_cmd_parts_head == NULL) || (g_cmd_parts_head->subpart == NULL)) {
|
|
soc_print("cmdline parts not initialized.\n");
|
|
return -1;
|
|
}
|
|
|
|
part = g_cmd_parts_head->subpart;
|
|
while ((part != NULL) && (partindex < MAX_PARTS)) {
|
|
if (strncpy_s(g_cmd_partition[partindex].part_name, BDEVNAME_SIZE, part->name, BDEVNAME_SIZE - 1) != 0) {
|
|
return -1;
|
|
}
|
|
g_cmd_partition[partindex].part_name[sizeof(g_cmd_partition[partindex].part_name) - 1] = '\0';
|
|
g_cmd_partition[partindex].start_addr = (part->from);
|
|
g_cmd_partition[partindex].part_size = (part->size);
|
|
|
|
partindex++;
|
|
part = part->next_subpart;
|
|
}
|
|
*part_index = partindex;
|
|
return 0;
|
|
}
|
|
|
|
/* 0 - success, -1 - fail */
|
|
td_s32 get_part_info(td_u8 partnum, td_u64 *start, td_u64 *size)
|
|
{
|
|
td_u32 partindex = 0;
|
|
|
|
if (start == NULL || size == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (get_cmdline_subpart(&partindex) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (partnum == 0) {
|
|
soc_print("partnum(%u) is invalid\n", (td_u32)partnum);
|
|
return -1;
|
|
}
|
|
if (partnum > partindex) {
|
|
soc_print("partnum is to large, max partnum is %u.\n", partindex);
|
|
return -1;
|
|
}
|
|
|
|
*start = (td_u64)g_cmd_partition[partnum - 1].start_addr;
|
|
*size = (td_u64)g_cmd_partition[partnum - 1].part_size;
|
|
|
|
return 0;
|
|
}
|