diff --git a/tools/firmware/hvmloader/Makefile b/tools/firmware/hvmloader/Makefile
index 84cba171cd6b..0e1dce26b342 100644
--- a/tools/firmware/hvmloader/Makefile
+++ b/tools/firmware/hvmloader/Makefile
@@ -34,6 +34,7 @@ OBJS  = hvmloader.o mp_tables.o util.o smbios.o
 OBJS += smp.o cacheattr.o xenbus.o vnuma.o
 OBJS += e820.o pci.o pir.o ctype.o
 OBJS += hvm_param.o
+OBJS += hypercall_page.o
 OBJS += ovmf.o seabios.o
 ifeq ($(debug),y)
 OBJS += tests.o
@@ -64,6 +65,7 @@ endif
 # Suppress the warning about LOAD segments with RWX permissions, as what we
 # build isn't a normal user-mode executable.
 LDFLAGS-$(call ld-option,--warn-rwx-segments) := --no-warn-rwx-segments
+LDFLAGS-y += -z noexecstack
 
 .PHONY: all
 all: hvmloader
@@ -74,6 +76,7 @@ acpi:
 
 rombios.o: roms.inc
 smbios.o: CFLAGS += -D__SMBIOS_DATE__="\"$(SMBIOS_REL_DATE)\""
+hypercall_page.o: CFLAGS += -D__ASSEMBLY__
 
 ACPI_PATH = ../../libacpi
 DSDT_FILES = dsdt_anycpu.c dsdt_15cpu.c dsdt_anycpu_qemu_xen.c
diff --git a/tools/firmware/hvmloader/config.h b/tools/firmware/hvmloader/config.h
index cd716bf39245..4ff7aa9d4483 100644
--- a/tools/firmware/hvmloader/config.h
+++ b/tools/firmware/hvmloader/config.h
@@ -64,7 +64,6 @@ extern bool acpi_enabled;
 
 /* Memory map. */
 #define SCRATCH_PHYSICAL_ADDRESS      0x00010000
-#define HYPERCALL_PHYSICAL_ADDRESS    0x00080000
 #define VGABIOS_PHYSICAL_ADDRESS      0x000C0000
 #define HVMLOADER_PHYSICAL_ADDRESS    0x00100000
 /* Special BIOS mappings, etc. are allocated from here upwards... */
diff --git a/tools/firmware/hvmloader/hvmloader.c b/tools/firmware/hvmloader/hvmloader.c
index f8af88fabf24..cd81eb412840 100644
--- a/tools/firmware/hvmloader/hvmloader.c
+++ b/tools/firmware/hvmloader/hvmloader.c
@@ -120,7 +120,6 @@ bool acpi_enabled;
 static void init_hypercalls(void)
 {
     uint32_t eax, ebx, ecx, edx;
-    unsigned long i;
     char signature[13];
     xen_extraversion_t extraversion;
     uint32_t base;
@@ -142,8 +141,7 @@ static void init_hypercalls(void)
 
     /* Fill in hypercall transfer pages. */
     cpuid(base + 2, &eax, &ebx, &ecx, &edx);
-    for ( i = 0; i < eax; i++ )
-        wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
+    wrmsr(ebx, (unsigned long)hypercall_page);
 
     /* Print version information. */
     cpuid(base + 1, &eax, &ebx, &ecx, &edx);
@@ -324,9 +322,6 @@ int main(void)
     const struct bios_config *bios;
     const struct hvm_modlist_entry *bios_module;
 
-    /* Initialise hypercall stubs with RET, rendering them no-ops. */
-    memset((void *)HYPERCALL_PHYSICAL_ADDRESS, 0xc3 /* RET */, PAGE_SIZE);
-
     printf("HVM Loader\n");
     BUG_ON(hvm_start_info->magic != XEN_HVM_START_MAGIC_VALUE);
 
diff --git a/tools/firmware/hvmloader/hvmloader.lds b/tools/firmware/hvmloader/hvmloader.lds
index edb1a12dc305..559cd2107f6a 100644
--- a/tools/firmware/hvmloader/hvmloader.lds
+++ b/tools/firmware/hvmloader/hvmloader.lds
@@ -7,9 +7,9 @@ SECTIONS
    * NB: there's no need to use the AT keyword in order to set the LMA, by
    * default the linker will use VMA = LMA unless specified otherwise.
    */
-  .text : { *(.text) *(.text.*) }
+  .text : { *(.text) *(.text.*)}
   .rodata : { *(.rodata) *(.rodata.*) }
-  .data : { *(.data) *(.data.*) }
+  .data : { *(.data) *(.data.*) *(.hcall) }
   .bss : { *(.bss) *(.bss.*) }
   _end = .;
 }
diff --git a/tools/firmware/hvmloader/hypercall.h b/tools/firmware/hvmloader/hypercall.h
index 5368c3072007..a55051c5350f 100644
--- a/tools/firmware/hvmloader/hypercall.h
+++ b/tools/firmware/hvmloader/hypercall.h
@@ -35,148 +35,91 @@
 #include <xen/xen.h>
 #include "config.h"
 
