diff --git a/core/Makefile.inc b/core/Makefile.inc
index b09c30c0..7c247836 100644
--- a/core/Makefile.inc
+++ b/core/Makefile.inc
@@ -8,7 +8,7 @@ CORE_OBJS += pci-opal.o fast-reboot.o device.o exceptions.o trace.o affinity.o
 CORE_OBJS += vpd.o hostservices.o platform.o nvram.o nvram-format.o hmi.o
 CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o
 CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o
-CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o
+CORE_OBJS += flash-subpartition.o bitmap.o buddy.o pci-quirk.o cpufeatures.o
 
 ifeq ($(SKIBOOT_GCOV),1)
 CORE_OBJS += gcov-profiling.o
diff --git a/core/cpufeatures.c b/core/cpufeatures.c
new file mode 100644
index 00000000..2c47886b
--- /dev/null
+++ b/core/cpufeatures.c
@@ -0,0 +1,784 @@
+/* Copyright 2017 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <skiboot.h>
+#include <cpu.h>
+#include <ccan/str/str.h>
+#include <device.h>
+
+/* Table to set up the /cpus/cpufeatures dt */
+#define USABLE_PR		(1U << 0)
+#define USABLE_OS		(1U << 1)
+#define USABLE_HV		(1U << 2)
+
+#define HV_SUPPORT_NONE		0
+#define HV_SUPPORT_CUSTOM	1
+#define HV_SUPPORT_HFSCR	2
+
+#define OS_SUPPORT_NONE		0
+#define OS_SUPPORT_CUSTOM	1
+#define OS_SUPPORT_FSCR		2
+
+#define CPU_P8_DD1		(1U << CPUFEATURES_CPU_P8_DD1)
+#define CPU_P8_DD2		(1U << CPUFEATURES_CPU_P8_DD2)
+#define CPU_P9_DD1		(1U << CPUFEATURES_CPU_P9_DD1)
+#define CPU_P9_DD2		(1U << CPUFEATURES_CPU_P9_DD2)
+
+#define CPU_P8			(CPU_P8_DD1|CPU_P8_DD2)
+#define CPU_P9			(CPU_P9_DD1|CPU_P9_DD2)
+#define CPU_ALL			(CPU_P8|CPU_P9)
+
+#define ISA_BASE		0
+#define ISA_V3			3000
+
+struct cpu_feature {
+	const char *name;
+	uint32_t cpus_supported;
+	uint32_t isa;
+	uint32_t usable_mask;
+	uint32_t hv_support;
+	uint32_t os_support;
+	uint32_t hfscr_bit_nr;
+	uint32_t fscr_bit_nr;
+	uint32_t hwcap_bit_nr;
+	const char *dependencies_names; /* space-delimited names */
+};
+
+/*
+ * The base (or NULL) cpu feature set is the CPU features available
+ * when no child nodes of the /cpus/features node exist. The base feature
+ * set is POWER8 (ISAv2.07), less features that are listed explicitly.
+ *
+ * There will be a /cpus/features/isa property that specifies the currently
+ * active ISA level. Those architected features without explicit nodes
+ * will match the current ISA level. A greater ISA level will imply some
+ * features are phased out.
+ */
+static const struct cpu_feature cpu_features_table[] = {
+	/*
+	 * Big endian as in ISAv2.07, MSR_LE=0
+	 */
+	{ "big-endian",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * Little endian as in ISAv2.07, MSR_LE=1. When big and little endian
+	 * are defined, there is an LPCR ILE bit and implementation specific
+	 * way to switch HILE mode.
+	 */
+	{ "little-endian",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * MSR_HV=1 mode as in ISAv2.07 (i.e., hypervisor privileged
+	 * instructions and registers).
+	 */
+	{ "hypervisor",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * Interrupt vectors and register modes as in ISAv2.07
+	 * (e.g., AIL, ILE, HV, etc LPCR bits).
+	 */
+	{ "interrupt-facilities",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	{ "smt",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, 14,
+	NULL, },
+
+	/*
+	 * PPR, RPR, PSPB registers, priority "or" instructions, as in
+	 * ISAv2.07.
+	 */
+	{ "program-priority-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 Book3S Chapter 5.7.9.1. Virtual Page Class Key Protecion
+	 * AMR, IAMR, AMOR, UAMOR, etc registers and MMU key bits.
+	 */
+	{ "virtual-page-class-key-protection",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 SAO storage control attribute
+	 */
+	{ "strong-access-ordering",
+	CPU_ALL & ~CPU_P9_DD1,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 no-execute storage control attribute
+	 */
+	{ "no-execute",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * Cache inhibited attribute on large pages.
+	 */
+	{ "cache-inhibited-large-page",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 Book3S Chapter 8. Debug Facilities
+	 * CIEA, CIABR, DEAW, MEte, trace interrupt, etc.
+	 * Except CFAR, branch tracing.
+	 */
+	{ "debug-facilities",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 CFAR
+	 */
+	{ "come-from-address-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	"debug-facilities", },
+
+	/*
+	 * ISAv2.07 Branch tracing (optional in ISA)
+	 */
+	{ "branch-tracing",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	"debug-facilities", },
+
+	/*
+	 * ISAv2.07 FPU instructions and registers.
+	 */
+	{ "floating-point",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(63), -1, 27,
+	NULL, },
+
+	/*
+	 * VSX / VMX
+	 * XXX: should vector-scalar be separate?
+	 */
+	{ "vector",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(62), -1, -1 /* 28 and 7 */,
+	"floating-point", },
+
+	{ "vector-crypto",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, 57,
+	"vector", },
+
+	/* BCD */
+	{ "decimal-integer",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/* DFP */
+	{ "decimal-floating-point",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, 10,
+	"floating-point", },
+
+	/* DSCR */
+	{ "data-stream-control-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(61), -1, 61,
+	NULL, },
+
+	/* BHRB */
+	{ "branch-history-rolling-buffer",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(59), -1, -1,
+	NULL, },
+
+	/* HTM */
+	{ "transactional-memory",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(58), -1, 62,
+	NULL, },
+
+	/*
+	 * ISAv3 transactional memory which provides
+	 * TEXASR bit 17, self-induced vs external footprint overflow
+	 */
+	{ "transactional-memory-v3",
+	CPU_ALL,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"transactional-memory", },
+
+	/* EBB */
+	{ "event-based-branch",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(56), PPC_BITLSHIFT(56), 60,
+	NULL, },
+
+	/* TAR */
+	{ "target-address-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	PPC_BITLSHIFT(55), PPC_BITLSHIFT(55), 58,
+	NULL, },
+
+	/* CTRL */
+	{ "control-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 Book3S Chapter 11. Processor Control.
+	 * msgsnd, msgsndp, doorbell, etc.
+	 *
+	 * ISAv3 is not compatible (different addressing, HFSCR required
+	 * for msgsndp).
+	 */
+	{ "processor-control-facility",
+	CPU_P8_DD2, /* P8 DD1 has no dbell */
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 PURR, SPURR registers
+	 */
+	{ "processor-utilization-of-resources-register",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	{ "coprocessor-icswx",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 hash based MMU and all instructions, registers,
+	 * data structures, exceptions, etc.
+	 */
+	{ "mmu-hash",
+	CPU_P8,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	{ "machine-check-power8",
+	CPU_P8,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	{ "performance-monitor-power8",
+	CPU_P8,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * POWER CPUs do not used this, and it's removed from ISAv3.
+	 */
+	{ "alignment-interrupt-dsisr",
+	0,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 / POWER8 doze, nap, sleep, winkle instructions
+	 * XXX: is Linux we using some BookIV specific implementation details
+	 * in nap handling? We have no POWER8 specific key. How about for
+	 * ISAv3 stop implementation?
+	 */
+	{ "idle-nap",
+	CPU_P8,
+	ISA_BASE, USABLE_HV,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 wait instruction
+	 */
+	{ "wait",
+	CPU_P8,
+	ISA_BASE, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	{ "subcore",
+	CPU_P8,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	"smt", },
+
+	/*
+	 * ISAv3.0 radix based MMU
+	 */
+	{ "mmu-radix",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3.0 hash based MMU, new hash pte format, PCTR, etc
+	 */
+	{ "mmu-hash-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 wait instruction
+	 */
+	{ "wait-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 stop idle instructions and registers
+	 */
+	{ "idle-stop",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	{ "hypervisor-virtualization-interrupt",
+	CPU_P9,
+	ISA_V3, USABLE_HV,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * POWER9 MCE / machine check exception.
+	 */
+	{ "machine-check-power9",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * POWER9 PMU / performance monitor unit.
+	 */
+	{ "performance-monitor-power9",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_CUSTOM,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 scv/rfscv system call instructions and exceptions, fscr bit
+	 * etc.
+	 */
+	{ "system-call-vectored",
+	CPU_P9,
+	ISA_V3, USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_CUSTOM,
+	-1, PPC_BITLSHIFT(51), -1,
+	NULL, },
+
+	/*
+	 * ISAv3 Book3S Chapter 10. Processor Control.
+	 * global msgsnd, msgsndp, msgsync, doorbell, etc.
+	 */
+	{ "processor-control-facility-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_CUSTOM, OS_SUPPORT_NONE,
+	PPC_BITLSHIFT(53), -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 addpcis instruction
+	 */
+	{ "pc-relative-addressing",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv2.07 Book3S Chapter 7. Timer Facilities
+	 * TB, VTB, DEC, HDEC, IC, etc registers and exceptions.
+	 * Not including PURR or SPURR registers.
+	 */
+	{ "timer-facilities",
+	CPU_ALL,
+	ISA_BASE, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 Book3S Chapter 7. Timer Facilities
+	 * Large decrementer and hypervisor decrementer
+	 */
+	{ "timer-facilities-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"timer-facilities", },
+
+	/*
+	 * ISAv3 deliver a random number instruction (darn)
+	 */
+	{ "random-number-generator",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 fixed point instructions
+	 * multiply-add, modulo, count trailing zeroes, cmprb, cmpeqb,
+	 * extswsli, mfvsrld, mtvsrdd, mtvsrws, addex
+	 */
+	{ "fixed-point-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3 lightweight mffs
+	 */
+	{ "floating-point-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"floating-point", },
+
+	{ "decimal-integer-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"decimal-integer", },
+
+	{ "decimal-floating-point-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"decimal-floating-point", },
+
+	{ "vector-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"vector", },
+
+	{ "vector-binary128",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, 54,
+	"vector-v3", },
+
+	{ "vector-binary16",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"vector-v3", },
+
+	/*
+	 * ISAv3 branch instruction and register additions
+	 * CA32, OV32, mcrxrx, setb
+	 */
+	{ "branch-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3
+	 * external event based branch
+	 */
+	{ "event-based-branch-v3",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	"event-based-branch", },
+
+	{ "atomic-memory-operations",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	{ "copy-paste",
+	CPU_P9,
+	ISA_V3, USABLE_HV|USABLE_OS|USABLE_PR,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+
+	/*
+	 * ISAv3
+	 * GSR SPR register
+	 * ISAv3 includes this but POWER9 does not implement it.
+	 */
+	{ "group-start-register",
+	0,
+	ISA_V3, USABLE_HV|USABLE_OS,
+	HV_SUPPORT_NONE, OS_SUPPORT_NONE,
+	-1, -1, -1,
+	NULL, },
+};
+
+static void add_cpu_feature_nodeps(struct dt_node *features, const struct cpu_feature *f)
+{
+	struct dt_node *feature;
+
+	feature = dt_new(features, f->name);
+	assert(feature);
+
+	dt_add_property_cells(feature, "isa", f->isa);
+	dt_add_property_cells(feature, "usable-mask", f->usable_mask);
+
+	if (f->usable_mask & USABLE_HV) {
+		if (f->hv_support != HV_SUPPORT_NONE) {
+			dt_add_property_cells(feature, "hv-support", f->hv_support);
+			if (f->hfscr_bit_nr != -1)
+				dt_add_property_cells(feature, "hfscr-bit-nr", f->hfscr_bit_nr);
+		} else {
+			assert(f->hfscr_bit_nr == -1);
+		}
+	}
+
+	if (f->usable_mask & USABLE_OS) {
+		if (f->os_support != OS_SUPPORT_NONE) {
+			dt_add_property_cells(feature, "os-support", f->os_support);
+			if (f->fscr_bit_nr != -1)
+				dt_add_property_cells(feature, "fscr-bit-nr", f->fscr_bit_nr);
+		} else {
+			assert(f->fscr_bit_nr == -1);
+		}
+	}
+
+	if (f->usable_mask & USABLE_PR) {
+		if (f->hwcap_bit_nr != -1)
+			dt_add_property_cells(feature, "hwcap-bit-nr", f->hwcap_bit_nr);
+	}
+
+	if (f->dependencies_names)
+		dt_add_property(feature, "dependencies", NULL, 0);
+}
+
+void add_cpufeatures(struct dt_node *cpus,
+		uint32_t cpu_feature_isa, uint32_t cpu_feature_cpu)
+{
+	struct dt_node *features;
+	struct dt_node *feature;
+	int i;
+
+	features = dt_new(cpus, "features");
+	assert(features);
+
+	dt_add_property_cells(features, "isa", cpu_feature_isa);
+
+	dt_add_property_string(features, "device_type", "cpu-features");
+
+	for (i = 0; i < ARRAY_SIZE(cpu_features_table); i++) {
+		const struct cpu_feature *f = &cpu_features_table[i];
+
+		if (f->cpus_supported & cpu_feature_cpu)
+			add_cpu_feature_nodeps(features, f);
+	}
+
+	/* dependency construction pass */
+	dt_for_each_node(features, feature) {
+		const struct cpu_feature *f;
+		const char *deps_names;
+		struct dt_property *deps;
+		int nr_deps;
+		int i;
+
+		/* Find features with dependencies */
+
+		deps = __dt_find_property(feature, "dependencies");
+		if (!deps)
+			continue;
+
+		/* Find the matching cpu table */
+		for (i = 0; i < ARRAY_SIZE(cpu_features_table); i++) {
+			f = &cpu_features_table[i];
+			if (!strcmp(f->name, feature->name))
+				break;
+		}
+		assert(f->dependencies_names);
+
+		/*
+		 * Count number of depended features and allocate space
+		 * for phandles in the property.
+		 */
+		deps_names = f->dependencies_names;
+		nr_deps = strcount(deps_names, " ") + 1;
+		dt_resize_property(&deps, nr_deps * sizeof(u32));
+		deps->len = nr_deps * sizeof(u32);
+
+		/*
+		 * For each one, find the depended feature then advance to
+		 * next name.
+		 */
+		for (i = 0; i < nr_deps; i++) {
+			struct dt_node *dep;
+
+			dt_for_each_node(features, dep) {
+				if (strstarts(deps_names, dep->name))
+					break;
+			}
+
+			dt_property_set_cell(deps, i, dep->phandle);
+
+			/* Advance over the name + delimiter */
+			deps_names += strlen(dep->name) + 1;
+		}
+	}
+}
+
+void dt_add_cpufeatures(struct dt_node *root)
+{
+	int version;
+	uint32_t cpu_feature_isa = 0;
+	uint32_t cpu_feature_cpu = 0;
+	struct dt_node *cpus;
+
+	version = mfspr(SPR_PVR);
+	switch(PVR_TYPE(version)) {
+	case PVR_TYPE_P8:
+	case PVR_TYPE_P8E:
+	case PVR_TYPE_P8NVL:
+		cpu_feature_isa = CPUFEATURES_ISA_V2_07;
+		if (PVR_VERS_MAJ(version) == 1)
+			cpu_feature_cpu = CPUFEATURES_CPU_P8_DD1;
+		else
+			cpu_feature_cpu = CPUFEATURES_CPU_P8_DD2;
+		break;
+	case PVR_TYPE_P9:
+		cpu_feature_isa = CPUFEATURES_ISA_V3;
+		if (PVR_VERS_MAJ(version) == 1)
+			cpu_feature_cpu = CPUFEATURES_CPU_P9_DD1;
+		else
+			cpu_feature_cpu = CPUFEATURES_CPU_P9_DD2;
+		break;
+	}
+
+	cpus = dt_new_check(root, "cpus");
+
+	add_cpufeatures(cpus, cpu_feature_isa, cpu_feature_cpu);
+}
+
diff --git a/core/device.c b/core/device.c
index f3ee63fb..38a420eb 100644
--- a/core/device.c
+++ b/core/device.c
@@ -598,6 +598,13 @@ u32 dt_property_get_cell(const struct dt_property *prop, u32 index)
 	return fdt32_to_cpu(((const u32 *)prop->prop)[index]);
 }
 
+void dt_property_set_cell(struct dt_property *prop, u32 index, u32 val)
+{
+	assert(prop->len >= (index+1)*sizeof(u32));
+	/* Always aligned, so this works. */
+	((u32 *)prop->prop)[index] = cpu_to_fdt32(val);
+}
+
 /* First child of this node. */
 struct dt_node *dt_first(const struct dt_node *root)
 {
diff --git a/core/init.c b/core/init.c
index 6b8137c8..08b70ea3 100644
--- a/core/init.c
+++ b/core/init.c
@@ -789,6 +789,7 @@ void __noreturn __nomcount main_cpu_entry(const void *fdt)
 			abort();
 	} else {
 		dt_expand(fdt);
+		dt_add_cpufeatures(dt_root);
 	}
 
 	/* Now that we have a full devicetree, verify that we aren't on fire. */
diff --git a/hdata/cpu-common.c b/hdata/cpu-common.c
index c69c53a7..bd7c9729 100644
--- a/hdata/cpu-common.c
+++ b/hdata/cpu-common.c
@@ -29,6 +29,8 @@ struct dt_node * add_core_common(struct dt_node *cpus,
 	const char *name;
 	struct dt_node *cpu;
 	uint32_t version;
+	uint32_t cpu_feature_isa = 0;
+	uint32_t cpu_feature_cpu = 0;
 	uint64_t freq;
 	const uint8_t pa_features_p7[] = {
 		6, 0,
@@ -94,20 +96,28 @@ struct dt_node * add_core_common(struct dt_node *cpus,
 		pa_features_size = sizeof(pa_features_p7p);
 		tlb_congruence = 128;
 		break;
-	case PVR_TYPE_P8E:
 	case PVR_TYPE_P8:
+	case PVR_TYPE_P8E:
 	case PVR_TYPE_P8NVL:
 		name = "PowerPC,POWER8";
+		cpu_feature_isa = CPUFEATURES_ISA_V2_07;
+		if (PVR_VERS_MAJ(version) == 1)
+			cpu_feature_cpu = CPUFEATURES_CPU_P8_DD1;
+		else
+			cpu_feature_cpu = CPUFEATURES_CPU_P8_DD2;
 		pa_features = pa_features_p8;
 		pa_features_size = sizeof(pa_features_p8);
 		tlb_congruence = 512;
 		break;
 	case PVR_TYPE_P9:
 		name = "PowerPC,POWER9";
+		cpu_feature_isa = CPUFEATURES_ISA_V3;
 		if (PVR_VERS_MAJ(version) == 1) {
+			cpu_feature_cpu = CPUFEATURES_CPU_P9_DD1;
 			pa_features = pa_features_p9_dd1;
 			pa_features_size = sizeof(pa_features_p9_dd1);
 		} else {
+			cpu_feature_cpu = CPUFEATURES_CPU_P9_DD2;
 			pa_features = pa_features_p9_dd2;
 			pa_features_size = sizeof(pa_features_p9_dd2);
 		}
@@ -119,6 +129,8 @@ struct dt_node * add_core_common(struct dt_node *cpus,
 		pa_features = NULL;
 	}
 
+	add_cpufeatures(cpus, cpu_feature_isa, cpu_feature_cpu);
+
 	cpu = dt_new_addr(cpus, name, int_server);
 	assert(cpu);
 	dt_add_property_string(cpu, "device_type", "cpu");
diff --git a/include/device.h b/include/device.h
index 5155daad..ca4dd0b0 100644
--- a/include/device.h
+++ b/include/device.h
@@ -125,6 +125,7 @@ void dt_check_del_prop(struct dt_node *node, const char *name);
 /* Warning: moves *prop! */
 void dt_resize_property(struct dt_property **prop, size_t len);
 
+void dt_property_set_cell(struct dt_property *prop, u32 index, u32 val);
 u32 dt_property_get_cell(const struct dt_property *prop, u32 index);
 
 /* First child of this node. */
diff --git a/include/skiboot.h b/include/skiboot.h
index 8bc767a4..27fcd1a6 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -184,6 +184,23 @@ extern void start_kernel_secondary(uint64_t entry) __noreturn;
 /* Get description of machine from HDAT and create device-tree */
 extern int parse_hdat(bool is_opal);
 
+struct dt_node;
+
+/* Add /cpus/cpufeatures node for boot environment that passes an fdt */
+extern void dt_add_cpufeatures(struct dt_node *root);
+
+#define CPUFEATURES_ISA_V2_07	2070
+#define CPUFEATURES_ISA_V3	3000
+
+#define CPUFEATURES_CPU_P8_DD1	0
+#define CPUFEATURES_CPU_P8_DD2	1
+#define CPUFEATURES_CPU_P9_DD1	2
+#define CPUFEATURES_CPU_P9_DD2	3
+
+/* Add /cpus/cpufeatures node given the given ISA and CPU */
+extern void add_cpufeatures(struct dt_node *cpus,
+		uint32_t cpu_feature_isa, uint32_t cpu_feature_cpu);
+
 /* Root of device tree. */
 extern struct dt_node *dt_root;
 
