diff --git a/docs/man/xl.conf.5.pod b/docs/man/xl.conf.5.pod
index 962144e38e..207ab3e77a 100644
--- a/docs/man/xl.conf.5.pod
+++ b/docs/man/xl.conf.5.pod
@@ -81,13 +81,15 @@ Default: C</var/lock/xl>
 
 Sets the default value for the C<max_grant_frames> domain config value.
 
-Default: C<32> on hosts up to 16TB of memory, C<64> on hosts larger than 16TB
+Default: value of Xen command line B<gnttab_max_frames> parameter (or its
+default value if unspecified).
 
 =item B<max_maptrack_frames=NUMBER>
 
 Sets the default value for the C<max_maptrack_frames> domain config value.
 
-Default: C<1024>
+Default: value of Xen command line B<gnttab_max_maptrack_frames>
+parameter (or its default value if unspecified).
 
 =item B<vif.default.script="PATH">
 
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 49b56fa1a3..a2a5d321c5 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -364,8 +364,8 @@
  */
 #define LIBXL_HAVE_BUILDINFO_GRANT_LIMITS 1
 
-#define LIBXL_MAX_GRANT_FRAMES_DEFAULT 32
-#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT 1024
+#define LIBXL_MAX_GRANT_FRAMES_DEFAULT -1
+#define LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT -1
 
 /*
  * LIBXL_HAVE_BUILDINFO_* indicates that libxl_domain_build_info has
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 0546d7865a..63e29bb2fb 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -511,8 +511,8 @@ libxl_domain_build_info = Struct("domain_build_info",[
 
     ("vnuma_nodes", Array(libxl_vnode_info, "num_vnuma_nodes")),
 
-    ("max_grant_frames",    uint32, {'init_val': 'LIBXL_MAX_GRANT_FRAMES_DEFAULT'}),
-    ("max_maptrack_frames", uint32, {'init_val': 'LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT'}),
+    ("max_grant_frames",    integer, {'init_val': 'LIBXL_MAX_GRANT_FRAMES_DEFAULT'}),
+    ("max_maptrack_frames", integer, {'init_val': 'LIBXL_MAX_MAPTRACK_FRAMES_DEFAULT'}),
     
     ("device_model_version", libxl_device_model_version),
     ("device_model_stubdomain", libxl_defbool),
diff --git a/tools/libxl/libxlu_cfg.c b/tools/libxl/libxlu_cfg.c
index 72815d25dd..09d5c78a46 100644
--- a/tools/libxl/libxlu_cfg.c
+++ b/tools/libxl/libxlu_cfg.c
@@ -268,8 +268,9 @@ int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n,
     return 0;
 }
 
-int xlu_cfg_get_long(const XLU_Config *cfg, const char *n,
-                     long *value_r, int dont_warn) {
+int xlu_cfg_get_bounded_long(const XLU_Config *cfg, const char *n,
+                             long min, long max, long *value_r,
+                             int dont_warn) {
     long l;
     XLU_ConfigSetting *set;
     int e;
@@ -303,10 +304,29 @@ int xlu_cfg_get_long(const XLU_Config *cfg, const char *n,
                     cfg->config_source, set->lineno, n);
         return EINVAL;
     }
+    if (l < min)
+        if (!dont_warn)
+            fprintf(cfg->report,
+                    "%s:%d: warning: value `%ld' is smaller than minimum bound '%ld'\n",
+                    cfg->config_source, set->lineno, l, min);
+        return EINVAL;
+    if (l > max)
+        if (!dont_warn)
+            fprintf(cfg->report,
+                    "%s:%d: warning: value `%ld' is greater than maximum bound '%ld'\n",
+                    cfg->config_source, set->lineno, l, max);
+        return EINVAL;
+
     *value_r= l;
     return 0;
 }
 
+int xlu_cfg_get_long(const XLU_Config *cfg, const char *n,
+                     long *value_r, int dont_warn) {
+    return xlu_cfg_get_bounded_long(cfg, n, LONG_MIN, LONG_MAX, value_r,
+                                    dont_warn);
+}
+
 int xlu_cfg_get_defbool(const XLU_Config *cfg, const char *n, libxl_defbool *b,
                      int dont_warn)
 {
diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h
index 057cc25cb2..92e35c5462 100644
--- a/tools/libxl/libxlutil.h
+++ b/tools/libxl/libxlutil.h
@@ -63,6 +63,8 @@ int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n,
                            char **value_r, int dont_warn);
 int xlu_cfg_get_long(const XLU_Config*, const char *n, long *value_r,
                      int dont_warn);
+int xlu_cfg_get_bounded_long(const XLU_Config*, const char *n, long min,
+                             long max, long *value_r, int dont_warn);
 int xlu_cfg_get_defbool(const XLU_Config*, const char *n, libxl_defbool *b,
                      int dont_warn);
 
diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c
index 44d3606141..a751e85910 100644
--- a/tools/python/xen/lowlevel/xc/xc.c
+++ b/tools/python/xen/lowlevel/xc/xc.c
@@ -127,8 +127,8 @@ static PyObject *pyxc_domain_create(XcObject *self,
         },
         .max_vcpus = 1,
         .max_evtchn_port = -1, /* No limit. */
