diff mbox

[6/9] Introduce a simple iomap structure

Message ID 1386175377-23086-7-git-send-email-drjones@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Jones Dec. 4, 2013, 4:42 p.m. UTC
Add a simple structure and search function to the common code that
can be used for looking up a set of addresses by type. The user
must supply the iomaps[] table, e.g.

struct iomap iomaps[] = {
{
    .type = "virtio_mmio",
    .nr = 4,
    .addrs = { 0x2000000, 0x2000200, 0x2000400, 0x2000600, },
},
{
    .type = NULL,
},
};

Also add a script scripts/gen-devtree-iomaps.pl that can generate
the iomaps table from an fdt, e.g.

fdtdump dtb | scripts/gen-devtree-iomaps.pl - virtio_mmio

Signed-off-by: Andrew Jones <drjones@redhat.com>

---
v2:
  - switch to kernel coding style
  - rework fdt parsing to enable extraction of properties. Extract
    the 'compatible' property.
  - add iomaps_find_compatible() to allow searching for iomaps by
    its 'compatible' property.
---
 README                        |   1 +
 lib/iomaps.c                  |  31 +++++++++++++
 lib/iomaps.h                  |  14 ++++++
 scripts/gen-devtree-iomaps.pl | 105 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+)
 create mode 100644 lib/iomaps.c
 create mode 100644 lib/iomaps.h
 create mode 100755 scripts/gen-devtree-iomaps.pl

Comments

