#include <linux/types.h>
 #include <linux/zalloc.h>
 
-#include <opencsd/ocsd_if_types.h>
 #include <stdlib.h>
 
 #include "auxtrace.h"
        union perf_event *event_buf;
        struct thread *thread;
        struct thread *prev_packet_thread;
+       ocsd_ex_level prev_packet_el;
+       ocsd_ex_level el;
        struct branch_stack *last_branch;
        struct branch_stack *last_branch_rb;
        struct cs_etm_packet *prev_packet;
 
        queue = &etmq->etm->queues.queue_array[etmq->queue_nr];
        tidq->trace_chan_id = trace_chan_id;
+       tidq->el = tidq->prev_packet_el = ocsd_EL_unknown;
        tidq->thread = machine__findnew_thread(&etm->session->machines.host, -1,
                                               queue->tid);
        tidq->prev_packet_thread = machine__idle_thread(&etm->session->machines.host);
                tmp = tidq->packet;
                tidq->packet = tidq->prev_packet;
                tidq->prev_packet = tmp;
+               tidq->prev_packet_el = tidq->el;
                thread__put(tidq->prev_packet_thread);
                tidq->prev_packet_thread = thread__get(tidq->thread);
        }
        return evsel->core.attr.type == aux->pmu_type;
 }
 
-static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address)
+static struct machine *cs_etm__get_machine(struct cs_etm_queue *etmq,
+                                          ocsd_ex_level el)
 {
-       struct machine *machine;
+       enum cs_etm_pid_fmt pid_fmt = cs_etm__get_pid_fmt(etmq);
 
-       machine = &etmq->etm->session->machines.host;
+       /*
+        * For any virtualisation based on nVHE (e.g. pKVM), or host kernels
+        * running at EL1 assume everything is the host.
+        */
+       if (pid_fmt == CS_ETM_PIDFMT_CTXTID)
+               return &etmq->etm->session->machines.host;
+
+       /*
+        * Not perfect, but otherwise assume anything in EL1 is the default
+        * guest, and everything else is the host. Distinguishing between guest
+        * and host userspaces isn't currently supported either. Neither is
+        * multiple guest support. All this does is reduce the likeliness of
+        * decode errors where we look into the host kernel maps when it should
+        * have been the guest maps.
+        */
+       switch (el) {
+       case ocsd_EL1:
+               return machines__find_guest(&etmq->etm->session->machines,
+                                           DEFAULT_GUEST_KERNEL_ID);
+       case ocsd_EL3:
+       case ocsd_EL2:
+       case ocsd_EL0:
+       case ocsd_EL_unknown:
+       default:
+               return &etmq->etm->session->machines.host;
+       }
+}
+
+static u8 cs_etm__cpu_mode(struct cs_etm_queue *etmq, u64 address,
+                          ocsd_ex_level el)
+{
+       struct machine *machine = cs_etm__get_machine(etmq, el);
 
        if (address >= machine__kernel_start(machine)) {
                if (machine__is_host(machine))
        } else {
                if (machine__is_host(machine))
                        return PERF_RECORD_MISC_USER;
-               else if (perf_guest)
+               else {
+                       /*
+                        * Can't really happen at the moment because
+                        * cs_etm__get_machine() will always return
+                        * machines.host for any non EL1 trace.
+                        */
                        return PERF_RECORD_MISC_GUEST_USER;
-               else
-                       return PERF_RECORD_MISC_HYPERVISOR;
+               }
        }
 }
 
                return 0;
 
        addr_location__init(&al);
-       cpumode = cs_etm__cpu_mode(etmq, address);
        tidq = cs_etm__etmq_get_traceid_queue(etmq, trace_chan_id);
        if (!tidq)
                goto out;
 
+       cpumode = cs_etm__cpu_mode(etmq, address, tidq->el);
+
        if (!thread__find_map(tidq->thread, cpumode, address, &al))
                goto out;
 
        return etmq->buf_len;
 }
 
-static void cs_etm__set_thread(struct cs_etm_auxtrace *etm,
-                              struct cs_etm_traceid_queue *tidq, pid_t tid)
+static void cs_etm__set_thread(struct cs_etm_queue *etmq,
+                              struct cs_etm_traceid_queue *tidq, pid_t tid,
+                              ocsd_ex_level el)
 {
-       struct machine *machine = &etm->session->machines.host;
+       struct machine *machine = cs_etm__get_machine(etmq, el);
 
        if (tid != -1) {
                thread__zput(tidq->thread);
        /* Couldn't find a known thread */
        if (!tidq->thread)
                tidq->thread = machine__idle_thread(machine);
+
+       tidq->el = el;
 }
 
-int cs_etm__etmq_set_tid(struct cs_etm_queue *etmq,
-                        pid_t tid, u8 trace_chan_id)
+int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid,
+                           u8 trace_chan_id, ocsd_ex_level el)
 {
        struct cs_etm_traceid_queue *tidq;
 
        if (!tidq)
                return -EINVAL;
 
-       cs_etm__set_thread(etmq->etm, tidq, tid);
+       cs_etm__set_thread(etmq, tidq, tid, el);
        return 0;
 }
 
        struct perf_sample sample = {.ip = 0,};
 
        event->sample.header.type = PERF_RECORD_SAMPLE;
-       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr);
+       event->sample.header.misc = cs_etm__cpu_mode(etmq, addr, tidq->el);
        event->sample.header.size = sizeof(struct perf_event_header);
 
        /* Set time field based on etm auxtrace config. */
        ip = cs_etm__last_executed_instr(tidq->prev_packet);
 
        event->sample.header.type = PERF_RECORD_SAMPLE;
-       event->sample.header.misc = cs_etm__cpu_mode(etmq, ip);
+       event->sample.header.misc = cs_etm__cpu_mode(etmq, ip,
+                                                    tidq->prev_packet_el);
        event->sample.header.size = sizeof(struct perf_event_header);
 
        /* Set time field based on etm auxtrace config. */