-#define hcall_addr(name)                                                \
-    ((unsigned long)HYPERCALL_PHYSICAL_ADDRESS + __HYPERVISOR_##name * 32)
-
-#define _hypercall0(type, name)                 \
-({                                              \
-    long __res;                                 \
-    asm volatile (                              \
-        "call *%%eax"                           \
-        : "=a" (__res)                          \
-        : "0" (hcall_addr(name))                \
-        : "memory" );                           \
-    (type)__res;                                \
-})
-
-#define _hypercall1(type, name, a1)             \
-({                                              \
-    long __res, __ign1;                         \
-    asm volatile (                              \
-        "call *%%eax"                           \
-        : "=a" (__res), "=b" (__ign1)           \
-        : "0" (hcall_addr(name)),               \
-          "1" ((long)(a1))                      \
-        : "memory" );                           \
-    (type)__res;                                \
-})
-
-#define _hypercall2(type, name, a1, a2)                 \
-({                                                      \
-    long __res, __ign1, __ign2;                         \
-    asm volatile (                                      \
-        "call *%%eax"                                   \
-        : "=a" (__res), "=b" (__ign1), "=c" (__ign2)    \
-        : "0" (hcall_addr(name)),                       \
-          "1" ((long)(a1)), "2" ((long)(a2))            \
-        : "memory" );                                   \
-    (type)__res;                                        \
-})
-
-#define _hypercall3(type, name, a1, a2, a3)             \
-({                                                      \
-    long __res, __ign1, __ign2, __ign3;                 \
-    asm volatile (                                      \
-        "call *%%eax"                                   \
-        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),   \
-          "=d" (__ign3)                                 \
-        : "0" (hcall_addr(name)),                       \
-          "1" ((long)(a1)), "2" ((long)(a2)),           \
-          "3" ((long)(a3))                              \
-        : "memory" );                                   \
-    (type)__res;                                        \
-})
-
-#define _hypercall4(type, name, a1, a2, a3, a4)         \
-({                                                      \
-    long __res, __ign1, __ign2, __ign3, __ign4;         \
-    asm volatile (                                      \
-        "call *%%eax"                                   \
-        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),   \
-          "=d" (__ign3), "=S" (__ign4)                  \
-        : "0" (hcall_addr(name)),                       \
-          "1" ((long)(a1)), "2" ((long)(a2)),           \
-          "3" ((long)(a3)), "4" ((long)(a4))            \
-        : "memory" );                                   \
-    (type)__res;                                        \
-})
-
-#define _hypercall5(type, name, a1, a2, a3, a4, a5)     \
-({                                                      \
-    long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \
-    asm volatile (                                      \
-        "call *%%eax"                                   \
-        : "=a" (__res), "=b" (__ign1), "=c" (__ign2),   \
-          "=d" (__ign3), "=S" (__ign4), "=D" (__ign5)   \
-        : "0" (hcall_addr(name)),                       \
-          "1" ((long)(a1)), "2" ((long)(a2)),           \
-          "3" ((long)(a3)), "4" ((long)(a4)),           \
-          "5" ((long)(a5))                              \
-        : "memory" );                                   \
-    (type)__res;                                        \
-})
+extern const char hypercall_page[];
+
+#define _hypercall2(type, hcall, a1, a2)                                \
+    ({                                                                  \
+        long res, _a1 = (long)(a1), _a2 = (long)(a2);                   \
+        asm volatile (                                                  \
+            "call hypercall_page + %c[offset]"                          \
+            : "=a" (res), "+b" (_a1), "+c" (_a2)                        \
+            : [offset] "i" (hcall * 32)                                 \
+            : "memory" );                                               \
+        (type)res;                                                      \
+    })
+
+#define _hypercall3(type, hcall, a1, a2, a3)                            \
+    ({                                                                  \
+        long res, _a1 = (long)(a1), _a2 = (long)(a2), _a3 = (long)(a3); \
+        asm volatile (                                                  \
+            "call hypercall_page + %c[offset]"                          \
+            : "=a" (res), "+b" (_a1), "+c" (_a2), "+d" (_a3)            \
+            : [offset] "i" (hcall * 32)                                 \
+            : "memory" );                                               \
+        (type)res;                                                      \
+    })
 
 static inline int
 hypercall_sched_op(
     int cmd, void *arg)
 {
-    return _hypercall2(int, sched_op, cmd, arg);
+    return _hypercall2(int, __HYPERVISOR_sched_op, cmd, arg);
 }
 
 static inline int
 hypercall_memory_op(
     unsigned int cmd, void *arg)
 {
-    return _hypercall2(int, memory_op, cmd, arg);
+    return _hypercall2(int, __HYPERVISOR_memory_op, cmd, arg);
 }
 
 static inline int
 hypercall_multicall(
     void *call_list, int nr_calls)
 {
-    return _hypercall2(int, multicall, call_list, nr_calls);
+    return _hypercall2(int, __HYPERVISOR_multicall, call_list, nr_calls);
 }
 
 static inline int
 hypercall_event_channel_op(
     int cmd, void *arg)
 {
-    return _hypercall2(int, event_channel_op, cmd, arg);
+    return _hypercall2(int, __HYPERVISOR_event_channel_op, cmd, arg);
 }
 
 static inline int
 hypercall_xen_version(
     int cmd, void *arg)
 {
-    return _hypercall2(int, xen_version, cmd, arg);
+    return _hypercall2(int, __HYPERVISOR_xen_version, cmd, arg);
 }
 
 static inline int
 hypercall_console_io(
     int cmd, int count, char *str)
 {
-    return _hypercall3(int, console_io, cmd, count, str);
+    return _hypercall3(int, __HYPERVISOR_console_io, cmd, count, str);
 }
 
 static inline int
 hypercall_vm_assist(
     unsigned int cmd, unsigned int type)
 {
-    return _hypercall2(int, vm_assist, cmd, type);
+    return _hypercall2(int, __HYPERVISOR_vm_assist, cmd, type);
 }
 
 static inline int
 hypercall_vcpu_op(
     int cmd, int vcpuid, void *extra_args)
 {
-    return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args);
+    return _hypercall3(int, __HYPERVISOR_vcpu_op, cmd, vcpuid, extra_args);
 }
 
 static inline int
 hypercall_hvm_op(
     int cmd, void *arg)
 {
-    return _hypercall2(int, hvm_op, cmd, arg);
+    return _hypercall2(int, __HYPERVISOR_hvm_op, cmd, arg);
 }
 
 #endif /* __HVMLOADER_HYPERCALL_H__ */
