diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 0014c34..da8cdc8 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -476,6 +476,32 @@ static void do_cpu_reset(void *opaque)
     }
 }
 
+static int check_load_zimage(const char *filename)
+{
+    int fd;
+    uint8_t buf[40];
+    uint32_t *p = (uint32_t *) &buf[36];
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+        perror(filename);
+        return -1;
+    }
+
+    memset(buf, 0, sizeof(buf));
+    if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
+        close(fd);
+        return -1;
+    }
+
+    /* Check for zImage magic number */
+    if (*p == 0x016F2818) {
+        return 1;
+    }
+
+   return 0;
+}
+
 void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
 {
     CPUState *cs;
@@ -515,15 +541,23 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
         return;
     }
 
-    if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+    if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) &&
+                    (!cpu->env.el1_aarch32)) {
         primary_loader = bootloader_aarch64;
         kernel_load_offset = KERNEL64_LOAD_ADDR;
         elf_machine = EM_AARCH64;
     } else {
-        primary_loader = bootloader;
-        kernel_load_offset = KERNEL_LOAD_ADDR;
-        elf_machine = EM_ARM;
-    }
+        if (check_load_zimage(info->kernel_filename)) {
+            primary_loader = bootloader;
+            kernel_load_offset = KERNEL_LOAD_ADDR;
+            elf_machine = EM_ARM;
+        } else {
+            primary_loader = bootloader;
+            /* Assuming we are loading Image hence aligning it to 0x8000 */
+            kernel_load_offset = KERNEL_LOAD_ADDR - 0x8000;
+            elf_machine = EM_ARM;
+        }
+   }
 
     info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb");
 
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 314e55b..64213e6 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -204,7 +204,8 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
         qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
 
         cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
-        if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
+        if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64) &&
+                                    (!armcpu->env.el1_aarch32)) {
             cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
             cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
             migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
@@ -527,6 +528,24 @@ static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
     return board->fdt;
 }
 
+#if defined(TARGET_AARCH64) && !defined(CONFIG_USER_ONLY)
+static void check_special_cpu_model_flags(const char *cpu_model,
+                                          Object *cpuobj)
+{
+        ARMCPU *cpu = ARM_CPU(cpuobj);
+
+        if (!cpu) {
+            return;
+        }
+
+        if (strcmp(cpu_model, "host,el1_aarch32") == 0) {
+            cpu->env.el1_aarch32 = 1;
+        } else {
+            cpu->env.el1_aarch32 = 0;
+        }
+}
+#endif
+
 static void machvirt_init(MachineState *machine)
 {
     qemu_irq pic[NUM_IRQS];
@@ -540,6 +559,12 @@ static void machvirt_init(MachineState *machine)
         cpu_model = "cortex-a15";
     }
 
+#if defined(TARGET_AARCH64) && !defined(CONFIG_USER_ONLY)
+    if (strcmp(cpu_model, "host,el1_aarch32") == 0) {
+        cpu_model = "host";
+    }
+#endif
+
     vbi = find_machine_info(cpu_model);
 
     if (!vbi) {
@@ -578,6 +603,9 @@ static void machvirt_init(MachineState *machine)
             object_property_set_int(cpuobj, vbi->memmap[VIRT_CPUPERIPHS].base,
                                     "reset-cbar", &error_abort);
         }
+#if defined(TARGET_AARCH64) && !defined(CONFIG_USER_ONLY)
+        check_special_cpu_model_flags(machine->cpu_model, cpuobj);
+#endif
 
         object_property_set_bool(cpuobj, true, "realized", NULL);
     }
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index 5ce7350..37dfe30 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -103,7 +103,7 @@ static void arm_cpu_reset(CPUState *s)
         env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q';
     }
 
-    if (arm_feature(env, ARM_FEATURE_AARCH64)) {
+    if (arm_feature(env, ARM_FEATURE_AARCH64) && (!env->el1_aarch32)) {
         /* 64 bit CPUs always start in 64 bit mode */
         env->aarch64 = 1;
 #if defined(CONFIG_USER_ONLY)
@@ -394,7 +394,8 @@ static void arm_cpu_post_init(Object *obj)
                                  &error_abort);
     }
 
