* grub-core/lib/fdt.c: Fix miscellaneous bugs.

This commit is contained in:
Francesco Lavra 2013-10-28 13:52:32 +01:00 committed by Vladimir Serbinenko
parent 74124c344c
commit 526d3d25e2
2 changed files with 90 additions and 52 deletions

View file

@ -1,3 +1,7 @@
2013-10-28 Francesco Lavra <francescolavra.fl@gmail.com>
* grub-core/lib/fdt.c: Fix miscellaneous bugs.
2013-10-28 Vladimir Serbinenko <phcoder@gmail.com> 2013-10-28 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/lib/progress.c (grub_file_progress_hook_real): Add missing * grub-core/lib/progress.c (grub_file_progress_hook_real): Add missing

View file

@ -43,6 +43,16 @@
#define prop_entry_size(prop_len) \ #define prop_entry_size(prop_len) \
(3 * sizeof(grub_uint32_t) + ALIGN_UP(prop_len, sizeof(grub_uint32_t))) (3 * sizeof(grub_uint32_t) + ALIGN_UP(prop_len, sizeof(grub_uint32_t)))
#define SKIP_NODE_NAME(name, token, end) \
name = (char *) ((token) + 1); \
while (name < (char *) end) \
{ \
if (!*name++) \
break; \
} \
token = (grub_uint32_t *) ALIGN_UP((grub_addr_t) (name), sizeof(*token))
static grub_uint32_t *get_next_node (const void *fdt, char *node_name) static grub_uint32_t *get_next_node (const void *fdt, char *node_name)
{ {
grub_uint32_t *end = (void *) struct_end (fdt); grub_uint32_t *end = (void *) struct_end (fdt);
@ -50,9 +60,9 @@ static grub_uint32_t *get_next_node (const void *fdt, char *node_name)
if (node_name >= (char *) end) if (node_name >= (char *) end)
return NULL; return NULL;
while (*node_name) while (*node_name++)
{ {
if (++node_name >= (char *) end) if (node_name >= (char *) end)
return NULL; return NULL;
} }
token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4); token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4);
@ -73,7 +83,8 @@ static grub_uint32_t *get_next_node (const void *fdt, char *node_name)
case FDT_PROP: case FDT_PROP:
/* Skip property token and following data (len, nameoff and property /* Skip property token and following data (len, nameoff and property
value). */ value). */
token += 3 + grub_be_to_cpu32 (*(token + 1)); token += prop_entry_size(grub_be_to_cpu32(*(token + 1)))
/ sizeof(*token);
break; break;
case FDT_NOP: case FDT_NOP:
token++; token++;
@ -116,12 +127,14 @@ static grub_uint32_t get_free_space (void *fdt)
static int add_subnode (void *fdt, int parentoffset, const char *name) static int add_subnode (void *fdt, int parentoffset, const char *name)
{ {
grub_uint32_t *begin = (void *) ((grub_addr_t) fdt grub_uint32_t *token = (void *) ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct(fdt) + grub_fdt_get_off_dt_struct(fdt)
+ parentoffset); + parentoffset);
grub_uint32_t *end = (void *) struct_end (fdt); grub_uint32_t *end = (void *) struct_end (fdt);
unsigned int entry_size = node_entry_size (name); unsigned int entry_size = node_entry_size (name);
grub_uint32_t *token = begin; char *node_name;
SKIP_NODE_NAME(node_name, token, end);
/* Insert the new subnode just after the properties of the parent node (if /* Insert the new subnode just after the properties of the parent node (if
any).*/ any).*/
@ -132,28 +145,28 @@ static int add_subnode (void *fdt, int parentoffset, const char *name)
switch (grub_be_to_cpu32(*token)) switch (grub_be_to_cpu32(*token))
{ {
case FDT_PROP: case FDT_PROP:
/* Skip len and nameoff. */ /* Skip len, nameoff and property value. */
token += 2; token += prop_entry_size(grub_be_to_cpu32(*(token + 1)))
/ sizeof(*token);
break; break;
case FDT_BEGIN_NODE: case FDT_BEGIN_NODE:
case FDT_END_NODE: case FDT_END_NODE:
goto insert; goto insert;
case FDT_NOP: case FDT_NOP:
token++;
break; break;
default: default:
/* invalid token */ /* invalid token */
return -1; return -1;
} }
token++;
} }
insert: insert:
grub_memmove (token + entry_size, token, grub_memmove (token + entry_size / sizeof(*token), token,
(grub_addr_t) end - (grub_addr_t) token); (grub_addr_t) end - (grub_addr_t) token);
*token = grub_cpu_to_be32(FDT_BEGIN_NODE); *token = grub_cpu_to_be32(FDT_BEGIN_NODE);
token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */ token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */
grub_strcpy((char *) (token + 1), name); grub_strcpy((char *) (token + 1), name);
token += entry_size / sizeof(*token) - 1; token[entry_size / sizeof(*token) - 1] = grub_cpu_to_be32(FDT_END_NODE);
*token = grub_cpu_to_be32(FDT_END_NODE);
return ((grub_addr_t) token - (grub_addr_t) fdt return ((grub_addr_t) token - (grub_addr_t) fdt
- grub_fdt_get_off_dt_struct(fdt)); - grub_fdt_get_off_dt_struct(fdt));
} }
@ -175,29 +188,32 @@ static int rearrange_blocks (void *fdt, unsigned int clearance)
if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap) if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap)
&& (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct)) && (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct))
{ {
/* No need to allocate memory for a temporary FDT, just move the strings /* No need to allocate memory for a temporary FDT, just move the strings
block if needed. */ block if needed. */
if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings) if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings)
grub_memmove(fdt_ptr + off_dt_strings, {
fdt_ptr + grub_fdt_get_off_dt_strings (fdt), grub_memmove(fdt_ptr + off_dt_strings,
grub_fdt_get_size_dt_strings (fdt)); fdt_ptr + grub_fdt_get_off_dt_strings (fdt),
return 0; grub_fdt_get_size_dt_strings (fdt));
} grub_fdt_set_off_dt_strings (fdt, off_dt_strings);
}
return 0;
}
tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt)); tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt));
if (!tmp_fdt) if (!tmp_fdt)
return -1; return -1;
grub_memcpy (tmp_fdt + off_mem_rsvmap, grub_memcpy (tmp_fdt + off_mem_rsvmap,
fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt), fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt),
get_mem_rsvmap_size (fdt)); get_mem_rsvmap_size (fdt));
grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap); grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap);
grub_memcpy (tmp_fdt + off_dt_struct, grub_memcpy (tmp_fdt + off_dt_struct,
fdt_ptr + grub_fdt_get_off_dt_struct (fdt), fdt_ptr + grub_fdt_get_off_dt_struct (fdt),
grub_fdt_get_size_dt_struct (fdt)); grub_fdt_get_size_dt_struct (fdt));
grub_fdt_set_off_dt_struct (fdt, off_dt_struct); grub_fdt_set_off_dt_struct (fdt, off_dt_struct);
grub_memcpy (tmp_fdt + off_dt_strings, grub_memcpy (tmp_fdt + off_dt_strings,
fdt_ptr + grub_fdt_get_off_dt_strings (fdt), fdt_ptr + grub_fdt_get_off_dt_strings (fdt),
grub_fdt_get_size_dt_strings (fdt)); grub_fdt_get_size_dt_strings (fdt));
grub_fdt_set_off_dt_strings (fdt, off_dt_strings); grub_fdt_set_off_dt_strings (fdt, off_dt_strings);
/* Copy reordered blocks back to fdt. */ /* Copy reordered blocks back to fdt. */
@ -214,9 +230,12 @@ static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset,
grub_uint32_t *prop = (void *) ((grub_addr_t) fdt grub_uint32_t *prop = (void *) ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_off_dt_struct (fdt)
+ nodeoffset); + nodeoffset);
grub_uint32_t *end = (void *) struct_end(fdt);
grub_uint32_t nameoff; grub_uint32_t nameoff;
char *node_name;
do SKIP_NODE_NAME(node_name, prop, end);
while (prop < end - 2)
{ {
if (grub_be_to_cpu32(*prop) == FDT_PROP) if (grub_be_to_cpu32(*prop) == FDT_PROP)
{ {
@ -224,15 +243,19 @@ static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset,
if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt)) if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt))
&& !grub_strcmp (name, (char *) fdt + && !grub_strcmp (name, (char *) fdt +
grub_fdt_get_off_dt_strings (fdt) + nameoff)) grub_fdt_get_off_dt_strings (fdt) + nameoff))
return prop; {
prop += prop_entry_size(grub_be_to_cpu32(*prop + 1)) / sizeof (*prop); if (prop + prop_entry_size(grub_be_to_cpu32(*(prop + 1)))
/ sizeof (*prop) >= end)
return NULL;
return prop;
}
prop += prop_entry_size(grub_be_to_cpu32(*(prop + 1))) / sizeof (*prop);
} }
else if (grub_be_to_cpu32(*prop) != FDT_NOP) else if (grub_be_to_cpu32(*prop) == FDT_NOP)
prop++;
else
return NULL; return NULL;
prop++; }
} while ((grub_addr_t) prop < ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct (fdt)
+ grub_fdt_get_size_dt_struct (fdt)));
return NULL; return NULL;
} }
@ -271,6 +294,9 @@ int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
token = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) token = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt)
+ parentoffset); + parentoffset);
end = (void *) struct_end (fdt); end = (void *) struct_end (fdt);
if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE))
return -1;
SKIP_NODE_NAME(node_name, token, end);
while (token < end) while (token < end)
{ {
switch (grub_be_to_cpu32(*token)) switch (grub_be_to_cpu32(*token))
@ -280,19 +306,19 @@ int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
if (node_name + grub_strlen (name) >= (char *) end) if (node_name + grub_strlen (name) >= (char *) end)
return -1; return -1;
if (!grub_strcmp (node_name, name)) if (!grub_strcmp (node_name, name))
return (int) ((grub_addr_t) token return (int) ((grub_addr_t) token - (grub_addr_t) fdt
+ ALIGN_UP(grub_strlen (name) + 1, 4)
- grub_fdt_get_off_dt_struct (fdt)); - grub_fdt_get_off_dt_struct (fdt));
token = get_next_node (fdt, node_name); token = get_next_node (fdt, node_name);
if (!token) if (!token)
return -1; return -1;
break; break;
case FDT_END_NODE:
return -1;
case FDT_PROP: case FDT_PROP:
/* Skip property token and following data (len, nameoff and property /* Skip property token and following data (len, nameoff and property
value). */ value). */
token += 3 + grub_be_to_cpu32 (*(token + 1)); if (token >= end - 1)
return -1;
token += prop_entry_size(grub_be_to_cpu32(*(token + 1)))
/ sizeof(*token);
break; break;
case FDT_NOP: case FDT_NOP:
token++; token++;
@ -327,7 +353,10 @@ int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
int prop_name_present = 0; int prop_name_present = 0;
grub_uint32_t nameoff = 0; grub_uint32_t nameoff = 0;
if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3)) if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3)
|| (grub_be_to_cpu32(*(grub_uint32_t *) ((grub_addr_t) fdt
+ grub_fdt_get_off_dt_struct (fdt) + nodeoffset))
!= FDT_BEGIN_NODE))
return -1; return -1;
prop = find_prop (fdt, nodeoffset, name); prop = find_prop (fdt, nodeoffset, name);
if (prop) if (prop)
@ -339,7 +368,7 @@ int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
prop_name_present = 1; prop_name_present = 1;
for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++) for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++)
*(prop + 3 + i) = grub_cpu_to_be32 (FDT_NOP); *(prop + 3 + i) = grub_cpu_to_be32 (FDT_NOP);
if (len > prop_len) if (len > ALIGN_UP(prop_len, sizeof(grub_uint32_t)))
{ {
/* Length of new property value is greater than the space allocated /* Length of new property value is greater than the space allocated
for the current value: a new entry needs to be created, so save the for the current value: a new entry needs to be created, so save the
@ -371,19 +400,24 @@ int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
+ grub_strlen (name) + 1); + grub_strlen (name) + 1);
} }
if (!prop) { if (!prop) {
prop = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt) char *node_name = (char *) ((grub_addr_t) fdt
+ nodeoffset); + grub_fdt_get_off_dt_struct (fdt) + nodeoffset
grub_memmove (prop + prop_entry_size(len), prop, + sizeof(grub_uint32_t));
grub_fdt_get_size_dt_struct (fdt) - nodeoffset);
prop = (void *) (node_name + ALIGN_UP(grub_strlen(node_name) + 1, 4));
grub_memmove (prop + prop_entry_size(len) / sizeof(*prop), prop,
struct_end(fdt) - (grub_addr_t) prop);
grub_fdt_set_size_dt_struct (fdt, grub_fdt_get_size_dt_struct (fdt)
+ prop_entry_size(len));
*prop = grub_cpu_to_be32 (FDT_PROP); *prop = grub_cpu_to_be32 (FDT_PROP);
*(prop + 1) = grub_cpu_to_be32 (len);
*(prop + 2) = grub_cpu_to_be32 (nameoff); *(prop + 2) = grub_cpu_to_be32 (nameoff);
/* Insert padding bytes at the end of the value; if they are not needed,
they will be overwritten by the follozing memcpy. */
*(prop + prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0;
grub_memcpy (prop + 3, val, len);
} }
*(prop + 1) = grub_cpu_to_be32 (len);
/* Insert padding bytes at the end of the value; if they are not needed, they
will be overwritten by the following memcpy. */
*(prop + prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0;
grub_memcpy (prop + 3, val, len);
return 0; return 0;
} }