&args->query_debug_event.exception_mask);
                break;
        case KFD_IOC_DBG_TRAP_QUERY_EXCEPTION_INFO:
+               r = kfd_dbg_trap_query_exception_info(target,
+                               args->query_exception_info.source_id,
+                               args->query_exception_info.exception_code,
+                               args->query_exception_info.clear_exception,
+                               (void __user *)args->query_exception_info.info_ptr,
+                               &args->query_exception_info.info_size);
+               break;
        case KFD_IOC_DBG_TRAP_GET_QUEUE_SNAPSHOT:
        case KFD_IOC_DBG_TRAP_GET_DEVICE_SNAPSHOT:
                pr_warn("Debug op %i not supported yet\n", args->op);
 
        return r;
 }
 
+int kfd_dbg_trap_query_exception_info(struct kfd_process *target,
+               uint32_t source_id,
+               uint32_t exception_code,
+               bool clear_exception,
+               void __user *info,
+               uint32_t *info_size)
+{
+       bool found = false;
+       int r = 0;
+       uint32_t copy_size, actual_info_size = 0;
+       uint64_t *exception_status_ptr = NULL;
+
+       if (!target)
+               return -EINVAL;
+
+       if (!info || !info_size)
+               return -EINVAL;
+
+       mutex_lock(&target->event_mutex);
+
+       if (KFD_DBG_EC_TYPE_IS_QUEUE(exception_code)) {
+               /* Per queue exceptions */
+               struct queue *queue = NULL;
+               int i;
+
+               for (i = 0; i < target->n_pdds; i++) {
+                       struct kfd_process_device *pdd = target->pdds[i];
+                       struct qcm_process_device *qpd = &pdd->qpd;
+
+                       list_for_each_entry(queue, &qpd->queues_list, list) {
+                               if (!found && queue->properties.queue_id == source_id) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (found)
+                               break;
+               }
+
+               if (!found) {
+                       r = -EINVAL;
+                       goto out;
+               }
+
+               if (!(queue->properties.exception_status & KFD_EC_MASK(exception_code))) {
+                       r = -ENODATA;
+                       goto out;
+               }
+               exception_status_ptr = &queue->properties.exception_status;
+       } else if (KFD_DBG_EC_TYPE_IS_DEVICE(exception_code)) {
+               /* Per device exceptions */
+               struct kfd_process_device *pdd = NULL;
+               int i;
+
+               for (i = 0; i < target->n_pdds; i++) {
+                       pdd = target->pdds[i];
+                       if (pdd->dev->id == source_id) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       r = -EINVAL;
+                       goto out;
+               }
+
+               if (!(pdd->exception_status & KFD_EC_MASK(exception_code))) {
+                       r = -ENODATA;
+                       goto out;
+               }
+
+               if (exception_code == EC_DEVICE_MEMORY_VIOLATION) {
+                       copy_size = min((size_t)(*info_size), pdd->vm_fault_exc_data_size);
+
+                       if (copy_to_user(info, pdd->vm_fault_exc_data, copy_size)) {
+                               r = -EFAULT;
+                               goto out;
+                       }
+                       actual_info_size = pdd->vm_fault_exc_data_size;
+                       if (clear_exception) {
+                               kfree(pdd->vm_fault_exc_data);
+                               pdd->vm_fault_exc_data = NULL;
+                               pdd->vm_fault_exc_data_size = 0;
+                       }
+               }
+               exception_status_ptr = &pdd->exception_status;
+       } else if (KFD_DBG_EC_TYPE_IS_PROCESS(exception_code)) {
+               /* Per process exceptions */
+               if (!(target->exception_status & KFD_EC_MASK(exception_code))) {
+                       r = -ENODATA;
+                       goto out;
+               }
+
+               if (exception_code == EC_PROCESS_RUNTIME) {
+                       copy_size = min((size_t)(*info_size), sizeof(target->runtime_info));
+
+                       if (copy_to_user(info, (void *)&target->runtime_info, copy_size)) {
+                               r = -EFAULT;
+                               goto out;
+                       }
+
+                       actual_info_size = sizeof(target->runtime_info);
+               }
+
+               exception_status_ptr = &target->exception_status;
+       } else {
+               pr_debug("Bad exception type [%i]\n", exception_code);
+               r = -EINVAL;
+               goto out;
+       }
+
+       *info_size = actual_info_size;
+       if (clear_exception)
+               *exception_status_ptr &= ~KFD_EC_MASK(exception_code);
+out:
+       mutex_unlock(&target->event_mutex);
+       return r;
+}
+
 void kfd_dbg_set_enabled_debug_exception_mask(struct kfd_process *target,
                                        uint64_t exception_set_mask)
 {