-    if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+    if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) &&
+                                    (!cpu->env.el1_aarch32)) {
         qdev_property_add_static(DEVICE(obj), &arm_cpu_rvbar_property,
                                  &error_abort);
     }
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 7f80090..d2f8eda 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -343,6 +343,8 @@ typedef struct CPUARMState {
     /* Internal CPU feature flags.  */
     uint64_t features;
 
+    uint32_t el1_aarch32; /* 1 If CPU is in aarch64 state and el1 is aarch32 */
+
     void *nvic;
     const struct arm_boot_info *boot_info;
 } CPUARMState;
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
index c615286..03e6dcc 100644
--- a/target-arm/kvm64.c
+++ b/target-arm/kvm64.c
@@ -97,6 +97,15 @@ int kvm_arch_init_vcpu(CPUState *cs)
         cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PSCI_0_2;
     }
 
+    if ((cpu->env.el1_aarch32)) {
+        if ((kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_EL1_32BIT))) {
+            cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT;
+        } else {
+            fprintf(stderr, "KVM is not supporting EL1 AARCH32 mode\n");
+            return -1;
+        }
+    }
+
     /* Do KVM_ARM_VCPU_INIT ioctl */
     ret = kvm_arm_vcpu_init(cs);
     if (ret) {
@@ -111,6 +120,35 @@ int kvm_arch_init_vcpu(CPUState *cs)
 #define AARCH64_CORE_REG(x)   (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
                  KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(x))
 
+
+static int kvm_arch32_put_registers(CPUState *cs, int level)
+{
+    ARMCPU *cpu = ARM_CPU(cs);
+    CPUARMState *env = &cpu->env;
+    struct kvm_one_reg reg;
+    int ret;
+    int i;
+
+    for (i = 0; i < 16; i++) {
+        reg.id = AARCH64_CORE_REG(regs.regs[i]);
+        reg.addr = (uintptr_t) &env->regs[i];
+        ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+        if (ret) {
+            return ret;
+        }
+    }
+
+    /* PC will be set in env->regs[15] */
+    reg.addr = (uintptr_t) &env->regs[15];
+    reg.id = AARCH64_CORE_REG(regs.pc);
+    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &reg);
+    if (ret) {
+        return ret;
+    }
+
+    return ret;
+}
+
 int kvm_arch_put_registers(CPUState *cs, int level)
 {
     struct kvm_one_reg reg;
@@ -121,6 +159,10 @@ int kvm_arch_put_registers(CPUState *cs, int level)
     ARMCPU *cpu = ARM_CPU(cs);
     CPUARMState *env = &cpu->env;
 
+    if (env->el1_aarch32) {
+        return kvm_arch32_put_registers(cs, level);
+    }
+
     for (i = 0; i < 31; i++) {
         reg.id = AARCH64_CORE_REG(regs.regs[i]);
         reg.addr = (uintptr_t) &env->xregs[i];
@@ -188,6 +230,33 @@ int kvm_arch_put_registers(CPUState *cs, int level)
     return ret;
 }
 
+static int kvm_arch32_get_registers(CPUState *cs)
+{
+    ARMCPU *cpu = ARM_CPU(cs);
+    CPUARMState *env = &cpu->env;
+    struct kvm_one_reg reg;
+    int ret;
+    int i;
+
+    for (i = 0; i < 16; i++) {
+        reg.id = AARCH64_CORE_REG(regs.regs[i]);
+        reg.addr = (uintptr_t) &env->regs[i];
+        ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+        if (ret) {
+            return ret;
+        }
+    }
+
+    reg.addr = (uintptr_t) &env->regs[15];
+    reg.id = AARCH64_CORE_REG(regs.pc);
+    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &reg);
+    if (ret) {
+        return ret;
+    }
+
+    return ret;
+}
+
 int kvm_arch_get_registers(CPUState *cs)
 {
     struct kvm_one_reg reg;
@@ -198,6 +267,10 @@ int kvm_arch_get_registers(CPUState *cs)
     ARMCPU *cpu = ARM_CPU(cs);
     CPUARMState *env = &cpu->env;
 
+    if (env->el1_aarch32) {
+        return kvm_arch32_get_registers(cs);
+    }
+
     for (i = 0; i < 31; i++) {
         reg.id = AARCH64_CORE_REG(regs.regs[i]);
         reg.addr = (uintptr_t) &env->xregs[i];
