cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fdt.c (7762B)


      1// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
      2/*
      3 * libfdt - Flat Device Tree manipulation
      4 * Copyright (C) 2006 David Gibson, IBM Corporation.
      5 */
      6#include "libfdt_env.h"
      7
      8#include <fdt.h>
      9#include <libfdt.h>
     10
     11#include "libfdt_internal.h"
     12
     13/*
     14 * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
     15 * that the given buffer contains what appears to be a flattened
     16 * device tree with sane information in its header.
     17 */
     18int32_t fdt_ro_probe_(const void *fdt)
     19{
     20	uint32_t totalsize = fdt_totalsize(fdt);
     21
     22	if (can_assume(VALID_DTB))
     23		return totalsize;
     24
     25	/* The device tree must be at an 8-byte aligned address */
     26	if ((uintptr_t)fdt & 7)
     27		return -FDT_ERR_ALIGNMENT;
     28
     29	if (fdt_magic(fdt) == FDT_MAGIC) {
     30		/* Complete tree */
     31		if (!can_assume(LATEST)) {
     32			if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
     33				return -FDT_ERR_BADVERSION;
     34			if (fdt_last_comp_version(fdt) >
     35					FDT_LAST_SUPPORTED_VERSION)
     36				return -FDT_ERR_BADVERSION;
     37		}
     38	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
     39		/* Unfinished sequential-write blob */
     40		if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
     41			return -FDT_ERR_BADSTATE;
     42	} else {
     43		return -FDT_ERR_BADMAGIC;
     44	}
     45
     46	if (totalsize < INT32_MAX)
     47		return totalsize;
     48	else
     49		return -FDT_ERR_TRUNCATED;
     50}
     51
     52static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
     53{
     54	return (off >= hdrsize) && (off <= totalsize);
     55}
     56
     57static int check_block_(uint32_t hdrsize, uint32_t totalsize,
     58			uint32_t base, uint32_t size)
     59{
     60	if (!check_off_(hdrsize, totalsize, base))
     61		return 0; /* block start out of bounds */
     62	if ((base + size) < base)
     63		return 0; /* overflow */
     64	if (!check_off_(hdrsize, totalsize, base + size))
     65		return 0; /* block end out of bounds */
     66	return 1;
     67}
     68
     69size_t fdt_header_size_(uint32_t version)
     70{
     71	if (version <= 1)
     72		return FDT_V1_SIZE;
     73	else if (version <= 2)
     74		return FDT_V2_SIZE;
     75	else if (version <= 3)
     76		return FDT_V3_SIZE;
     77	else if (version <= 16)
     78		return FDT_V16_SIZE;
     79	else
     80		return FDT_V17_SIZE;
     81}
     82
     83size_t fdt_header_size(const void *fdt)
     84{
     85	return can_assume(LATEST) ? FDT_V17_SIZE :
     86		fdt_header_size_(fdt_version(fdt));
     87}
     88
     89int fdt_check_header(const void *fdt)
     90{
     91	size_t hdrsize;
     92
     93	/* The device tree must be at an 8-byte aligned address */
     94	if ((uintptr_t)fdt & 7)
     95		return -FDT_ERR_ALIGNMENT;
     96
     97	if (fdt_magic(fdt) != FDT_MAGIC)
     98		return -FDT_ERR_BADMAGIC;
     99	if (!can_assume(LATEST)) {
    100		if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
    101		    || (fdt_last_comp_version(fdt) >
    102			FDT_LAST_SUPPORTED_VERSION))
    103			return -FDT_ERR_BADVERSION;
    104		if (fdt_version(fdt) < fdt_last_comp_version(fdt))
    105			return -FDT_ERR_BADVERSION;
    106	}
    107	hdrsize = fdt_header_size(fdt);
    108	if (!can_assume(VALID_DTB)) {
    109
    110		if ((fdt_totalsize(fdt) < hdrsize)
    111		    || (fdt_totalsize(fdt) > INT_MAX))
    112			return -FDT_ERR_TRUNCATED;
    113
    114		/* Bounds check memrsv block */
    115		if (!check_off_(hdrsize, fdt_totalsize(fdt),
    116				fdt_off_mem_rsvmap(fdt)))
    117			return -FDT_ERR_TRUNCATED;
    118	}
    119
    120	if (!can_assume(VALID_DTB)) {
    121		/* Bounds check structure block */
    122		if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
    123			if (!check_off_(hdrsize, fdt_totalsize(fdt),
    124					fdt_off_dt_struct(fdt)))
    125				return -FDT_ERR_TRUNCATED;
    126		} else {
    127			if (!check_block_(hdrsize, fdt_totalsize(fdt),
    128					  fdt_off_dt_struct(fdt),
    129					  fdt_size_dt_struct(fdt)))
    130				return -FDT_ERR_TRUNCATED;
    131		}
    132
    133		/* Bounds check strings block */
    134		if (!check_block_(hdrsize, fdt_totalsize(fdt),
    135				  fdt_off_dt_strings(fdt),
    136				  fdt_size_dt_strings(fdt)))
    137			return -FDT_ERR_TRUNCATED;
    138	}
    139
    140	return 0;
    141}
    142
    143const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
    144{
    145	unsigned int uoffset = offset;
    146	unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
    147
    148	if (offset < 0)
    149		return NULL;
    150
    151	if (!can_assume(VALID_INPUT))
    152		if ((absoffset < uoffset)
    153		    || ((absoffset + len) < absoffset)
    154		    || (absoffset + len) > fdt_totalsize(fdt))
    155			return NULL;
    156
    157	if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
    158		if (((uoffset + len) < uoffset)
    159		    || ((offset + len) > fdt_size_dt_struct(fdt)))
    160			return NULL;
    161
    162	return fdt_offset_ptr_(fdt, offset);
    163}
    164
    165uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
    166{
    167	const fdt32_t *tagp, *lenp;
    168	uint32_t tag;
    169	int offset = startoffset;
    170	const char *p;
    171
    172	*nextoffset = -FDT_ERR_TRUNCATED;
    173	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
    174	if (!can_assume(VALID_DTB) && !tagp)
    175		return FDT_END; /* premature end */
    176	tag = fdt32_to_cpu(*tagp);
    177	offset += FDT_TAGSIZE;
    178
    179	*nextoffset = -FDT_ERR_BADSTRUCTURE;
    180	switch (tag) {
    181	case FDT_BEGIN_NODE:
    182		/* skip name */
    183		do {
    184			p = fdt_offset_ptr(fdt, offset++, 1);
    185		} while (p && (*p != '\0'));
    186		if (!can_assume(VALID_DTB) && !p)
    187			return FDT_END; /* premature end */
    188		break;
    189
    190	case FDT_PROP:
    191		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
    192		if (!can_assume(VALID_DTB) && !lenp)
    193			return FDT_END; /* premature end */
    194		/* skip-name offset, length and value */
    195		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
    196			+ fdt32_to_cpu(*lenp);
    197		if (!can_assume(LATEST) &&
    198		    fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
    199		    ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
    200			offset += 4;
    201		break;
    202
    203	case FDT_END:
    204	case FDT_END_NODE:
    205	case FDT_NOP:
    206		break;
    207
    208	default:
    209		return FDT_END;
    210	}
    211
    212	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
    213		return FDT_END; /* premature end */
    214
    215	*nextoffset = FDT_TAGALIGN(offset);
    216	return tag;
    217}
    218
    219int fdt_check_node_offset_(const void *fdt, int offset)
    220{
    221	if (!can_assume(VALID_INPUT)
    222	    && ((offset < 0) || (offset % FDT_TAGSIZE)))
    223		return -FDT_ERR_BADOFFSET;
    224
    225	if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
    226		return -FDT_ERR_BADOFFSET;
    227
    228	return offset;
    229}
    230
    231int fdt_check_prop_offset_(const void *fdt, int offset)
    232{
    233	if (!can_assume(VALID_INPUT)
    234	    && ((offset < 0) || (offset % FDT_TAGSIZE)))
    235		return -FDT_ERR_BADOFFSET;
    236
    237	if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
    238		return -FDT_ERR_BADOFFSET;
    239
    240	return offset;
    241}
    242
    243int fdt_next_node(const void *fdt, int offset, int *depth)
    244{
    245	int nextoffset = 0;
    246	uint32_t tag;
    247
    248	if (offset >= 0)
    249		if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
    250			return nextoffset;
    251
    252	do {
    253		offset = nextoffset;
    254		tag = fdt_next_tag(fdt, offset, &nextoffset);
    255
    256		switch (tag) {
    257		case FDT_PROP:
    258		case FDT_NOP:
    259			break;
    260
    261		case FDT_BEGIN_NODE:
    262			if (depth)
    263				(*depth)++;
    264			break;
    265
    266		case FDT_END_NODE:
    267			if (depth && ((--(*depth)) < 0))
    268				return nextoffset;
    269			break;
    270
    271		case FDT_END:
    272			if ((nextoffset >= 0)
    273			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
    274				return -FDT_ERR_NOTFOUND;
    275			else
    276				return nextoffset;
    277		}
    278	} while (tag != FDT_BEGIN_NODE);
    279
    280	return offset;
    281}
    282
    283int fdt_first_subnode(const void *fdt, int offset)
    284{
    285	int depth = 0;
    286
    287	offset = fdt_next_node(fdt, offset, &depth);
    288	if (offset < 0 || depth != 1)
    289		return -FDT_ERR_NOTFOUND;
    290
    291	return offset;
    292}
    293
    294int fdt_next_subnode(const void *fdt, int offset)
    295{
    296	int depth = 1;
    297
    298	/*
    299	 * With respect to the parent, the depth of the next subnode will be
    300	 * the same as the last.
    301	 */
    302	do {
    303		offset = fdt_next_node(fdt, offset, &depth);
    304		if (offset < 0 || depth < 1)
    305			return -FDT_ERR_NOTFOUND;
    306	} while (depth > 1);
    307
    308	return offset;
    309}
    310
    311const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
    312{
    313	int len = strlen(s) + 1;
    314	const char *last = strtab + tabsize - len;
    315	const char *p;
    316
    317	for (p = strtab; p <= last; p++)
    318		if (memcmp(p, s, len) == 0)
    319			return p;
    320	return NULL;
    321}
    322
    323int fdt_move(const void *fdt, void *buf, int bufsize)
    324{
    325	if (!can_assume(VALID_INPUT) && bufsize < 0)
    326		return -FDT_ERR_NOSPACE;
    327
    328	FDT_RO_PROBE(fdt);
    329
    330	if (fdt_totalsize(fdt) > (unsigned int)bufsize)
    331		return -FDT_ERR_NOSPACE;
    332
    333	memmove(buf, fdt, fdt_totalsize(fdt));
    334	return 0;
    335}