diff --git a/tools/firmware/hvmloader/hypercall_page.S b/tools/firmware/hvmloader/hypercall_page.S
new file mode 100644
index 000000000000..75428591d19a
--- /dev/null
+++ b/tools/firmware/hvmloader/hypercall_page.S
@@ -0,0 +1,67 @@
+#include <xen/xen.h>
+
+        .section ".hcall", "aw"
+        .align 4096
+
+        .globl hypercall_page
+hypercall_page:
+         /* Poisoned with `ret` for safety before hypercalls are set up. */
+        .fill 4096, 1, 0xc3
+        .type hypercall_page, STT_OBJECT
+        .size hypercall_page, 4096
+
+#define DECLARE_HYPERCALL(name)                                                 \
+        .globl HYPERCALL_ ## name;                                              \
+        .type  HYPERCALL_ ## name, STT_FUNC;                                    \
+        .size  HYPERCALL_ ## name, 32;                                          \
+        .set   HYPERCALL_ ## name, hypercall_page + __HYPERVISOR_ ## name * 32
+
+DECLARE_HYPERCALL(set_trap_table)
+DECLARE_HYPERCALL(mmu_update)
+DECLARE_HYPERCALL(set_gdt)
+DECLARE_HYPERCALL(stack_switch)
+DECLARE_HYPERCALL(set_callbacks)
+DECLARE_HYPERCALL(fpu_taskswitch)
+DECLARE_HYPERCALL(sched_op_compat)
+DECLARE_HYPERCALL(platform_op)
+DECLARE_HYPERCALL(set_debugreg)
+DECLARE_HYPERCALL(get_debugreg)
+DECLARE_HYPERCALL(update_descriptor)
+DECLARE_HYPERCALL(memory_op)
+DECLARE_HYPERCALL(multicall)
+DECLARE_HYPERCALL(update_va_mapping)
+DECLARE_HYPERCALL(set_timer_op)
+DECLARE_HYPERCALL(event_channel_op_compat)
+DECLARE_HYPERCALL(xen_version)
+DECLARE_HYPERCALL(console_io)
+DECLARE_HYPERCALL(physdev_op_compat)
+DECLARE_HYPERCALL(grant_table_op)
+DECLARE_HYPERCALL(vm_assist)
+DECLARE_HYPERCALL(update_va_mapping_otherdomain)
+DECLARE_HYPERCALL(iret)
+DECLARE_HYPERCALL(vcpu_op)
+DECLARE_HYPERCALL(set_segment_base)
+DECLARE_HYPERCALL(mmuext_op)
+DECLARE_HYPERCALL(xsm_op)
+DECLARE_HYPERCALL(nmi_op)
+DECLARE_HYPERCALL(sched_op)
+DECLARE_HYPERCALL(callback_op)
+DECLARE_HYPERCALL(xenoprof_op)
+DECLARE_HYPERCALL(event_channel_op)
+DECLARE_HYPERCALL(physdev_op)
+DECLARE_HYPERCALL(hvm_op)
+DECLARE_HYPERCALL(sysctl)
+DECLARE_HYPERCALL(domctl)
+DECLARE_HYPERCALL(kexec_op)
+DECLARE_HYPERCALL(tmem_op)
+DECLARE_HYPERCALL(argo_op)
+DECLARE_HYPERCALL(xenpmu_op)
+
+DECLARE_HYPERCALL(arch_0)
+DECLARE_HYPERCALL(arch_1)
+DECLARE_HYPERCALL(arch_2)
+DECLARE_HYPERCALL(arch_3)
+DECLARE_HYPERCALL(arch_4)
+DECLARE_HYPERCALL(arch_5)
+DECLARE_HYPERCALL(arch_6)
+DECLARE_HYPERCALL(arch_7)