Christoffer Dall Dec. 29, 2013, 6:30 a.m. UTC | #1
On Wed, Dec 04, 2013 at 05:42:54PM +0100, Andrew Jones wrote:
> Add a simple structure and search function to the common code that
> can be used for looking up a set of addresses by type. The user
> must supply the iomaps[] table, e.g.
> 
> struct iomap iomaps[] = {
> {
>     .type = "virtio_mmio",
>     .nr = 4,
>     .addrs = { 0x2000000, 0x2000200, 0x2000400, 0x2000600, },
> },
> {
>     .type = NULL,
> },
> };
> 
> Also add a script scripts/gen-devtree-iomaps.pl that can generate
> the iomaps table from an fdt, e.g.
> 
> fdtdump dtb | scripts/gen-devtree-iomaps.pl - virtio_mmio
> 
> Signed-off-by: Andrew Jones <drjones@redhat.com>
> 
> ---
> v2:
>   - switch to kernel coding style
>   - rework fdt parsing to enable extraction of properties. Extract
>     the 'compatible' property.
>   - add iomaps_find_compatible() to allow searching for iomaps by
>     its 'compatible' property.
> ---
>  README                        |   1 +
>  lib/iomaps.c                  |  31 +++++++++++++
>  lib/iomaps.h                  |  14 ++++++
>  scripts/gen-devtree-iomaps.pl | 105 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 151 insertions(+)
>  create mode 100644 lib/iomaps.c
>  create mode 100644 lib/iomaps.h
>  create mode 100755 scripts/gen-devtree-iomaps.pl
> 
> diff --git a/README b/README
> index 0174679c05021..f834c61dc6112 100644
> --- a/README
> +++ b/README
> @@ -20,6 +20,7 @@ Directory structure:
>  .:		Makefile and config files for the tests
>  ./config:	config files for the tests
>  ./docs:		documentation files
> +./scripts:	misc helper scripts
>  ./lib:		general services for the tests
>  ./lib/<ARCH>:	architecture dependent services for the tests
>  ./<ARCH>:	the sources of the tests and the created objects/images
> diff --git a/lib/iomaps.c b/lib/iomaps.c
> new file mode 100644
> index 0000000000000..f2263b79dce96
> --- /dev/null
> +++ b/lib/iomaps.c
> @@ -0,0 +1,31 @@
> +#include "libcflat.h"
> +#include "iomaps.h"
> +
> +extern const struct iomap iomaps[];
> +
> +const struct iomap *iomaps_find_type(const char *type)
> +{
> +	const struct iomap *m = &iomaps[0];
> +
> +	while (m->type) {
> +		if (strcmp(m->type, type) == 0)
> +			return m;
> +		++m;
> +	}
> +	return NULL;
> +}
> +
> +const struct iomap *iomaps_find_compatible(const char *compat)
> +{
> +	const struct iomap *m = &iomaps[0];
> +	const char *c;
> +	int i;
> +
> +	while (m->type) {
> +		for (i = 0, c = m->compats[0]; c != NULL; c = m->compats[++i])
> +			if (strcmp(c, compat) == 0)
> +				return m;
> +		++m;
> +	}
> +	return NULL;
> +}
> diff --git a/lib/iomaps.h b/lib/iomaps.h
> new file mode 100644
> index 0000000000000..76a1aa4720337
> --- /dev/null
> +++ b/lib/iomaps.h
> @@ -0,0 +1,14 @@
> +#ifndef _IOMAPS_H_
> +#define _IOMAPS_H_
> +#include "libcflat.h"
> +
> +struct iomap {
> +	const char *type;
> +	const char *compats[5];

I would name the field compatible to be more in-line with the
DT-representation, but OK.

it looks from the above like the array must be null terminated?

how about #define IOMAP_MAX_COMPATS 5
and then turn your loop above into:

for (i = 0; i < IOMAP_MAX_COMPATS; i++, m++) {
	if (!m->compats[i])
		break;
	if (strcmp(m->compats[i], compat) == 0)
		return m;
}

> +	u32 nr;
> +	u32 addrs[64];

why are we limiting ourselves to a 32 bit physical address space?

> +};
> +
> +const struct iomap *iomaps_find_type(const char *type);
> +const struct iomap *iomaps_find_compatible(const char *compat);
> +#endif
> diff --git a/scripts/gen-devtree-iomaps.pl b/scripts/gen-devtree-iomaps.pl
> new file mode 100755
> index 0000000000000..b48e85e48ab34
> --- /dev/null
> +++ b/scripts/gen-devtree-iomaps.pl
> @@ -0,0 +1,105 @@
> +#!/usr/bin/perl -w
> +use strict;
> +use File::Temp qw/:POSIX/;
> +
> +my $dts = shift @ARGV;
> +my @types = @ARGV;
> +my $max_nr_addrs = 64;
> +my $max_nr_compats = 4;
> +
> +if (!defined $dts || $#types < 0) {
> +	print STDERR "Usage: gen-devtree-iomaps ".
> +		"<dts-file|-> <addr-type> [addr-types...]\n";
> +	exit 1;
> +}
> +
> +my $dtb = tmpnam();
> +system "dtc -I dts -O dtb $dts -o $dtb";
> +
> +my $g = join '|', map { $_ . '@' } @types;
> +my @devs = grep { /$g/ } `fdtget -l $dtb / 2>/dev/null`;
> +
> +my %iomaps;
> +foreach my $dev (@devs) {
> +
> +	chomp($dev);
> +	my ($type, $addr) = split /@/, $dev;
> +
> +	if (!exists $iomaps{$type}) {
> +
> +		my $compatible = `fdtget $dtb /$dev compatible 2>/dev/null`;
> +		chomp($compatible);
> +		my @compats = split ' ', $compatible;
> +
> +		$iomaps{$type}{compats} = \@compats;
> +		$iomaps{$type}{addrs} = [$addr];
> +	} else {
> +		push @{ $iomaps{$type}{addrs} }, $addr;
> +	}
> +}
> +unlink $dtb;
> +
> +print <<EOF;
> +/*
> + * Generated file. See gen-devtree-iomaps.pl
> + */
> +#include "iomaps.h"
> +EOF
> +print "\nconst struct iomap iomaps[] = {\n";
> +foreach my $type (keys %iomaps) {
> +
> +	my $compats = $iomaps{$type}{compats};
> +	my $addrs = $iomaps{$type}{addrs};
> +
> +	my $nr_compats = $#{ $compats } + 1;
> +	if ($nr_compats > $max_nr_compats) {
> +		print STDERR "$type has $nr_compats compats, but iomaps can ".
> +			"only support up to $max_nr_compats.\n";
> +		splice @{ $compats }, $max_nr_compats;
> +	}
> +
> +	@{ $addrs } = sort @{ $addrs };
> +
> +	my $nr = $#{ $addrs } + 1;
> +	if ($nr > $max_nr_addrs) {
> +		print STDERR "$type has $nr addrs, but iomaps can ".
> +			"only support up to $max_nr_addrs.\n";
> +		$nr = $max_nr_addrs;
> +		splice @{ $addrs }, $nr;
> +	}
> +
> +	@{ $addrs } = map { $_ = sprintf '0x%.8x', hex($_) } @{ $addrs };
> +
> +	print "{\n";
> +	print "\t.type = \"$type\",\n";
> +
> +	print "\t.compats = {";
> +	foreach my $compat (@{ $compats }) {
> +		print " \"$compat\",";
> +	}
> +	print " NULL, },\n";
> +
> +	print "\t.nr = $nr,\n";
> +	print "\t.addrs = {";
> +	if ($nr < 5) {
> +		print ' ';
> +		print join ', ', @{ $addrs };
> +		print ", },\n";
> +	} else {
> +		print "\n";
> +		for (my $i = 0; $i < $nr; $i += 5) {
> +			print "\t\t";
> +			my $j = $i;
> +			while ($j < $i + 4 && $j < $nr - 1) {
> +				print $addrs->[$j] . ", ";
> +				++$j;
> +			}
> +			print $addrs->[$j] . ",\n";
> +		}
> +		print "\t},\n";
> +	}
> +	print "},\n";
> +}
> +print "{\n\t.type = NULL,\n},\n";
> +print "};\n";
> +exit 0;

