[RFC][PATCH] add FDT address translation

Walter Goossens waltergoossens at home.nl
Mon Feb 14 08:51:16 EST 2011


Thanks for reviewing, I'll create a new version.

Walter

On 2/12/11 10:11 AM, Grant Likely wrote:
> On Thu, Dec 30, 2010 at 12:38:00AM +0100, Walter Goossens wrote:
>> This patch adds address translations to fdt. Needed when the early
>> console is connected to a simple-bus that has address translation
>> enabled.
>>
>> To be honest, I'm not too happy about this solution but it works and
>> most code is stolen from other parts of the fdt code, so I'm
>> probably not doing too weird stuff :)
>> What do you guys think of this?
> Hi Walter,
>
> Thanks for the patch, comments below.
>
> g.
>
>> Greetz Walter
>> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
>> index c1360e0..d586efe 100644
>> --- a/drivers/of/fdt.c
>> +++ b/drivers/of/fdt.c
>> @@ -538,6 +538,161 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
>>   	/* break now */
>>   	return 1;
>>   }
>> +/**
>> + * flat_dt_translate_address - Translate an address using the ranges property
>> + *
>> + * This function converts address from "node address-space" to "parent address-
>> + * space"
>> + */
>> +static int __init flat_dt_translate_address(unsigned long node, unsigned long parent, u64 *address)
>> +{
>> +	unsigned long size = 0;
>> +	__be32 *prop;
>> +	__be32 *ranges;
>> +	int size_cells = 0;
>> +	int addr_cells = 0;
>> +	int paddr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
>> +
>> +	ranges = (u32 *)of_get_flat_dt_prop(node,"ranges",&size);
> Shouldn't need the cast here.  Also coding style.  Need spaced after each ','
>
> Lots of other style issues through this patch.  You should run this
> patch through scripts/checkpatch.pl before resubmitting.
>
>> +
>> +	if(size==0) {
> Coding style:
> 	if (size == 0) {
>
>> +		pr_debug("No translation possible/necessary\n");
> These are two different cases.  If ranges is *not* present, then the
> address cannot be translated and it looks like -EINVAL should be
> returned.  If ranges is empty, then the node address is 1:1 mapped to
> the parent address and success should be returned.
>
>> +		return 0;
>> +	}
>> +
>> +	prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
>> +	if (prop)
>> +		size_cells = be32_to_cpup(prop);
> I'd reverse the logic:
>
> 	prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
> 	if (!prop)
> 		return -EINVAL;
> 	size_cells = be32_to_cpup(prop);
>
>> +	pr_debug("size_cells = %x\n", size_cells);
>> +
>> +	prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
>> +	if (prop)
>> +		addr_cells = be32_to_cpup(prop);
> Ditto here.
>
> Also, before I dig too deep into the implementation, have you looked
> at dt_xlate() in arch/powerpc/boot/devtree.c?  It would need a bit of
> rework, but it is essentially the algorithm you want to implement.
>
>> +	pr_debug("addr_cells = %x\n", addr_cells);
>> +
>> +	if(parent) {
>> +		prop = of_get_flat_dt_prop(parent, "#address-cells", NULL);
>> +		if (prop)
>> +			paddr_cells = be32_to_cpup(prop);
>> +		pr_debug("paddr_cells = %x\n", paddr_cells);
>> +	}
>> +	if((addr_cells<=0)||(size_cells<=0)||
>> +		(addr_cells>2)||(size_cells>2)||(paddr_cells>2)) {
>> +		pr_warn("Translation not possible in fdt. Invalid address.\n");
>> +		*address = 0;
>> +		return -1;
>> +	}
>> +	pr_debug("FDT-Ranges:\n");
>> +	while(size>0) {
>> +		u64 from,to,tsize;
>> +		from = be32_to_cpup(ranges++);
>> +		size-=4;
>> +		if(addr_cells==2) {
>> +			from += (((u64)be32_to_cpup(ranges++))<<32);
>> +			size-=4;
>> +		}
>> +		to = be32_to_cpup(ranges++);
>> +		size-=4;
>> +		if(paddr_cells==2) {
>> +			to += (((u64)be32_to_cpup(ranges++))<<32);
>> +			size-=4;
>> +		}
>> +		tsize = be32_to_cpup(ranges++);
>> +		size-=4;
>> +		if(size_cells==2) {
>> +			tsize += (((u64)be32_to_cpup(ranges++))<<32);
>> +			size-=4;
>> +		}
>> +		pr_debug("  From %llX To %llX Size %llX\n", from, to, tsize);
>> +		if((*address>=from)&&(*address<(from+tsize)))
>> +			*address += (to - from);
>> +	}
>> +	return 1;
>> +}
>> +
>> +static int __init of_scan_flat_dt_ranges(unsigned long *pnode,
>> +				unsigned long parent, unsigned long target,
>> +				u64 *address, int ignore)
>> +{
>> +	int rc = 0;
>> +	int depth = -1;
>> +	char *pathp;
>> +	unsigned long p = *pnode;
>> +	do {
>> +		u32 tag = be32_to_cpup((__be32 *)p);
>> +
>> +		p += 4;
>> +		if (tag == OF_DT_END_NODE) {
>> +			if(depth--) {
>> +				break;
>> +			} else {
>> +				continue;
>> +			}
>> +		}
>> +		if (tag == OF_DT_NOP)
>> +			continue;
>> +		if (tag == OF_DT_END)
>> +			break;
>> +		if (tag == OF_DT_PROP) {
>> +			u32 sz = be32_to_cpup((__be32 *)p);
>> +			p += 8;
>> +			if (be32_to_cpu(initial_boot_params->version)<  0x10)
>> +				p = ALIGN(p, sz>= 8 ? 8 : 4);
>> +			p += sz;
>> +			p = ALIGN(p, 4);
>> +			continue;
>> +		}
>> +		if (tag != OF_DT_BEGIN_NODE) {
>> +			pr_err("Invalid tag %x in flat device tree!\n", tag);
>> +			return -EINVAL;
>> +		}
>> +		pathp = (char *)p;
>> +		p = ALIGN(p + strlen(pathp) + 1, 4);
>> +		if ((*pathp) == '/') {
>> +			char *lp, *np;
>> +			for (lp = NULL, np = pathp; *np; np++)
>> +				if ((*np) == '/')
>> +					lp = np+1;
>> +			if (lp != NULL)
>> +				pathp = lp;
>> +		}
>> +		if((ignore==0)&&  (p == target)) {
>> +			rc = 1;
>> +			ignore++;
>> +			pr_debug("Found target. Start address translation\n");
>> +		}
>> +		if(depth) {
>> +			int res;
>> +			*pnode=p;
>> +			res = of_scan_flat_dt_ranges(pnode, p, target,
>> +							address,ignore);
>> +			if(res<0) {
>> +				/* Something bad happened */
>> +				return -1;
>> +			} else if(res>0) {
>> +				/* Something gooed happened. Translate. */
>> +				rc++;
>> +				pr_debug("translate %08lX %s\n", p, pathp);
>> +				if(flat_dt_translate_address(p, parent,
>> +						address)<0) {
>> +					return -1;
>> +				}
>> +			}
>> +			p=*pnode;
>> +		} else {
>> +			depth++;
>> +		}
>> +	} while (1);
>> +	*pnode=p;
>> +	return rc;
>> +}
> You don't need to implement this.  drivers/of/fdt.c has functions that
> already parse the flattened tree.
>
>> +int __init early_init_dt_translate_address(unsigned long node, u64 *address)
>> +{
>> +	unsigned long p = ((unsigned long)initial_boot_params) +
>> +			be32_to_cpu(initial_boot_params->off_dt_struct);
> of_get_flat_dt_root()
>
>> +	return of_scan_flat_dt_ranges(&p, 0, node, address,0);
>> +}
>> +
>>
>>   /**
>>    * unflatten_device_tree - create tree of device_nodes from flat blob
>> diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
>> index 7bbf5b3..92e8694 100644
>> --- a/include/linux/of_fdt.h
>> +++ b/include/linux/of_fdt.h
>> @@ -82,6 +82,8 @@ extern void early_init_dt_add_memory_arch(u64 base, u64 size);
>>   extern u64 early_init_dt_alloc_memory_arch(u64 size, u64 align);
>>   extern u64 dt_mem_next_cell(int s, __be32 **cellp);
>>
>> +extern int early_init_dt_translate_address(unsigned long node, u64 *address);
>> +
>>   /*
>>    * If BLK_DEV_INITRD, the fdt early init code will call this function,
>>    * to be provided by the arch code. start and end are specified as
>> _______________________________________________
>> devicetree-discuss mailing list
>> devicetree-discuss at lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/devicetree-discuss
>



More information about the devicetree-discuss mailing list