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

/*
* 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;
}