This script doesn't work on either of the Ubuntu distros I run.  The
reasons are that the dumpfdt tool is not processing the multi-compatible
strings output from the dtb correctly and the fdtget utility included
does not yet have the -l option to list subnodes.

I spent a fair amount of time trying to fix this, changing dumpfdt to
'dtc -I dtb -O dts', and I tried it on the newest Ubuntu distro, tried
compiling fdtget from the kernel sources etc. and failed miserably.

I think at the very least we need to check the tools available on the
build machine as part of the configure script or test it a little
broader.

Thanks,
Andrew Jones Jan. 2, 2014, 4:04 p.m. UTC | #2
On Sat, Dec 28, 2013 at 10:30:54PM -0800, Christoffer Dall wrote:
> > +struct iomap {
> > +	const char *type;
> > +	const char *compats[5];
> 
> I would name the field compatible to be more in-line with the
> DT-representation, but OK.
> 
> it looks from the above like the array must be null terminated?
> 
> how about #define IOMAP_MAX_COMPATS 5

defines are good.

> and then turn your loop above into:
> 
> for (i = 0; i < IOMAP_MAX_COMPATS; i++, m++) {
> 	if (!m->compats[i])
> 		break;

Doesn't improve the loop conditions much, IMO.

> 	if (strcmp(m->compats[i], compat) == 0)
> 		return m;
> }
> 
> > +	u32 nr;
> > +	u32 addrs[64];
> 
> why are we limiting ourselves to a 32 bit physical address space?

I have had this mentally noted from the beginning as something I'll
need to deal with when getting aarch64 going. Anyway, I think a lot
of this is going to change as I teach kvm-unit-tests more and more
about DT.

<snip>
> > +print "{\n\t.type = NULL,\n},\n";
> > +print "};\n";
> > +exit 0;
> 
> This script doesn't work on either of the Ubuntu distros I run.  The
> reasons are that the dumpfdt tool is not processing the multi-compatible
> strings output from the dtb correctly and the fdtget utility included
> does not yet have the -l option to list subnodes.
> 
> I spent a fair amount of time trying to fix this, changing dumpfdt to
> 'dtc -I dtb -O dts', and I tried it on the newest Ubuntu distro, tried
> compiling fdtget from the kernel sources etc. and failed miserably.
> 
> I think at the very least we need to check the tools available on the
> build machine as part of the configure script or test it a little
> broader.
>

Drat. I've preferred the idea of using the tools to parse the DT during
the build - avoiding the need for [cross-compiled] libfdt as a dep, but
I guess as we need more and more from the DT, or when particular tests
want to look at the DT itself, and there are already dependency problems,
then it's looking more and more like we should just use libfdt.

drew
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoffer Dall Jan. 2, 2014, 5:23 p.m. UTC | #3
On Thu, Jan 02, 2014 at 05:04:48PM +0100, Andrew Jones wrote:
> On Sat, Dec 28, 2013 at 10:30:54PM -0800, Christoffer Dall wrote:
> > > +struct iomap {
> > > +	const char *type;
> > > +	const char *compats[5];
> > 
> > I would name the field compatible to be more in-line with the
> > DT-representation, but OK.
> > 
> > it looks from the above like the array must be null terminated?
> > 
> > how about #define IOMAP_MAX_COMPATS 5
> 
> defines are good.
> 
> > and then turn your loop above into:
> > 
> > for (i = 0; i < IOMAP_MAX_COMPATS; i++, m++) {
> > 	if (!m->compats[i])
> > 		break;
> 
> Doesn't improve the loop conditions much, IMO.
> 

I think it's much simpler to read a for loop of the typical form of
until "i < CONSTANT";  what you had before was a data structure with an
assumption of null-termination that could only be realized by looking at
the code that deals with it.

> > 	if (strcmp(m->compats[i], compat) == 0)
> > 		return m;
> > }
> > 
> > > +	u32 nr;
> > > +	u32 addrs[64];
> > 
> > why are we limiting ourselves to a 32 bit physical address space?
> 
> I have had this mentally noted from the beginning as something I'll
> need to deal with when getting aarch64 going. Anyway, I think a lot
> of this is going to change as I teach kvm-unit-tests more and more
> about DT.

Shouldn't be a big change to just support u64 in your iomap instead.

> 
> <snip>
> > > +print "{\n\t.type = NULL,\n},\n";
> > > +print "};\n";
> > > +exit 0;
> > 
> > This script doesn't work on either of the Ubuntu distros I run.  The
> > reasons are that the dumpfdt tool is not processing the multi-compatible
> > strings output from the dtb correctly and the fdtget utility included
> > does not yet have the -l option to list subnodes.
> > 
> > I spent a fair amount of time trying to fix this, changing dumpfdt to
> > 'dtc -I dtb -O dts', and I tried it on the newest Ubuntu distro, tried
> > compiling fdtget from the kernel sources etc. and failed miserably.
> > 
> > I think at the very least we need to check the tools available on the
> > build machine as part of the configure script or test it a little
> > broader.
> >
> 
> Drat. I've preferred the idea of using the tools to parse the DT during
> the build - avoiding the need for [cross-compiled] libfdt as a dep, but
> I guess as we need more and more from the DT, or when particular tests
> want to look at the DT itself, and there are already dependency problems,
> then it's looking more and more like we should just use libfdt.
> 
Yes, after working with this for a while that's the same conclusion I
came to.  From the ARM perspective, anything running in a VM will be
device-tree driven (this is not the time for anybody to say anything
about ACPI), so we could just feed the VM the device tree directly if we
support libfdt in the guest and avoid another point of failure.  I can
try to find some time to help you hack that up if you want.
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Maydell Jan. 2, 2014, 5:32 p.m. UTC | #4
On 2 January 2014 16:04, Andrew Jones <drjones@redhat.com> wrote:
> Drat. I've preferred the idea of using the tools to parse the DT during
> the build - avoiding the need for [cross-compiled] libfdt as a dep, but
> I guess as we need more and more from the DT, or when particular tests
> want to look at the DT itself, and there are already dependency problems,
> then it's looking more and more like we should just use libfdt.

You can just have a local copy of the libfdt sources and compile
them in -- libfdt is written to support that (since you need to use
it that way for bootloaders and the like), and QEMU does that
as a fallback if there's no system libfdt. So you don't necessarily
need to require a cross-libfdt to be provided externally.

thanks
-- PMM
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrew Jones Jan. 2, 2014, 6:40 p.m. UTC | #5
On Thu, Jan 02, 2014 at 09:23:38AM -0800, Christoffer Dall wrote:
> On Thu, Jan 02, 2014 at 05:04:48PM +0100, Andrew Jones wrote:
> > On Sat, Dec 28, 2013 at 10:30:54PM -0800, Christoffer Dall wrote:
> > > > +struct iomap {
> > > > +	const char *type;
> > > > +	const char *compats[5];
> > > 
> > > I would name the field compatible to be more in-line with the
> > > DT-representation, but OK.
> > > 
> > > it looks from the above like the array must be null terminated?
> > > 
> > > how about #define IOMAP_MAX_COMPATS 5
> > 
> > defines are good.
> > 
> > > and then turn your loop above into:
> > > 
> > > for (i = 0; i < IOMAP_MAX_COMPATS; i++, m++) {
> > > 	if (!m->compats[i])
> > > 		break;
> > 
> > Doesn't improve the loop conditions much, IMO.
> > 
> 
> I think it's much simpler to read a for loop of the typical form of
> until "i < CONSTANT";  what you had before was a data structure with an
> assumption of null-termination that could only be realized by looking at
> the code that deals with it.
> 
> > > 	if (strcmp(m->compats[i], compat) == 0)
> > > 		return m;
> > > }
> > > 
> > > > +	u32 nr;
> > > > +	u32 addrs[64];
> > > 
> > > why are we limiting ourselves to a 32 bit physical address space?
> > 
> > I have had this mentally noted from the beginning as something I'll
> > need to deal with when getting aarch64 going. Anyway, I think a lot
> > of this is going to change as I teach kvm-unit-tests more and more
> > about DT.
> 
> Shouldn't be a big change to just support u64 in your iomap instead.
> 
> > 
> > <snip>
> > > > +print "{\n\t.type = NULL,\n},\n";
> > > > +print "};\n";
> > > > +exit 0;
> > > 
> > > This script doesn't work on either of the Ubuntu distros I run.  The
> > > reasons are that the dumpfdt tool is not processing the multi-compatible
> > > strings output from the dtb correctly and the fdtget utility included
> > > does not yet have the -l option to list subnodes.
> > > 
> > > I spent a fair amount of time trying to fix this, changing dumpfdt to
> > > 'dtc -I dtb -O dts', and I tried it on the newest Ubuntu distro, tried
> > > compiling fdtget from the kernel sources etc. and failed miserably.
> > > 
> > > I think at the very least we need to check the tools available on the
> > > build machine as part of the configure script or test it a little
> > > broader.
> > >
> > 
> > Drat. I've preferred the idea of using the tools to parse the DT during
> > the build - avoiding the need for [cross-compiled] libfdt as a dep, but
> > I guess as we need more and more from the DT, or when particular tests
> > want to look at the DT itself, and there are already dependency problems,
> > then it's looking more and more like we should just use libfdt.
> > 
> Yes, after working with this for a while that's the same conclusion I
> came to.  From the ARM perspective, anything running in a VM will be
> device-tree driven (this is not the time for anybody to say anything
> about ACPI), so we could just feed the VM the device tree directly if we
> support libfdt in the guest and avoid another point of failure.  I can
> try to find some time to help you hack that up if you want.

I'll start this work right away. Peter's import libfdt idea sounds like
the perfect solution.

drew
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoffer Dall Jan. 2, 2014, 9:05 p.m. UTC | #6
On Thu, Jan 02, 2014 at 07:40:14PM +0100, Andrew Jones wrote:
> On Thu, Jan 02, 2014 at 09:23:38AM -0800, Christoffer Dall wrote:
> > On Thu, Jan 02, 2014 at 05:04:48PM +0100, Andrew Jones wrote:
> > > On Sat, Dec 28, 2013 at 10:30:54PM -0800, Christoffer Dall wrote:
> > > > > +struct iomap {
> > > > > +	const char *type;
> > > > > +	const char *compats[5];
> > > > 
> > > > I would name the field compatible to be more in-line with the
> > > > DT-representation, but OK.
> > > > 
> > > > it looks from the above like the array must be null terminated?
> > > > 
> > > > how about #define IOMAP_MAX_COMPATS 5
> > > 
> > > defines are good.
> > > 
> > > > and then turn your loop above into:
> > > > 
> > > > for (i = 0; i < IOMAP_MAX_COMPATS; i++, m++) {
> > > > 	if (!m->compats[i])
> > > > 		break;
> > > 
> > > Doesn't improve the loop conditions much, IMO.
> > > 
> > 
> > I think it's much simpler to read a for loop of the typical form of
> > until "i < CONSTANT";  what you had before was a data structure with an
> > assumption of null-termination that could only be realized by looking at
> > the code that deals with it.
> > 
> > > > 	if (strcmp(m->compats[i], compat) == 0)
> > > > 		return m;
> > > > }
> > > > 
> > > > > +	u32 nr;
> > > > > +	u32 addrs[64];
> > > > 
> > > > why are we limiting ourselves to a 32 bit physical address space?
> > > 
> > > I have had this mentally noted from the beginning as something I'll
> > > need to deal with when getting aarch64 going. Anyway, I think a lot
> > > of this is going to change as I teach kvm-unit-tests more and more
> > > about DT.
> > 
> > Shouldn't be a big change to just support u64 in your iomap instead.
> > 
> > > 
> > > <snip>
> > > > > +print "{\n\t.type = NULL,\n},\n";
> > > > > +print "};\n";
> > > > > +exit 0;
> > > > 
> > > > This script doesn't work on either of the Ubuntu distros I run.  The
> > > > reasons are that the dumpfdt tool is not processing the multi-compatible
> > > > strings output from the dtb correctly and the fdtget utility included
> > > > does not yet have the -l option to list subnodes.
> > > > 
> > > > I spent a fair amount of time trying to fix this, changing dumpfdt to
> > > > 'dtc -I dtb -O dts', and I tried it on the newest Ubuntu distro, tried
> > > > compiling fdtget from the kernel sources etc. and failed miserably.
> > > > 
> > > > I think at the very least we need to check the tools available on the
> > > > build machine as part of the configure script or test it a little
> > > > broader.
> > > >
> > > 
> > > Drat. I've preferred the idea of using the tools to parse the DT during
> > > the build - avoiding the need for [cross-compiled] libfdt as a dep, but
> > > I guess as we need more and more from the DT, or when particular tests
> > > want to look at the DT itself, and there are already dependency problems,
> > > then it's looking more and more like we should just use libfdt.
> > > 
> > Yes, after working with this for a while that's the same conclusion I
> > came to.  From the ARM perspective, anything running in a VM will be
> > device-tree driven (this is not the time for anybody to say anything
> > about ACPI), so we could just feed the VM the device tree directly if we
> > support libfdt in the guest and avoid another point of failure.  I can
> > try to find some time to help you hack that up if you want.
> 
> I'll start this work right away. Peter's import libfdt idea sounds like
> the perfect solution.
> 
Agreed.

-Christoffer
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/README b/README
index 0174679c05021..f834c61dc6112 100644
--- a/README
+++ b/README
@@ -20,6 +20,7 @@  Directory structure:
 .:		Makefile and config files for the tests
 ./config:	config files for the tests
 ./docs:		documentation files
+./scripts:	misc helper scripts
 ./lib:		general services for the tests
 ./lib/<ARCH>:	architecture dependent services for the tests
 ./<ARCH>:	the sources of the tests and the created objects/images
diff --git a/lib/iomaps.c b/lib/iomaps.c
new file mode 100644
index 0000000000000..f2263b79dce96
--- /dev/null
+++ b/lib/iomaps.c
@@ -0,0 +1,31 @@ 
+#include "libcflat.h"
+#include "iomaps.h"
+
+extern const struct iomap iomaps[];
+
+const struct iomap *iomaps_find_type(const char *type)
+{
+	const struct iomap *m = &iomaps[0];
+
+	while (m->type) {
+		if (strcmp(m->type, type) == 0)
+			return m;
+		++m;
+	}
+	return NULL;
+}
+
+const struct iomap *iomaps_find_compatible(const char *compat)
+{
+	const struct iomap *m = &iomaps[0];
+	const char *c;
+	int i;
+
+	while (m->type) {
+		for (i = 0, c = m->compats[0]; c != NULL; c = m->compats[++i])
+			if (strcmp(c, compat) == 0)
+				return m;
+		++m;
+	}
+	return NULL;
+}
diff --git a/lib/iomaps.h b/lib/iomaps.h
new file mode 100644
index 0000000000000..76a1aa4720337
--- /dev/null
+++ b/lib/iomaps.h
@@ -0,0 +1,14 @@ 
+#ifndef _IOMAPS_H_
+#define _IOMAPS_H_
+#include "libcflat.h"
+
+struct iomap {
+	const char *type;
+	const char *compats[5];
+	u32 nr;
+	u32 addrs[64];
+};
+
+const struct iomap *iomaps_find_type(const char *type);
+const struct iomap *iomaps_find_compatible(const char *compat);
+#endif
diff --git a/scripts/gen-devtree-iomaps.pl b/scripts/gen-devtree-iomaps.pl
new file mode 100755
index 0000000000000..b48e85e48ab34
--- /dev/null
+++ b/scripts/gen-devtree-iomaps.pl
@@ -0,0 +1,105 @@ 
+#!/usr/bin/perl -w
+use strict;
+use File::Temp qw/:POSIX/;
+
+my $dts = shift @ARGV;
+my @types = @ARGV;
+my $max_nr_addrs = 64;
+my $max_nr_compats = 4;
+
+if (!defined $dts || $#types < 0) {
+	print STDERR "Usage: gen-devtree-iomaps ".
+		"<dts-file|-> <addr-type> [addr-types...]\n";
+	exit 1;
+}
+
+my $dtb = tmpnam();
+system "dtc -I dts -O dtb $dts -o $dtb";
+
+my $g = join '|', map { $_ . '@' } @types;
+my @devs = grep { /$g/ } `fdtget -l $dtb / 2>/dev/null`;
+
+my %iomaps;
+foreach my $dev (@devs) {
+
+	chomp($dev);
+	my ($type, $addr) = split /@/, $dev;
+
+	if (!exists $iomaps{$type}) {
+
+		my $compatible = `fdtget $dtb /$dev compatible 2>/dev/null`;
+		chomp($compatible);
+		my @compats = split ' ', $compatible;
+
+		$iomaps{$type}{compats} = \@compats;
+		$iomaps{$type}{addrs} = [$addr];
+	} else {
+		push @{ $iomaps{$type}{addrs} }, $addr;
+	}
+}
+unlink $dtb;
+
+print <<EOF;
+/*
+ * Generated file. See gen-devtree-iomaps.pl
+ */
+#include "iomaps.h"
+EOF
+print "\nconst struct iomap iomaps[] = {\n";
+foreach my $type (keys %iomaps) {
+
+	my $compats = $iomaps{$type}{compats};
+	my $addrs = $iomaps{$type}{addrs};
+
+	my $nr_compats = $#{ $compats } + 1;
+	if ($nr_compats > $max_nr_compats) {
+		print STDERR "$type has $nr_compats compats, but iomaps can ".
+			"only support up to $max_nr_compats.\n";
+		splice @{ $compats }, $max_nr_compats;
+	}
+
+	@{ $addrs } = sort @{ $addrs };
+
+	my $nr = $#{ $addrs } + 1;
+	if ($nr > $max_nr_addrs) {
+		print STDERR "$type has $nr addrs, but iomaps can ".
+			"only support up to $max_nr_addrs.\n";
+		$nr = $max_nr_addrs;
+		splice @{ $addrs }, $nr;
+	}
+
+	@{ $addrs } = map { $_ = sprintf '0x%.8x', hex($_) } @{ $addrs };
+
+	print "{\n";
+	print "\t.type = \"$type\",\n";
+
+	print "\t.compats = {";
+	foreach my $compat (@{ $compats }) {
+		print " \"$compat\",";
+	}
+	print " NULL, },\n";
+
+	print "\t.nr = $nr,\n";
+	print "\t.addrs = {";
+	if ($nr < 5) {
+		print ' ';
+		print join ', ', @{ $addrs };
+		print ", },\n";
+	} else {
+		print "\n";
+		for (my $i = 0; $i < $nr; $i += 5) {
+			print "\t\t";
+			my $j = $i;
+			while ($j < $i + 4 && $j < $nr - 1) {
+				print $addrs->[$j] . ", ";
+				++$j;
+			}
+			print $addrs->[$j] . ",\n";
+		}
+		print "\t},\n";
+	}
+	print "},\n";
+}
+print "{\n\t.type = NULL,\n},\n";
+print "};\n";
+exit 0;