diff --git a/core/mem_region.c b/core/mem_region.c
index 3cc8ae0..56601e0 100644
--- a/core/mem_region.c
+++ b/core/mem_region.c
@@ -918,11 +918,30 @@ void mem_region_release_unused(void)
 	unlock(&mem_region_lock);
 }
 
+static void mem_region_add_dt_reserved_node(struct dt_node *parent,
+		struct mem_region *region)
+{
+	struct dt_node *node;
+	char *name, *p;
+
+	name = strdup(region->name);
+
+	/* remove any cell addresses in the region name; we have our own cell
+	 * addresses here */
+	p = strchr(name, '@');
+	if (p)
+		*p = '\0';
+
+	node = dt_new_addr(parent, name, region->start);
+	dt_add_property_u64s(node, "reg", region->start, region->len);
+}
+
 void mem_region_add_dt_reserved(void)
 {
 	int names_len, ranges_len, len;
 	struct mem_region *region;
 	void *names, *ranges;
+	struct dt_node *node;
 	uint64_t *range;
 	char *name;
 
@@ -936,6 +955,12 @@ void mem_region_add_dt_reserved(void)
 	mem_regions_finalised = true;
 	unlock(&mem_region_lock);
 
+	/* establish top-level reservation node */
+	node = dt_new(dt_root, "reserved-memory");
+	dt_add_property_cells(node, "#address-cells", 2);
+	dt_add_property_cells(node, "#size-cells", 2);
+	dt_add_property(node, "ranges", NULL, 0);
+
 	/* First pass: calculate length of property data */
 	list_for_each(&regions, region, list) {
 		if (!region_is_reserved(region))
@@ -961,6 +986,8 @@ void mem_region_add_dt_reserved(void)
 		       (long long)(region->start + region->len - 1),
 		       region->name);
 
+		mem_region_add_dt_reserved_node(node, region);
+
 		range[0] = cpu_to_fdt64(region->start);
 		range[1] = cpu_to_fdt64(region->len);
 		range += 2;
diff --git a/core/test/run-mem_region_reservations.c b/core/test/run-mem_region_reservations.c
index b33827f..8be3e67 100644
--- a/core/test/run-mem_region_reservations.c
+++ b/core/test/run-mem_region_reservations.c
@@ -108,13 +108,83 @@ static struct {
 	{ "test.3", 0x4000, false },
 };
 
-int main(void)
+static void check_property_reservations(void)
 {
 	const struct dt_property *names, *ranges;
-	struct mem_region *r;
-	unsigned int i, l, c;
-	uint64_t *rangep;
+	unsigned int i, l;
 	const char *name;
+	uint64_t *rangep;
+
+	/* check dt properties */
+	names = dt_find_property(dt_root, "reserved-names");
+	ranges = dt_find_property(dt_root, "reserved-ranges");
+
+	assert(names && ranges);
+
+	/* walk through names & ranges properies, ensuring that the test
+	 * regions are all present */
+	for (name = names->prop, rangep = (uint64_t *)ranges->prop;
+			name < names->prop + names->len;
+			name += l, rangep += 2) {
+		uint64_t addr;
+
+		addr = dt_get_number(rangep, 2);
+		l = strlen(name) + 1;
+
+		for (i = 0; i < ARRAY_SIZE(test_regions); i++) {
+			if (strcmp(test_regions[i].name, name))
+				continue;
+			assert(test_regions[i].addr == addr);
+			assert(!test_regions[i].found);
+			test_regions[i].found = true;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(test_regions); i++) {
+		assert(test_regions[i].found);
+		test_regions[i].found = false;
+	}
+}
+
+static void check_node_reservations(void)
+{
+	struct dt_node *parent, *node;
+	unsigned int i;
+
+	parent = dt_find_by_name(dt_root, "reserved-memory");
+	assert(parent);
+
+	assert(dt_prop_get_cell(parent, "#address-cells", 0) == 2);
+	assert(dt_prop_get_cell(parent, "#size-cells", 0) == 2);
+	dt_require_property(parent, "ranges", 0);
+
+	dt_for_each_child(parent, node) {
+		uint64_t addr, size;
+
+		addr = dt_get_address(node, 0, &size);
+
+		for (i = 0; i < ARRAY_SIZE(test_regions); i++) {
+			if (strncmp(test_regions[i].name, node->name,
+						strlen(test_regions[i].name)))
+				continue;
+
+			assert(!test_regions[i].found);
+			assert(test_regions[i].addr == addr);
+			assert(size = 0x1000);
+			test_regions[i].found = true;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(test_regions); i++) {
+		assert(test_regions[i].found);
+		test_regions[i].found = false;
+	}
+}
+
+int main(void)
+{
+	struct mem_region *r;
+	unsigned int i;
 	void *buf;
 
 	/* Use malloc for the heap, so valgrind can find issues. */
@@ -146,33 +216,11 @@ int main(void)
 	r = new_region("test.4", 0x5000, 0x1000, NULL, REGION_RESERVED);
 	assert(!add_region(r));
 
-	/* check dt properties */
-	names = dt_find_property(dt_root, "reserved-names");
-	ranges = dt_find_property(dt_root, "reserved-ranges");
-
-	assert(names && ranges);
-
-	/* walk through names & ranges properies, ensuring that the test
-	 * regions are all present */
-	for (name = names->prop, rangep = (uint64_t *)ranges->prop, c = 0;
-			name < names->prop + names->len;
-			name += l, rangep += 2) {
-		uint64_t addr;
-
-		addr = dt_get_number(rangep, 2);
-		l = strlen(name) + 1;
-
-		for (i = 0; i < ARRAY_SIZE(test_regions); i++) {
-			if (strcmp(test_regions[i].name, name))
-				continue;
-			assert(test_regions[i].addr == addr);
-			assert(!test_regions[i].found);
-			test_regions[i].found = true;
-			c++;
-		}
-	}
+	/* check old property-style reservations */
+	check_property_reservations();
 
-	assert(c == ARRAY_SIZE(test_regions));
+	/* and new node-style reservations */
+	check_node_reservations();
 
 	dt_free(dt_root);
 	free((void *)(long)skiboot_heap.start);
diff --git a/doc/device-tree/reserved-memory.txt b/doc/device-tree/reserved-memory.txt
new file mode 100644
index 0000000..48e32d4
--- /dev/null
+++ b/doc/device-tree/reserved-memory.txt
@@ -0,0 +1,26 @@
+reserved-memory device tree nodes
+
+OPAL exposes reserved memory through a top-level reserved-memory node,
+containing subnodes that represent each reserved memory region.
+
+This follows the Linux specification for the /reserved-memory node,
+described in the kernel source tree, in:
+
+  Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+
+The top-level /reserved-memory node contains:
+
+  #size-cells = <2>
+  #address-cells = <2>
+   - addresses and sizes are all 64-bits
+
+  ranges;
+   - the empty ranges node indicates no translation of physical
+     addresses in the subnodes.
+
+The sub-nodes under the /reserved-memory node contain:
+
+ reg = <address size>
+  - the address and size of the reserved memory region. The address
+    and size values are two cells each, as sig
+
