xen/arm: Propagate clock-frequency to DOMU if present in the DT timer node
authorJulien Grall <julien.grall@citrix.com>
Fri, 19 Jun 2015 12:41:29 +0000 (13:41 +0100)
committerIan Campbell <ian.campbell@citrix.com>
Thu, 25 Jun 2015 09:59:08 +0000 (10:59 +0100)
When the property "clock-frequency" is present in the DT timer node, it
means that the bootloader/firmware didn't correctly configure the
CNTFRQ/CNTFRQ_EL0 on each processor.

The best solution would be to fix the offending firmware/bootloader,
although it may not always be possible to modify and re-flash it.

As it's not possible to trap the register CNTFRQ/CNTFRQ_EL0, we have
to extend xen_arch_domainconfig to provide the timer frequency to the
toolstack when the property "clock-frequency" is present to the host DT
timer node. Then, a property "clock-frequency" will be created in the guest
DT timer node if the value is not 0.

We could have set the property in the guest DT no matter if the property
is present in the host DT. Although, we still want to let the guest
using CNTFRQ in normal case. After all, the property "clock-frequency"
is just a workaround for buggy firmware.

Also add a stub for fdt_property_u32 which is not present in libfdt <
1.4.0 used by distribution such as Debian Wheezy.

Signed-off-by: Julien Grall <julien.grall@citrix.com>
Tested-by: Chris Brand <chris.brand@broadcom.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
[ ijc -- ran autogen.sh ]

tools/config.h.in
tools/configure
tools/configure.ac
tools/libxl/libxl_arm.c
tools/libxl/libxl_libfdt_compat.h
xen/arch/arm/domain.c
xen/arch/arm/time.c
xen/arch/arm/vtimer.c
xen/arch/arm/vtimer.h
xen/include/asm-arm/time.h
xen/include/public/arch-arm.h

index d55173b9a8d4a417b6adbf482a525199cb5bea73..478a2ccd4033d88888498954c418a1e42d2c7f0a 100644 (file)
    you don't. */
 #undef HAVE_DECL_FDT_NEXT_SUBNODE
 
+/* Define to 1 if you have the declaration of `fdt_property_u32', and to 0 if
+   you don't. */
+#undef HAVE_DECL_FDT_PROPERTY_U32
+
 /* Define to 1 if you have the `fdt_first_subnode' function. */
 #undef HAVE_FDT_FIRST_SUBNODE
 
index 896d8928f15a010ba795d32b24d150af7b5eb168..9e87e90ceb6b656c66b7108eb962ca4aac74faa1 100755 (executable)
@@ -8906,6 +8906,21 @@ cat >>confdefs.h <<_ACEOF
 #define HAVE_DECL_FDT_NEXT_SUBNODE $ac_have_decl
 _ACEOF
 
+
+# The helper fdt_property_u32 is only present in libfdt >= 1.4.0
+# It's an inline function, so only check if the declaration is present
+ac_fn_c_check_decl "$LINENO" "fdt_property_u32" "ac_cv_have_decl_fdt_property_u32" "#include <libfdt.h>
+"
+if test "x$ac_cv_have_decl_fdt_property_u32" = xyes; then :
+  ac_have_decl=1
+else
+  ac_have_decl=0
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_DECL_FDT_PROPERTY_U32 $ac_have_decl
+_ACEOF
+
 esac
 
 # Checks for header files.
