#include "hv/arch.h" #include "hv/log.h" #include "hv/psci.h" #include "hv/string.h" #include "hv/uart.h" #define PSCI32_VERSION 0x84000000ull #define PSCI32_CPU_SUSPEND 0x84000001ull #define PSCI32_CPU_OFF 0x84000002ull #define PSCI32_CPU_ON 0x84000003ull #define PSCI32_AFFINITY_INFO 0x84000004ull #define PSCI32_SYSTEM_OFF 0x84000008ull #define PSCI32_SYSTEM_RESET 0x84000009ull #define PSCI32_FEATURES 0x8400000aull #define PSCI64_CPU_SUSPEND 0xc4000001ull #define PSCI64_CPU_ON 0xc4000003ull #define PSCI64_AFFINITY_INFO 0xc4000004ull #define PSCI_RET_SUCCESS 0ull #define PSCI_RET_NOT_SUPPORTED UINT64_MAX #define PSCI_RET_INVALID_PARAMETERS (~0ull - 1ull) #define PSCI_RET_DENIED (~0ull - 2ull) static uint64_t g_calls; static uint64_t g_denied_cpu_on; static uint64_t g_last_function; static void log_psci(struct vcpu *vcpu, uint64_t fid, uint64_t ret) { struct log_event ev; hv_memset(&ev, 0, sizeof(ev)); ev.kind = LOG_HVC; ev.vcpu = vcpu != (struct vcpu *)0 ? vcpu->id : UINT64_MAX; ev.operand = fid; ev.result = ret; ev.reason = 320u; ev.name = "psci"; log_event_commit(&ev); } bool psci_handle_call(struct vcpu *vcpu, uint64_t function_id) { uint64_t ret = PSCI_RET_NOT_SUPPORTED; if (vcpu == (struct vcpu *)0) { return false; } g_calls++; g_last_function = function_id; switch (function_id) { case PSCI32_VERSION: ret = 0x00010001ull; break; case PSCI32_FEATURES: { uint64_t query = vcpu_read_gpr(vcpu, 1u); if (query == PSCI32_VERSION || query == PSCI32_FEATURES || query == PSCI32_AFFINITY_INFO || query == PSCI64_AFFINITY_INFO || query == PSCI32_SYSTEM_OFF || query == PSCI32_SYSTEM_RESET) { ret = PSCI_RET_SUCCESS; } break; } case PSCI32_AFFINITY_INFO: case PSCI64_AFFINITY_INFO: ret = PSCI_RET_SUCCESS; break; case PSCI32_CPU_SUSPEND: case PSCI64_CPU_SUSPEND: ret = PSCI_RET_DENIED; break; case PSCI32_CPU_ON: case PSCI64_CPU_ON: g_denied_cpu_on++; ret = PSCI_RET_DENIED; break; case PSCI32_CPU_OFF: ret = PSCI_RET_DENIED; break; case PSCI32_SYSTEM_OFF: case PSCI32_SYSTEM_RESET: log_psci(vcpu, function_id, PSCI_RET_SUCCESS); uart_puts("psci power request function="); uart_put_hex64(function_id); uart_puts("; halting\n"); arch_halt(); default: ret = PSCI_RET_NOT_SUPPORTED; break; } vcpu_write_gpr(vcpu, 0u, ret); log_psci(vcpu, function_id, ret); arch_advance_guest_pc(vcpu); return true; } void psci_dump(void) { uart_puts("psci calls="); uart_put_dec64(g_calls); uart_puts(" denied_cpu_on="); uart_put_dec64(g_denied_cpu_on); uart_puts(" last="); uart_put_hex64(g_last_function); uart_puts("\n"); }