1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include "el2_cpu_state_priv.h"
8 
9 #include <arch/arm64/el2_state.h>
10 #include <arch/arm64/mmu.h>
11 #include <arch/hypervisor.h>
12 #include <dev/interrupt.h>
13 #include <fbl/auto_lock.h>
14 #include <fbl/mutex.h>
15 #include <ktl/move.h>
16 #include <hypervisor/cpu.h>
17 #include <vm/physmap.h>
18 #include <vm/pmm.h>
19 
20 static fbl::Mutex guest_mutex;
21 static size_t num_guests TA_GUARDED(guest_mutex) = 0;
22 static ktl::unique_ptr<El2CpuState> el2_cpu_state TA_GUARDED(guest_mutex);
23 
Init()24 zx_status_t El2TranslationTable::Init() {
25     zx_status_t status = l0_page_.Alloc(0);
26     if (status != ZX_OK) {
27         return status;
28     }
29     status = l1_page_.Alloc(0);
30     if (status != ZX_OK) {
31         return status;
32     }
33 
34     // L0: Point to a single L1 translation table.
35     pte_t* l0_pte = l0_page_.VirtualAddress<pte_t>();
36     *l0_pte = l1_page_.PhysicalAddress() | MMU_PTE_L012_DESCRIPTOR_TABLE;
37 
38     // L1: Identity map the first 512GB of physical memory at.
39     pte_t* l1_pte = l1_page_.VirtualAddress<pte_t>();
40     for (size_t i = 0; i < PAGE_SIZE / sizeof(pte_t); i++) {
41         l1_pte[i] = i * (1u << 30) | MMU_PTE_ATTR_AF | MMU_PTE_ATTR_SH_INNER_SHAREABLE |
42                     MMU_PTE_ATTR_AP_P_RW_U_RW | MMU_PTE_ATTR_NORMAL_MEMORY |
43                     MMU_PTE_L012_DESCRIPTOR_BLOCK;
44     }
45 
46     __dmb(ARM_MB_SY);
47     return ZX_OK;
48 }
49 
Base() const50 zx_paddr_t El2TranslationTable::Base() const {
51     return l0_page_.PhysicalAddress();
52 }
53 
Alloc()54 zx_status_t El2Stack::Alloc() {
55     return page_.Alloc(0);
56 }
57 
Top() const58 zx_paddr_t El2Stack::Top() const {
59     return page_.PhysicalAddress() + PAGE_SIZE;
60 }
61 
OnTask(void * context,uint cpu_num)62 zx_status_t El2CpuState::OnTask(void* context, uint cpu_num) {
63     auto cpu_state = static_cast<El2CpuState*>(context);
64     El2TranslationTable& table = cpu_state->table_;
65     El2Stack& stack = cpu_state->stacks_[cpu_num];
66     zx_status_t status = arm64_el2_on(table.Base(), stack.Top());
67     if (status != ZX_OK) {
68         dprintf(CRITICAL, "Failed to turn EL2 on for CPU %u\n", cpu_num);
69         return status;
70     }
71     unmask_interrupt(kMaintenanceVector);
72     unmask_interrupt(kTimerVector);
73     return ZX_OK;
74 }
75 
el2_off_task(void * arg)76 static void el2_off_task(void* arg) {
77     mask_interrupt(kTimerVector);
78     mask_interrupt(kMaintenanceVector);
79     zx_status_t status = arm64_el2_off();
80     if (status != ZX_OK) {
81         dprintf(CRITICAL, "Failed to turn EL2 off for CPU %u\n", arch_curr_cpu_num());
82     }
83 }
84 
85 // static
Create(ktl::unique_ptr<El2CpuState> * out)86 zx_status_t El2CpuState::Create(ktl::unique_ptr<El2CpuState>* out) {
87     fbl::AllocChecker ac;
88     ktl::unique_ptr<El2CpuState> cpu_state(new (&ac) El2CpuState);
89     if (!ac.check()) {
90         return ZX_ERR_NO_MEMORY;
91     }
92     zx_status_t status = cpu_state->Init();
93     if (status != ZX_OK) {
94         return status;
95     }
96 
97     // Initialise the EL2 translation table.
98     status = cpu_state->table_.Init();
99     if (status != ZX_OK) {
100         return status;
101     }
102 
103     // Allocate EL2 stack for each CPU.
104     size_t num_cpus = arch_max_num_cpus();
105     El2Stack* stacks = new (&ac) El2Stack[num_cpus];
106     if (!ac.check()) {
107         return ZX_ERR_NO_MEMORY;
108     }
109     fbl::Array<El2Stack> el2_stacks(stacks, num_cpus);
110     for (auto& stack : el2_stacks) {
111         zx_status_t status = stack.Alloc();
112         if (status != ZX_OK) {
113             return status;
114         }
115     }
116     cpu_state->stacks_ = ktl::move(el2_stacks);
117 
118     // Setup EL2 for all online CPUs.
119     cpu_state->cpu_mask_ = percpu_exec(OnTask, cpu_state.get());
120     if (cpu_state->cpu_mask_ != mp_get_online_mask()) {
121         return ZX_ERR_NOT_SUPPORTED;
122     }
123 
124     *out = ktl::move(cpu_state);
125     return ZX_OK;
126 }
127 
~El2CpuState()128 El2CpuState::~El2CpuState() {
129     mp_sync_exec(MP_IPI_TARGET_MASK, cpu_mask_, el2_off_task, nullptr);
130 }
131 
alloc_vmid(uint8_t * vmid)132 zx_status_t alloc_vmid(uint8_t* vmid) {
133     fbl::AutoLock lock(&guest_mutex);
134     if (num_guests == 0) {
135         zx_status_t status = El2CpuState::Create(&el2_cpu_state);
136         if (status != ZX_OK) {
137             return status;
138         }
139     }
140     num_guests++;
141     return el2_cpu_state->AllocId(vmid);
142 }
143 
free_vmid(uint8_t vmid)144 zx_status_t free_vmid(uint8_t vmid) {
145     fbl::AutoLock lock(&guest_mutex);
146     zx_status_t status = el2_cpu_state->FreeId(vmid);
147     if (status != ZX_OK) {
148         return status;
149     }
150     num_guests--;
151     if (num_guests == 0) {
152         el2_cpu_state.reset();
153     }
154     return ZX_OK;
155 }
156