flatdevtree.c

来自「linux 内核源代码」· C语言 代码 · 共 1,037 行 · 第 1/2 页

C
1,037
字号
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. * * Copyright Pantelis Antoniou 2006 * Copyright (C) IBM Corporation 2006 * * Authors: Pantelis Antoniou <pantelis@embeddedalley.com> *	    Hollis Blanchard <hollisb@us.ibm.com> *	    Mark A. Greer <mgreer@mvista.com> *	    Paul Mackerras <paulus@samba.org> */#include <string.h>#include <stddef.h>#include "flatdevtree.h"#include "flatdevtree_env.h"#define _ALIGN(x, al)	(((x) + (al) - 1) & ~((al) - 1))static char *ft_root_node(struct ft_cxt *cxt){	return cxt->rgn[FT_STRUCT].start;}/* Routines for keeping node ptrs returned by ft_find_device current *//* First entry not used b/c it would return 0 and be taken as NULL/error */static void *ft_get_phandle(struct ft_cxt *cxt, char *node){	unsigned int i;	if (!node)		return NULL;	for (i = 1; i < cxt->nodes_used; i++)	/* already there? */		if (cxt->node_tbl[i] == node)			return (void *)i;	if (cxt->nodes_used < cxt->node_max) {		cxt->node_tbl[cxt->nodes_used] = node;		return (void *)cxt->nodes_used++;	}	return NULL;}static char *ft_node_ph2node(struct ft_cxt *cxt, const void *phandle){	unsigned int i = (unsigned int)phandle;	if (i < cxt->nodes_used)		return cxt->node_tbl[i];	return NULL;}static void ft_node_update_before(struct ft_cxt *cxt, char *addr, int shift){	unsigned int i;	if (shift == 0)		return;	for (i = 1; i < cxt->nodes_used; i++)		if (cxt->node_tbl[i] < addr)			cxt->node_tbl[i] += shift;}static void ft_node_update_after(struct ft_cxt *cxt, char *addr, int shift){	unsigned int i;	if (shift == 0)		return;	for (i = 1; i < cxt->nodes_used; i++)		if (cxt->node_tbl[i] >= addr)			cxt->node_tbl[i] += shift;}/* Struct used to return info from ft_next() */struct ft_atom {	u32 tag;	const char *name;	void *data;	u32 size;};/* Set ptrs to current one's info; return addr of next one */static char *ft_next(struct ft_cxt *cxt, char *p, struct ft_atom *ret){	u32 sz;	if (p >= cxt->rgn[FT_STRUCT].start + cxt->rgn[FT_STRUCT].size)		return NULL;	ret->tag = be32_to_cpu(*(u32 *) p);	p += 4;	switch (ret->tag) {	/* Tag */	case OF_DT_BEGIN_NODE:		ret->name = p;		ret->data = (void *)(p - 4);	/* start of node */		p += _ALIGN(strlen(p) + 1, 4);		break;	case OF_DT_PROP:		ret->size = sz = be32_to_cpu(*(u32 *) p);		ret->name = cxt->str_anchor + be32_to_cpu(*(u32 *) (p + 4));		ret->data = (void *)(p + 8);		p += 8 + _ALIGN(sz, 4);		break;	case OF_DT_END_NODE:	case OF_DT_NOP:		break;	case OF_DT_END:	default:		p = NULL;		break;	}	return p;}#define HDR_SIZE	_ALIGN(sizeof(struct boot_param_header), 8)#define EXPAND_INCR	1024	/* alloc this much extra when expanding *//* Copy the tree to a newly-allocated region and put things in order */static int ft_reorder(struct ft_cxt *cxt, int nextra){	unsigned long tot;	enum ft_rgn_id r;	char *p, *pend;	int stroff;	tot = HDR_SIZE + EXPAND_INCR;	for (r = FT_RSVMAP; r <= FT_STRINGS; ++r)		tot += cxt->rgn[r].size;	if (nextra > 0)		tot += nextra;	tot = _ALIGN(tot, 8);	if (!cxt->realloc)		return 0;	p = cxt->realloc(NULL, tot);	if (!p)		return 0;	memcpy(p, cxt->bph, sizeof(struct boot_param_header));	/* offsets get fixed up later */	cxt->bph = (struct boot_param_header *)p;	cxt->max_size = tot;	pend = p + tot;	p += HDR_SIZE;	memcpy(p, cxt->rgn[FT_RSVMAP].start, cxt->rgn[FT_RSVMAP].size);	cxt->rgn[FT_RSVMAP].start = p;	p += cxt->rgn[FT_RSVMAP].size;	memcpy(p, cxt->rgn[FT_STRUCT].start, cxt->rgn[FT_STRUCT].size);	ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start,			p - cxt->rgn[FT_STRUCT].start);	cxt->p += p - cxt->rgn[FT_STRUCT].start;	cxt->rgn[FT_STRUCT].start = p;	p = pend - cxt->rgn[FT_STRINGS].size;	memcpy(p, cxt->rgn[FT_STRINGS].start, cxt->rgn[FT_STRINGS].size);	stroff = cxt->str_anchor - cxt->rgn[FT_STRINGS].start;	cxt->rgn[FT_STRINGS].start = p;	cxt->str_anchor = p + stroff;	cxt->isordered = 1;	return 1;}static inline char *prev_end(struct ft_cxt *cxt, enum ft_rgn_id r){	if (r > FT_RSVMAP)		return cxt->rgn[r - 1].start + cxt->rgn[r - 1].size;	return (char *)cxt->bph + HDR_SIZE;}static inline char *next_start(struct ft_cxt *cxt, enum ft_rgn_id r){	if (r < FT_STRINGS)		return cxt->rgn[r + 1].start;	return (char *)cxt->bph + cxt->max_size;}/* * See if we can expand region rgn by nextra bytes by using up * free space after or before the region. */static int ft_shuffle(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn,		int nextra){	char *p = *pp;	char *rgn_start, *rgn_end;	rgn_start = cxt->rgn[rgn].start;	rgn_end = rgn_start + cxt->rgn[rgn].size;	if (nextra <= 0 || rgn_end + nextra <= next_start(cxt, rgn)) {		/* move following stuff */		if (p < rgn_end) {			if (nextra < 0)				memmove(p, p - nextra, rgn_end - p + nextra);			else				memmove(p + nextra, p, rgn_end - p);			if (rgn == FT_STRUCT)				ft_node_update_after(cxt, p, nextra);		}		cxt->rgn[rgn].size += nextra;		if (rgn == FT_STRINGS)			/* assumes strings only added at beginning */			cxt->str_anchor += nextra;		return 1;	}	if (prev_end(cxt, rgn) <= rgn_start - nextra) {		/* move preceding stuff */		if (p > rgn_start) {			memmove(rgn_start - nextra, rgn_start, p - rgn_start);			if (rgn == FT_STRUCT)				ft_node_update_before(cxt, p, -nextra);		}		*pp -= nextra;		cxt->rgn[rgn].start -= nextra;		cxt->rgn[rgn].size += nextra;		return 1;	}	return 0;}static int ft_make_space(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn,			 int nextra){	unsigned long size, ssize, tot;	char *str, *next;	enum ft_rgn_id r;	if (!cxt->isordered) {		unsigned long rgn_off = *pp - cxt->rgn[rgn].start;		if (!ft_reorder(cxt, nextra))			return 0;		*pp = cxt->rgn[rgn].start + rgn_off;	}	if (ft_shuffle(cxt, pp, rgn, nextra))		return 1;	/* See if there is space after the strings section */	ssize = cxt->rgn[FT_STRINGS].size;	if (cxt->rgn[FT_STRINGS].start + ssize			< (char *)cxt->bph + cxt->max_size) {		/* move strings up as far as possible */		str = (char *)cxt->bph + cxt->max_size - ssize;		cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start;		memmove(str, cxt->rgn[FT_STRINGS].start, ssize);		cxt->rgn[FT_STRINGS].start = str;		/* enough space now? */		if (rgn >= FT_STRUCT && ft_shuffle(cxt, pp, rgn, nextra))			return 1;	}	/* how much total free space is there following this region? */	tot = 0;	for (r = rgn; r < FT_STRINGS; ++r) {		char *r_end = cxt->rgn[r].start + cxt->rgn[r].size;		tot += next_start(cxt, rgn) - r_end;	}	/* cast is to shut gcc up; we know nextra >= 0 */	if (tot < (unsigned int)nextra) {		/* have to reallocate */		char *newp, *new_start;		int shift;		if (!cxt->realloc)			return 0;		size = _ALIGN(cxt->max_size + (nextra - tot) + EXPAND_INCR, 8);		newp = cxt->realloc(cxt->bph, size);		if (!newp)			return 0;		cxt->max_size = size;		shift = newp - (char *)cxt->bph;		if (shift) { /* realloc can return same addr */			cxt->bph = (struct boot_param_header *)newp;			ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start,					shift);			for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) {				new_start = cxt->rgn[r].start + shift;				cxt->rgn[r].start = new_start;			}			*pp += shift;			cxt->str_anchor += shift;		}		/* move strings up to the end */		str = newp + size - ssize;		cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start;		memmove(str, cxt->rgn[FT_STRINGS].start, ssize);		cxt->rgn[FT_STRINGS].start = str;		if (ft_shuffle(cxt, pp, rgn, nextra))			return 1;	}	/* must be FT_RSVMAP and we need to move FT_STRUCT up */	if (rgn == FT_RSVMAP) {		next = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size			+ nextra;		ssize = cxt->rgn[FT_STRUCT].size;		if (next + ssize >= cxt->rgn[FT_STRINGS].start)			return 0;	/* "can't happen" */		memmove(next, cxt->rgn[FT_STRUCT].start, ssize);		ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, nextra);		cxt->rgn[FT_STRUCT].start = next;		if (ft_shuffle(cxt, pp, rgn, nextra))			return 1;	}	return 0;		/* "can't happen" */}static void ft_put_word(struct ft_cxt *cxt, u32 v){	*(u32 *) cxt->p = cpu_to_be32(v);	cxt->p += 4;}static void ft_put_bin(struct ft_cxt *cxt, const void *data, unsigned int sz){	unsigned long sza = _ALIGN(sz, 4);	/* zero out the alignment gap if necessary */	if (sz < sza)		*(u32 *) (cxt->p + sza - 4) = 0;	/* copy in the data */	memcpy(cxt->p, data, sz);	cxt->p += sza;}char *ft_begin_node(struct ft_cxt *cxt, const char *name){	unsigned long nlen = strlen(name) + 1;	unsigned long len = 8 + _ALIGN(nlen, 4);	char *ret;	if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len))		return NULL;	ret = cxt->p;	ft_put_word(cxt, OF_DT_BEGIN_NODE);	ft_put_bin(cxt, name, strlen(name) + 1);	return ret;}void ft_end_node(struct ft_cxt *cxt){	ft_put_word(cxt, OF_DT_END_NODE);}void ft_nop(struct ft_cxt *cxt){	if (ft_make_space(cxt, &cxt->p, FT_STRUCT, 4))		ft_put_word(cxt, OF_DT_NOP);}#define NO_STRING	0x7fffffffstatic int lookup_string(struct ft_cxt *cxt, const char *name){	char *p, *end;	p = cxt->rgn[FT_STRINGS].start;	end = p + cxt->rgn[FT_STRINGS].size;	while (p < end) {		if (strcmp(p, (char *)name) == 0)			return p - cxt->str_anchor;		p += strlen(p) + 1;	}	return NO_STRING;}/* lookup string and insert if not found */static int map_string(struct ft_cxt *cxt, const char *name){	int off;	char *p;	off = lookup_string(cxt, name);	if (off != NO_STRING)		return off;	p = cxt->rgn[FT_STRINGS].start;	if (!ft_make_space(cxt, &p, FT_STRINGS, strlen(name) + 1))		return NO_STRING;	strcpy(p, name);	return p - cxt->str_anchor;}int ft_prop(struct ft_cxt *cxt, const char *name, const void *data,		unsigned int sz){	int off, len;	off = map_string(cxt, name);	if (off == NO_STRING)		return -1;	len = 12 + _ALIGN(sz, 4);	if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len))		return -1;	ft_put_word(cxt, OF_DT_PROP);	ft_put_word(cxt, sz);	ft_put_word(cxt, off);	ft_put_bin(cxt, data, sz);	return 0;}int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str){	return ft_prop(cxt, name, str, strlen(str) + 1);}int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val){	u32 v = cpu_to_be32((u32) val);	return ft_prop(cxt, name, &v, 4);}/* Calculate the size of the reserved map */static unsigned long rsvmap_size(struct ft_cxt *cxt){	struct ft_reserve *res;	res = (struct ft_reserve *)cxt->rgn[FT_RSVMAP].start;	while (res->start || res->len)		++res;	return (char *)(res + 1) - cxt->rgn[FT_RSVMAP].start;}/* Calculate the size of the struct region by stepping through it */static unsigned long struct_size(struct ft_cxt *cxt){	char *p = cxt->rgn[FT_STRUCT].start;	char *next;	struct ft_atom atom;	/* make check in ft_next happy */	if (cxt->rgn[FT_STRUCT].size == 0)		cxt->rgn[FT_STRUCT].size = 0xfffffffful - (unsigned long)p;	while ((next = ft_next(cxt, p, &atom)) != NULL)		p = next;	return p + 4 - cxt->rgn[FT_STRUCT].start;}/* add `adj' on to all string offset values in the struct area */static void adjust_string_offsets(struct ft_cxt *cxt, int adj){	char *p = cxt->rgn[FT_STRUCT].start;	char *next;	struct ft_atom atom;	int off;	while ((next = ft_next(cxt, p, &atom)) != NULL) {		if (atom.tag == OF_DT_PROP) {			off = be32_to_cpu(*(u32 *) (p + 8));			*(u32 *) (p + 8) = cpu_to_be32(off + adj);		}		p = next;	}}/* start construction of the flat OF tree from scratch */void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size,		void *(*realloc_fn) (void *, unsigned long)){	struct boot_param_header *bph = blob;	char *p;	struct ft_reserve *pres;	/* clear the cxt */	memset(cxt, 0, sizeof(*cxt));	cxt->bph = bph;	cxt->max_size = max_size;	cxt->realloc = realloc_fn;	cxt->isordered = 1;	/* zero everything in the header area */	memset(bph, 0, sizeof(*bph));	bph->magic = cpu_to_be32(OF_DT_HEADER);	bph->version = cpu_to_be32(0x10);	bph->last_comp_version = cpu_to_be32(0x10);	/* start pointers */	cxt->rgn[FT_RSVMAP].start = p = blob + HDR_SIZE;	cxt->rgn[FT_RSVMAP].size = sizeof(struct ft_reserve);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?