diff --git a/tools/libxl/libxl_dom.c b/tools/libxl/libxl_dom.c
index 5ef35e66bf..72ca5e266c 100644
--- a/tools/libxl/libxl_dom.c
+++ b/tools/libxl/libxl_dom.c
@@ -317,17 +317,31 @@ static int hvm_set_mca_capabilities(libxl__gc *gc, uint32_t domid,
 static void hvm_set_conf_params(xc_interface *handle, uint32_t domid,
                                 libxl_domain_build_info *const info)
 {
-    xc_hvm_param_set(handle, domid, HVM_PARAM_PAE_ENABLED,
-                    libxl_defbool_val(info->u.hvm.pae));
+    switch(info->type) {
+    case LIBXL_DOMAIN_TYPE_PVH:
+        xc_hvm_param_set(handle, domid, HVM_PARAM_PAE_ENABLED, true);
+        xc_hvm_param_set(handle, domid, HVM_PARAM_TIMER_MODE,
+                         timer_mode(info));
+        xc_hvm_param_set(handle, domid, HVM_PARAM_NESTEDHVM,
+                         libxl_defbool_val(info->nested_hvm));
+        break;
+    case LIBXL_DOMAIN_TYPE_HVM:
+        xc_hvm_param_set(handle, domid, HVM_PARAM_PAE_ENABLED,
+                         libxl_defbool_val(info->u.hvm.pae));
 #if defined(__i386__) || defined(__x86_64__)
-    xc_hvm_param_set(handle, domid, HVM_PARAM_HPET_ENABLED,
-                    libxl_defbool_val(info->u.hvm.hpet));
+        xc_hvm_param_set(handle, domid, HVM_PARAM_HPET_ENABLED,
+                         libxl_defbool_val(info->u.hvm.hpet));
 #endif
-    xc_hvm_param_set(handle, domid, HVM_PARAM_TIMER_MODE, timer_mode(info));
-    xc_hvm_param_set(handle, domid, HVM_PARAM_VPT_ALIGN,
-                    libxl_defbool_val(info->u.hvm.vpt_align));
-    xc_hvm_param_set(handle, domid, HVM_PARAM_NESTEDHVM,
-                    libxl_defbool_val(info->nested_hvm));
+        xc_hvm_param_set(handle, domid, HVM_PARAM_TIMER_MODE,
+                         timer_mode(info));
+        xc_hvm_param_set(handle, domid, HVM_PARAM_VPT_ALIGN,
+                         libxl_defbool_val(info->u.hvm.vpt_align));
+        xc_hvm_param_set(handle, domid, HVM_PARAM_NESTEDHVM,
+                         libxl_defbool_val(info->nested_hvm));
+        break;
+    default:
+        abort();
+    }
 }
 
 int libxl__build_pre(libxl__gc *gc, uint32_t domid,
@@ -467,9 +481,11 @@ int libxl__build_pre(libxl__gc *gc, uint32_t domid,
     state->store_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->store_domid);
     state->console_port = xc_evtchn_alloc_unbound(ctx->xch, domid, state->console_domid);
 
-    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+    if (info->type != LIBXL_DOMAIN_TYPE_PV)
         hvm_set_conf_params(ctx->xch, domid, info);
+
 #if defined(__i386__) || defined(__x86_64__)
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
         rc = hvm_set_viridian_features(gc, domid, info);
         if (rc)
             return rc;
@@ -477,10 +493,10 @@ int libxl__build_pre(libxl__gc *gc, uint32_t domid,
         rc = hvm_set_mca_capabilities(gc, domid, info);
         if (rc)
             return rc;
-#endif
     }
+#endif
 
-    /* Alternate p2m support on x86 is available only for HVM guests. */
+    /* Alternate p2m support on x86 is available only for PVH/HVM guests. */
     if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
         /* The config parameter "altp2m" replaces the parameter "altp2mhvm". For
          * legacy reasons, both parameters are accepted on x86 HVM guests.
@@ -494,6 +510,9 @@ int libxl__build_pre(libxl__gc *gc, uint32_t domid,
         else
             xc_hvm_param_set(ctx->xch, domid, HVM_PARAM_ALTP2M,
                              info->altp2m);
+    } else if (info->type == LIBXL_DOMAIN_TYPE_PVH) {
+        xc_hvm_param_set(ctx->xch, domid, HVM_PARAM_ALTP2M,
+                         info->altp2m);
     }
 
     rc = libxl__arch_domain_create(gc, d_config, domid);
@@ -847,7 +866,7 @@ static int hvm_build_set_params(xc_interface *handle, uint32_t domid,
     uint64_t str_mfn, cons_mfn;
     int i;
 
-    if (info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE) {
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
         va_map = xc_map_foreign_range(handle, domid,
                                       XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
                                       HVM_INFO_PFN);
@@ -903,7 +922,7 @@ static int hvm_build_set_xs_values(libxl__gc *gc,
 
     /* Only one module can be passed. PVHv2 guests do not support this. */
     if (dom->acpi_modules[0].guest_addr_out && 
-        info->device_model_version !=LIBXL_DEVICE_MODEL_VERSION_NONE) {
+        info->type == LIBXL_DOMAIN_TYPE_HVM) {
         path = GCSPRINTF("/local/domain/%d/"HVM_XS_ACPI_PT_ADDRESS, domid);
 
         ret = libxl__xs_printf(gc, XBT_NULL, path, "0x%"PRIx64,
@@ -964,6 +983,7 @@ out:
 
 static int libxl__domain_firmware(libxl__gc *gc,
                                   libxl_domain_build_info *info,
+                                  libxl__domain_build_state *state,
                                   struct xc_dom_image *dom)
 {
     libxl_ctx *ctx = libxl__gc_owner(gc);
@@ -973,39 +993,64 @@ static int libxl__domain_firmware(libxl__gc *gc,
     void *data;
     const char *bios_filename = NULL;
 
-    if (info->u.hvm.firmware)
-        firmware = info->u.hvm.firmware;
-    else {
-        switch (info->device_model_version)
-        {
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
-            firmware = "hvmloader";
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
-            firmware = "hvmloader";
-            break;
-        case LIBXL_DEVICE_MODEL_VERSION_NONE:
-            if (info->kernel == NULL) {
-                LOG(ERROR, "no device model requested without a kernel");
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
+        if (info->u.hvm.firmware) {
+            firmware = info->u.hvm.firmware;
+        } else {
+            switch (info->device_model_version)
+            {
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+                firmware = "hvmloader";
+                break;
+            default:
+                LOG(ERROR, "invalid device model version %d",
+                    info->device_model_version);
                 rc = ERROR_FAIL;
                 goto out;
             }
-            break;
-        default:
-            LOG(ERROR, "invalid device model version %d",
-                info->device_model_version);
-            rc = ERROR_FAIL;
-            goto out;
         }
     }
 
-    if (info->kernel != NULL &&
-        info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) {
+    if (state->pv_kernel.path != NULL && info->type == LIBXL_DOMAIN_TYPE_PVH) {
         /* Try to load a kernel instead of the firmware. */
-        rc = xc_dom_kernel_file(dom, info->kernel);
-        if (rc == 0 && info->ramdisk != NULL)
-            rc = xc_dom_ramdisk_file(dom, info->ramdisk);
+        if (state->pv_kernel.mapped) {
+            rc = xc_dom_kernel_mem(dom, state->pv_kernel.data,
+                                   state->pv_kernel.size);
+            if (rc) {
+                LOGE(ERROR, "xc_dom_kernel_mem failed");
+                goto out;
+            }
+        } else {
+            rc = xc_dom_kernel_file(dom, state->pv_kernel.path);
+            if (rc) {
+                LOGE(ERROR, "xc_dom_kernel_file failed");
+                goto out;
+            }
+        }
+
+        if (state->pv_ramdisk.path && strlen(state->pv_ramdisk.path)) {
+            if (state->pv_ramdisk.mapped) {
+                rc = xc_dom_ramdisk_mem(dom, state->pv_ramdisk.data,
+                                        state->pv_ramdisk.size);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_ramdisk_mem failed");
+                    goto out;
+                }
+            } else {
+                rc = xc_dom_ramdisk_file(dom, state->pv_ramdisk.path);
+                if (rc) {
+                    LOGE(ERROR, "xc_dom_ramdisk_file failed");
+                    goto out;
+                }
+            }
+        }
     } else {
+        /*
+         * Only HVM guests should get here, PVH should always have a set
+         * kernel at this point.
+         */
+        assert(info->type == LIBXL_DOMAIN_TYPE_HVM);
         rc = xc_dom_kernel_file(dom, libxl__abs_path(gc, firmware,
                                                  libxl__xenfirmwaredir_path()));
     }
@@ -1015,7 +1060,8 @@ static int libxl__domain_firmware(libxl__gc *gc,
         goto out;
     }
 
-    if (info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
         if (info->u.hvm.system_firmware) {
             bios_filename = info->u.hvm.system_firmware;
         } else {
@@ -1039,7 +1085,8 @@ static int libxl__domain_firmware(libxl__gc *gc,
         if (rc) goto out;
     }
 
-    if (info->u.hvm.smbios_firmware) {
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->u.hvm.smbios_firmware) {
         data = NULL;
         e = libxl_read_file_contents(ctx, info->u.hvm.smbios_firmware,
                                      &data, &datalen);
@@ -1057,14 +1104,8 @@ static int libxl__domain_firmware(libxl__gc *gc,
         }
     }
 
-    if (info->u.hvm.acpi_firmware) {
-
-        if (info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_NONE) {
-            LOGE(ERROR, "PVH guests do not allow loading ACPI modules");
-            rc = ERROR_FAIL;
-            goto out;
-        }
-
+    if (info->type == LIBXL_DOMAIN_TYPE_HVM &&
+        info->u.hvm.acpi_firmware) {
         data = NULL;
         e = libxl_read_file_contents(ctx, info->u.hvm.acpi_firmware,
                                      &data, &datalen);
@@ -1097,13 +1138,12 @@ int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
     uint64_t mmio_start, lowmem_end, highmem_end, mem_size;
     libxl_domain_build_info *const info = &d_config->b_info;
     struct xc_dom_image *dom = NULL;
-    bool device_model =
-        info->device_model_version != LIBXL_DEVICE_MODEL_VERSION_NONE ?
-        true : false;
+    bool device_model = info->type == LIBXL_DOMAIN_TYPE_HVM ? true : false;
 
     xc_dom_loginit(ctx->xch);
 
-    dom = xc_dom_allocate(ctx->xch, info->cmdline, NULL);
+    dom = xc_dom_allocate(ctx->xch, info->type == LIBXL_DOMAIN_TYPE_PVH ?
+                          state->pv_cmdline : info->cmdline, NULL);
     if (!dom) {
         LOGE(ERROR, "xc_dom_allocate failed");
         rc = ERROR_NOMEM;
@@ -1128,7 +1168,7 @@ int libxl__build_hvm(libxl__gc *gc, uint32_t domid,
             dom->mmio_size = info->u.hvm.mmio_hole_memkb << 10;
     }
 
-    rc = libxl__domain_firmware(gc, info, dom);
+    rc = libxl__domain_firmware(gc, info, state, dom);
     if (rc != 0) {
         LOG(ERROR, "initializing domain firmware failed");
         goto out;