index 1a06ddf98d7daf919d90d414e25d53b264e35f4d..2886cb559116653489f802468bf7f00c7c34c8f2 100644 (file)
@@ -379,6 +379,10 @@ AS_IF([test "x$partial_dt" = "xy" ],
 #   * The prototype exists but the functions are not exposed. Don't ask why...
 AC_CHECK_FUNCS([fdt_first_subnode fdt_next_subnode])
 AC_CHECK_DECLS([fdt_first_subnode, fdt_next_subnode],,,[#include <libfdt.h>])
+
+# The helper fdt_property_u32 is only present in libfdt >= 1.4.0
+# It's an inline function, so only check if the declaration is present
+AC_CHECK_DECLS([fdt_property_u32],,,[#include <libfdt.h>])
 esac
 
 # Checks for header files.
index 4fb5e26fab30fb9b6b982cd1151c3e3f5ccc2ee6..f09c860aa6ec3cabe114973a8a2cf6239ea4fa84 100644 (file)
@@ -444,7 +444,9 @@ static int make_gicv3_node(libxl__gc *gc, void *fdt)
     return 0;
 }
 
-static int make_timer_node(libxl__gc *gc, void *fdt, const struct arch_info *ainfo)
+static int make_timer_node(libxl__gc *gc, void *fdt,
+                           const struct arch_info *ainfo,
+                           uint32_t frequency)
 {
     int res;
     gic_interrupt ints[3];
@@ -462,6 +464,9 @@ static int make_timer_node(libxl__gc *gc, void *fdt, const struct arch_info *ain
     res = fdt_property_interrupts(gc, fdt, ints, 3);
     if (res) return res;
 
+    if ( frequency )
+        fdt_property_u32(fdt, "clock-frequency", frequency);
+
     res = fdt_end_node(fdt);
     if (res) return res;
 
@@ -805,7 +810,7 @@ next_resize:
             goto out;
         }
 
-        FDT( make_timer_node(gc, fdt, ainfo) );
+        FDT( make_timer_node(gc, fdt, ainfo, xc_config->clock_frequency) );
         FDT( make_hypervisor_node(gc, fdt, vers) );
 
         if (pfdt)
index 53a507606d7f9b0c15c07d8e72742410f8634aeb..23230b51b1448e11ea005603584a802ea6982944 100644 (file)
@@ -61,6 +61,7 @@
 #define LIBXL_LIBFDT_COMPAT_H
 
 #include "libxl_internal.h"
+#include <libfdt.h>
 
 #if !HAVE_DECL_FDT_FIRST_SUBNODE
 _hidden int fdt_first_subnode(const void *fdt, int offset);
@@ -70,6 +71,14 @@ _hidden int fdt_first_subnode(const void *fdt, int offset);
 _hidden int fdt_next_subnode(const void *fdt, int offset);
 #endif
 
+#if !HAVE_DECL_FDT_PROPERTY_U32
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+       uint32_t tmp = cpu_to_fdt32(val);
+       return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+#endif
+
 #endif
 
 /*
index 24b89380f8852ea6db4e4e8ad0f67edf76d8b39a..8b1bf5a354f22d3e7d61b2e650a283a3a795f657 100644 (file)
@@ -593,7 +593,7 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
     if ( (rc = domain_vgic_init(d, config->nr_spis)) != 0 )
         goto fail;
 
-    if ( (rc = domain_vtimer_init(d)) != 0 )
+    if ( (rc = domain_vtimer_init(d, config)) != 0 )
         goto fail;
 
     /*
index ce6d3fd5dc72bc898e1fc8c4a2e98dd75f2cd7a0..5ded30c03128e44aa42d24d5856bbe2a4c2a67eb 100644 (file)
@@ -42,6 +42,8 @@ uint64_t __read_mostly boot_count;
  * register-mapped time source in the SoC. */
 unsigned long __read_mostly cpu_khz;  /* CPU clock frequency in kHz. */
 
+uint32_t __read_mostly timer_dt_clock_frequency;
+
 static unsigned int timer_irq[MAX_TIMER_PPI];
 
 unsigned int timer_get_irq(enum timer_ppi ppi)
@@ -86,7 +88,10 @@ void __init preinit_xen_time(void)
 
     res = dt_property_read_u32(timer, "clock-frequency", &rate);
     if ( res )
+    {
         cpu_khz = rate / 1000;
+        timer_dt_clock_frequency = rate;
+    }
     else
         cpu_khz = READ_SYSREG32(CNTFRQ_EL0) / 1000;
 
index 685bfea2d6d09cd169ec803ffc85380ac1d378e9..1418092aeb0c82add5e2466eaea65da18da43001 100644 (file)
@@ -60,11 +60,13 @@ static void virt_timer_expired(void *data)
     perfc_incr(vtimer_virt_inject);
 }
 
-int domain_vtimer_init(struct domain *d)
+int domain_vtimer_init(struct domain *d, struct xen_arch_domainconfig *config)
 {
     d->arch.phys_timer_base.offset = NOW();
     d->arch.virt_timer_base.offset = READ_SYSREG64(CNTPCT_EL0);
 
+    config->clock_frequency = timer_dt_clock_frequency;
+
     /* At this stage vgic_reserve_virq can't fail */
     if ( is_hardware_domain(d) )
     {
index 6d2e46efe2b37d8846b58709af1a187dcea6567c..99e81454e5f4978a1bb3514241a4e94abb1ef084 100644 (file)
@@ -20,7 +20,8 @@
 #ifndef __ARCH_ARM_VTIMER_H__
 #define __ARCH_ARM_VTIMER_H__
 
-extern int domain_vtimer_init(struct domain *d);
+extern int domain_vtimer_init(struct domain *d,
+                              struct xen_arch_domainconfig *config);
 extern int vcpu_vtimer_init(struct vcpu *v);
 extern int vtimer_emulate(struct cpu_user_regs *regs, union hsr hsr);
 extern int virt_timer_save(struct vcpu *v);
index 039039a416ce5e8c35a3d5ea6cb32d280f1b6b46..d755f36d0b06cec266a09c6d09ac159d6f3e2573 100644 (file)
@@ -22,6 +22,12 @@ enum timer_ppi
     MAX_TIMER_PPI = 4,
 };
 
+/*
+ * Value of "clock-frequency" in the DT timer node if present.
+ * 0 means the property doesn't exist.
+ */
+extern uint32_t timer_dt_clock_frequency;
+
 /* Get one of the timer IRQ number */
 unsigned int timer_get_irq(enum timer_ppi ppi);
 
index 6f24c5f14f57381257860fa8c129177d659d8f91..805483ea80379ba391b30ade822e513b0b4ff2bc 100644 (file)
@@ -311,6 +311,20 @@ struct xen_arch_domainconfig {
     uint8_t gic_version;
     /* IN */
     uint32_t nr_spis;
+    /*
+     * OUT
+     * Based on the property clock-frequency in the DT timer node.
+     * The property may be present when the bootloader/firmware doesn't
+     * set correctly CNTFRQ which hold the timer frequency.
+     *
+     * As it's not possible to trap this register, we have to replicate
+     * the value in the guest DT.
+     *
+     * = 0 => property not present
+     * > 0 => Value of the property
+     *
+     */
+    uint32_t clock_frequency;
 };
 #endif /* __XEN__ || __XEN_TOOLS__ */