diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index ff691df..01ff49b 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -152,6 +152,70 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp)
     scc->parent_realize(dev, errp);
 }
 
+#define FAC0_TCG \
+    ( FAC_BIT(0, FAC_N3) \
+    | FAC_BIT(0, FAC_ZARCH) \
+    | FAC_BIT(0, FAC_ZARCH_ACTIVE) \
+    | FAC_BIT(0, FAC_STFLE) \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT) \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT_FAST) \
+    | FAC_BIT(0, FAC_EXTENDED_IMMEDIATE) \
+    | FAC_BIT(0, FAC_GENERAL_INSTRUCTIONS_EXT) \
+    | FAC_BIT(0, FAC_FLOATING_POINT_EXT) \
+    | FAC_BIT(0, FAC_FLOATING_POINT_SUPPPORT_ENH))
+    /* ??? We may have most of the collection of facilities at bit 45.  */
+
+/* ??? These lists of facilities gleaned from arch/s390/kernel/head.S.  */
+#define FAC0_Z900 \
+    ( FAC_BIT(0, FAC_N3) \
+    | FAC_BIT(0, FAC_ZARCH) \
+    | FAC_BIT(0, FAC_ZARCH_ACTIVE))
+
+#define FAC0_Z990 \
+    ( FAC0_Z900 \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT) \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT_FAST))
+
+#define FAC0_Z9_109 \
+    ( FAC0_Z990 \
+    | FAC_BIT(0, FAC_STFLE) \
+    | FAC_BIT(0, FAC_EXTENDED_TRANSLATION_2) \
+    | FAC_BIT(0, FAC_MESSAGE_SECURITY_ASSIST) \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT) \
+    | FAC_BIT(0, FAC_LONG_DISPLACEMENT_FAST) \
+    | FAC_BIT(0, FAC_HFP_MADDSUB) \
+    | FAC_BIT(0, FAC_EXTENDED_IMMEDIATE) \
+    | FAC_BIT(0, FAC_EXTENDED_TRANSLATION_3) \
+    | FAC_BIT(0, FAC_HFP_UNNORMALIZED_EXT) \
+    | FAC_BIT(0, FAC_ETF2_ENH) \
+    | FAC_BIT(0, FAC_STORE_CLOCK_FAST) \
+    | FAC_BIT(0, FAC_ETF3_ENH) \
+    | FAC_BIT(0, FAC_EXTRACT_CPU_TIME))
+
+#define FAC0_Z10 \
+    ( FAC0_Z9_109 \
+    | FAC_BIT(0, FAC_COMPARE_AND_SWAP_AND_STORE) \
+    | FAC_BIT(0, FAC_COMPARE_AND_SWAP_AND_STORE_2) \
+    | FAC_BIT(0, FAC_GENERAL_INSTRUCTIONS_EXT) \
+    | FAC_BIT(0, FAC_EXECUTE_EXT) \
+    | FAC_BIT(0, FAC_FLOATING_POINT_SUPPPORT_ENH) \
+    | FAC_BIT(0, FAC_DFP) \
+    | FAC_BIT(0, FAC_PFPO))
+
+#define FAC0_Z196 \
+    ( FAC0_Z10 \
+    | FAC_BIT(0, FAC_FLOATING_POINT_EXT) \
+    | FAC_BIT(0, FAC_MULTI_45))
+
+#define FAC0_ZEC12 \
+    ( FAC0_Z196 \
+    | FAC_BIT(0, FAC_DFP_ZONED_CONVERSION) \
+    | FAC_BIT(0, FAC_MULTI_49) \
+    | FAC_BIT(0, FAC_CONSTRAINT_TRANSACTIONAL_EXE))
+#define FAC1_ZEC12 \
+    FAC_BIT(1, FAC_TRANSACTIONAL_EXE)
+
+
 static void s390_cpu_initfn(Object *obj)
 {
     CPUState *cs = CPU(obj);
@@ -181,9 +245,18 @@ static void s390_cpu_initfn(Object *obj)
     env->cpu_num = cpu_num++;
     env->ext_index = -1;
 
-    env->facilities[0] = 0xc000000000000000ull;
+    env->facilities[0] = FAC0_TCG;
     env->facilities[1] = 0;
 
+    /* ??? Current distros are targeting Z9-109 as the minimum.  TCG
+       supports most of the Z9-109 facilities but not all.  Sadly, the
+       kernel checks for facilities it doesn't actually need, minor stuff
+       like hex floating point and translation.  For now, include all
+       that the kernel requires we support.  */
+#ifndef CONFIG_USER_ONLY
+    env->facilities[0] |= FAC0_Z9_109;
+#endif
+
     if (tcg_enabled() && !inited) {
         inited = true;
         s390x_translate_init();
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 746aec8..a0bafef 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -83,6 +83,65 @@ typedef struct MchkQueue {
 #define KVM_S390_RUNTIME_DIRTY_PARTIAL  1
 #define KVM_S390_RUNTIME_DIRTY_FULL     2
 
+typedef enum {
+    FAC_N3                              = 0,
+    FAC_ZARCH                           = 1,
+    FAC_ZARCH_ACTIVE                    = 2,
+    FAC_DAT_ENH                         = 3,
+    FAC_ASN_LX_REUSE                    = 6,
+    FAC_STFLE                           = 7,
+    FAC_ENHANCED_DAT_1                  = 8,
+    FAC_SENSE_RUNNING_STATUS            = 9,
+    FAC_CONDITIONAL_SSKE                = 10,
+    FAC_CONFIGURATION_TOPOLOGY          = 11,
+    FAC_IPTE_RANGE                      = 13,
+    FAC_NONQ_KEY_SETTING                = 14,
+    FAC_EXTENDED_TRANSLATION_2          = 16,
+    FAC_MESSAGE_SECURITY_ASSIST         = 17,
+    FAC_LONG_DISPLACEMENT               = 18,
+    FAC_LONG_DISPLACEMENT_FAST          = 19,
+    FAC_HFP_MADDSUB                     = 20,
+    FAC_EXTENDED_IMMEDIATE              = 21,
+    FAC_EXTENDED_TRANSLATION_3          = 22,
+    FAC_HFP_UNNORMALIZED_EXT            = 23,
+    FAC_ETF2_ENH                        = 24,
+    FAC_STORE_CLOCK_FAST                = 25,
+    FAC_PARSING_ENH                     = 26,
+    FAC_MOVE_WITH_OPTIONAL_SPEC         = 27,
+    FAC_TOD_CLOCK_STEERING              = 28,
+    FAC_ETF3_ENH                        = 30,
+    FAC_EXTRACT_CPU_TIME                = 31,
+    FAC_COMPARE_AND_SWAP_AND_STORE      = 32,
+    FAC_COMPARE_AND_SWAP_AND_STORE_2    = 33,
+    FAC_GENERAL_INSTRUCTIONS_EXT        = 34,
+    FAC_EXECUTE_EXT                     = 35,
+    FAC_ENHANCED_MONITOR                = 36,
+    FAC_FLOATING_POINT_EXT              = 37,
+    FAC_SET_PROGRAM_PARAMETERS          = 40,
+    FAC_FLOATING_POINT_SUPPPORT_ENH     = 41,
+    FAC_DFP                             = 42,
+    FAC_DFP_FAST                        = 43,
+    FAC_PFPO                            = 44,
+    FAC_MULTI_45                        = 45,
+    FAC_CMPSC_ENH                       = 47,
+    FAC_DFP_ZONED_CONVERSION            = 48,
+    FAC_MULTI_49                        = 49,
+    FAC_CONSTRAINT_TRANSACTIONAL_EXE    = 50,
+    FAC_LOCAL_TLB_CLEARING              = 51,
+    FAC_INTERLOCKED_ACCESS_2            = 52,
+    FAC_RESET_REFERENCE_BITS_MULTIPLE   = 66,
+    FAC_CPU_MEASUREMENT_COUNTER         = 67,
+    FAC_CPU_MEASUREMENT_SAMPLING        = 68,
+    FAC_TRANSACTIONAL_EXE               = 73,
+    FAC_ACCESS_EXCEPTION_FS_INDICATION  = 75,
+    FAC_MESSAGE_SECURITY_ASSIST_3       = 76,
+    FAC_MESSAGE_SECURITY_ASSIST_4       = 77,
+    FAC_ENHANCED_DAT_2                  = 78,
+} S390Facility;
+
+#define FAC_BIT(WORD, BIT)  (BIT / 64 == WORD ? 1ull << (63 - BIT % 64) : 0)
+
+
 typedef struct CPUS390XState {
     uint64_t regs[16];     /* GP registers */
     CPU_DoubleU fregs[16]; /* FP registers */
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index 690b4a0..5aac7b0 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -1102,33 +1102,10 @@ typedef enum {
     EXIT_NORETURN,
 } ExitStatus;
 
-typedef enum DisasFacility {
-    FAC_Z,                  /* zarch (default) */
-    FAC_CASS,               /* compare and swap and store */
-    FAC_CASS2,              /* compare and swap and store 2*/
-    FAC_DFP,                /* decimal floating point */
-    FAC_DFPR,               /* decimal floating point rounding */
-    FAC_DO,                 /* distinct operands */
-    FAC_EE,                 /* execute extensions */
-    FAC_EI,                 /* extended immediate */
-    FAC_FPE,                /* floating point extension */
-    FAC_FPSSH,              /* floating point support sign handling */
-    FAC_FPRGR,              /* FPR-GR transfer */
-    FAC_GIE,                /* general instructions extension */
-    FAC_HFP_MA,             /* HFP multiply-and-add/subtract */
-    FAC_HW,                 /* high-word */
-    FAC_IEEEE_SIM,          /* IEEE exception sumilation */
-    FAC_LOC,                /* load/store on condition */
-    FAC_LD,                 /* long displacement */
-    FAC_PC,                 /* population count */
-    FAC_SCF,                /* store clock fast */
-    FAC_SFLE,               /* store facility list extended */
-} DisasFacility;
-
 struct DisasInsn {
     unsigned opc:16;
     DisasFormat fmt:8;
-    DisasFacility fac:8;
+    S390Facility fac:8;
     unsigned spec:8;
 
     const char *name;
@@ -4442,6 +4419,28 @@ static void in2_i2_32u_shl(DisasContext *s, DisasFields *f, DisasOps *o)
 
 /* ====================================================================== */
 
+/* Abbreviations for facilities used in the table.  */
+#define DFAC_Z               FAC_ZARCH
+#define DFAC_CASS            FAC_COMPARE_AND_SWAP_AND_STORE
+#define DFAC_CASS2           FAC_COMPARE_AND_SWAP_AND_STORE_2
+#define DFAC_DFP             FAC_DFP
+#define DFAC_DFPR            FAC_FLOATING_POINT_SUPPPORT_ENH
+#define DFAC_DO              FAC_MULTI_45
+#define DFAC_EE              FAC_EXECUTE_EXT
+#define DFAC_EI              FAC_EXTENDED_IMMEDIATE
+#define DFAC_FPE             FAC_FLOATING_POINT_EXT
+#define DFAC_FPSSH           FAC_FLOATING_POINT_SUPPPORT_ENH
+#define DFAC_FPRGR           FAC_FLOATING_POINT_SUPPPORT_ENH
+#define DFAC_GIE             FAC_GENERAL_INSTRUCTIONS_EXT
+#define DFAC_HFP_MA          FAC_HFP_MADDSUB
+#define DFAC_HW              FAC_MULTI_45
+#define DFAC_IEEEE_SIM       FAC_FLOATING_POINT_SUPPPORT_ENH
+#define DFAC_LOC             FAC_MULTI_45
+#define DFAC_LD              FAC_LONG_DISPLACEMENT
+#define DFAC_PC              FAC_MULTI_45
+#define DFAC_SCF             FAC_STORE_CLOCK_FAST
+#define DFAC_SFLE            FAC_STFLE
+
 /* Find opc within the table of insns.  This is formulated as a switch
    statement so that (1) we get compile-time notice of cut-paste errors
    for duplicated opcodes, and (2) the compiler generates the binary
@@ -4460,7 +4459,7 @@ enum DisasInsnEnum {
 #define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) {                       \
     .opc = OPC,                                                             \
     .fmt = FMT_##FT,                                                        \
-    .fac = FAC_##FC,                                                        \
+    .fac = DFAC_##FC,                                                        \
     .spec = SPEC_in1_##I1 | SPEC_in2_##I2 | SPEC_prep_##P | SPEC_wout_##W,  \
     .name = #NM,                                                            \
     .help_in1 = in1_##I1,                                                   \