-        .max_grant_frames = 32,
-        .max_maptrack_frames = 1024,
+        .max_grant_frames = -1,
+        .max_maptrack_frames = -1,
     };
 
     static char *kwd_list[] = { "domid", "ssidref", "handle", "flags",
diff --git a/tools/xl/xl.c b/tools/xl/xl.c
index ddd29b3f1b..921c64f5ed 100644
--- a/tools/xl/xl.c
+++ b/tools/xl/xl.c
@@ -23,6 +23,7 @@
 #include <ctype.h>
 #include <inttypes.h>
 #include <regex.h>
+#include <limits.h>
 
 #include <libxl.h>
 #include <libxl_utils.h>
@@ -96,7 +97,6 @@ static void parse_global_config(const char *configfile,
     XLU_Config *config;
     int e;
     const char *buf;
-    libxl_physinfo physinfo;
 
     config = xlu_cfg_init(stderr, configfile);
     if (!config) {
@@ -197,16 +197,11 @@ static void parse_global_config(const char *configfile,
     xlu_cfg_replace_string (config, "colo.default.proxyscript",
         &default_colo_proxy_script, 0);
 
-    if (!xlu_cfg_get_long (config, "max_grant_frames", &l, 0))
+    if (!xlu_cfg_get_bounded_long (config, "max_grant_frames", 0, INT_MAX,
+                                   &l, 0))
         max_grant_frames = l;
-    else {
-        libxl_physinfo_init(&physinfo);
-        max_grant_frames = (libxl_get_physinfo(ctx, &physinfo) != 0 ||
-                            !(physinfo.max_possible_mfn >> 32))
-                           ? 32 : 64;
-        libxl_physinfo_dispose(&physinfo);
-    }
-    if (!xlu_cfg_get_long (config, "max_maptrack_frames", &l, 0))
+    if (!xlu_cfg_get_bounded_long (config, "max_maptrack_frames", 0,
+                                   INT_MAX, &l, 0))
         max_maptrack_frames = l;
 
     libxl_cpu_bitmap_alloc(ctx, &global_vm_affinity_mask, 0);
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 112f8ee026..555991dae3 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1411,13 +1411,16 @@ void parse_config_data(const char *config_source,
         !xlu_cfg_get_string (config, "cpus_soft", &buf, 0))
         parse_vcpu_affinity(b_info, cpus, buf, num_cpus, false);
 
-    if (!xlu_cfg_get_long (config, "max_grant_frames", &l, 0))
+    if (!xlu_cfg_get_bounded_long (config, "max_grant_frames", 0, INT_MAX,
+                                   &l, 0))
         b_info->max_grant_frames = l;
     else
         b_info->max_grant_frames = max_grant_frames;
-    if (!xlu_cfg_get_long (config, "max_maptrack_frames", &l, 0))
+
+    if (!xlu_cfg_get_bounded_long (config, "max_maptrack_frames", 0,
+                                   INT_MAX, &l, 0))
         b_info->max_maptrack_frames = l;
-    else if (max_maptrack_frames != -1)
+    else
         b_info->max_maptrack_frames = max_maptrack_frames;
 
     libxl_defbool_set(&b_info->claim_mode, claim_mode);
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index 51d32106b7..3c899cd4a0 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -789,7 +789,7 @@ void __init start_xen(unsigned long boot_phys_offset,
         .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap,
         .max_evtchn_port = -1,
         .max_grant_frames = gnttab_dom0_frames(),
-        .max_maptrack_frames = opt_max_maptrack_frames,
+        .max_maptrack_frames = -1,
     };
     int rc;
 
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 00ee87bde5..7d27f36053 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -697,8 +697,8 @@ void __init noreturn __start_xen(unsigned long mbi_p)
     struct xen_domctl_createdomain dom0_cfg = {
         .flags = IS_ENABLED(CONFIG_TBOOT) ? XEN_DOMCTL_CDF_s3_integrity : 0,
         .max_evtchn_port = -1,
-        .max_grant_frames = opt_max_grant_frames,
-        .max_maptrack_frames = opt_max_maptrack_frames,
+        .max_grant_frames = -1,
+        .max_maptrack_frames = -1,
     };
 
     /* Critical region without IDT or TSS.  Any fault is deadly! */
diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index b34d520f6d..f5053a6ee8 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -84,11 +84,43 @@ struct grant_table {
     struct grant_table_arch arch;
 };
 
+static int __init parse_gnttab_limit(const char *param, const char *arg,
+                                     unsigned int *valp)
+{
+    const char *e;
+    unsigned long val;
+
+    val = simple_strtoul(arg, &e, 0);
+    if ( *e )
+        return -EINVAL;
+
+    if ( val >= 0 && val <= INT_MAX )
+        *valp = val;
+    else
+        printk("%s: value '%s' is out of range; using value '%u'\n",
+               param, arg, *valp);
+
+    return 0;
+}
+
 unsigned int __read_mostly opt_max_grant_frames = 64;
-integer_runtime_param("gnttab_max_frames", opt_max_grant_frames);
+
+static int __init parse_gnttab_max_frames(const char *arg)
+{
+    return parse_gnttab_limit("gnttab_max_frames", arg,
+                              &opt_max_grant_frames);
+}
+custom_runtime_param("gnttab_max_frames", parse_gnttab_max_frames);
 
 unsigned int __read_mostly opt_max_maptrack_frames = 1024;
-integer_runtime_param("gnttab_max_maptrack_frames", opt_max_maptrack_frames);
+
+static int __init parse_gnttab_max_maptrack_frames(const char *arg)
+{
+    return parse_gnttab_limit("gnttab_max_maptrack_frames", arg,
+                              &opt_max_maptrack_frames);
+}
+custom_runtime_param("gnttab_max_maptrack_frames",
+                     parse_gnttab_max_maptrack_frames);
 
 #ifndef GNTTAB_MAX_VERSION
 #define GNTTAB_MAX_VERSION 2
@@ -1837,12 +1869,18 @@ active_alloc_failed:
     return -ENOMEM;
 }
 
-int grant_table_init(struct domain *d, unsigned int max_grant_frames,
-                     unsigned int max_maptrack_frames)
+int grant_table_init(struct domain *d, int max_grant_frames,
+                     int max_maptrack_frames)
 {
     struct grant_table *gt;
     int ret = -ENOMEM;
 
+    /* Default to maximum value if no value was specified */
+    if ( max_grant_frames < 0 )
+        max_grant_frames = opt_max_grant_frames;
+    if ( max_maptrack_frames < 0 )
+        max_maptrack_frames = opt_max_maptrack_frames;
+
     if ( max_grant_frames < INITIAL_NR_GRANT_FRAMES ||
          max_grant_frames > opt_max_grant_frames ||
          max_maptrack_frames > opt_max_maptrack_frames )
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 9f2cfd602c..e313da499f 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -82,13 +82,15 @@ struct xen_domctl_createdomain {
     uint32_t iommu_opts;
 
     /*
-     * Various domain limits, which impact the quantity of resources (global
-     * mapping space, xenheap, etc) a guest may consume.
+     * Various domain limits, which impact the quantity of resources
+     * (global mapping space, xenheap, etc) a guest may consume.  For
+     * max_grant_frames and max_maptrack_frames, < 0 means "use the
+     * default maximum value in the hypervisor".
      */
     uint32_t max_vcpus;
     uint32_t max_evtchn_port;
-    uint32_t max_grant_frames;
-    uint32_t max_maptrack_frames;
+    int32_t max_grant_frames;
+    int32_t max_maptrack_frames;
 
     struct xen_arch_domainconfig arch;
 };
diff --git a/xen/include/xen/grant_table.h b/xen/include/xen/grant_table.h
index 6f9345d9ef..34886bb6f8 100644
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -36,8 +36,8 @@ extern unsigned int opt_max_grant_frames;
 extern unsigned int opt_max_maptrack_frames;
 
 /* Create/destroy per-domain grant table context. */
-int grant_table_init(struct domain *d, unsigned int max_grant_frames,
-                     unsigned int max_maptrack_frames);
+int grant_table_init(struct domain *d, int max_grant_frames,
+                     int max_maptrack_frames);
 void grant_table_destroy(
     struct domain *d);
 void grant_table_init_vcpu(struct vcpu *v);
@@ -68,8 +68,8 @@ int gnttab_get_status_frame(struct domain *d, unsigned long idx,
 #define opt_max_maptrack_frames 0
 
 static inline int grant_table_init(struct domain *d,
-                                   unsigned int max_grant_frames,
-                                   unsigned int max_maptrack_frames)
+                                   int max_grant_frames,
+                                   int max_maptrack_frames)
 {
     return 0;
 }
