quic/qbox
Loading...
Searching...
No Matches
cpu_arm_host.h
1/*
2 * This file is part of libqbox
3 * Copyright (c) 2026 Qualcomm Innovation Center, Inc. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#pragma once
9
10#include <string>
11
12#include <cci_configuration>
13
14#include <libqemu-cxx/target/aarch64.h>
15
16#include <tlm-extensions/exclusive-access.h>
17#include <module_factory_registery.h>
18
19#include <arm.h>
20#include <ports/qemu-initiator-signal-socket.h>
21#include <ports/qemu-target-signal-socket.h>
22#include <qemu-instance.h>
23
25{
26protected:
27 int get_psci_conduit_val() const
28 {
29 if (p_psci_conduit.get_value() == "disabled") {
30 return 0;
31 } else if (p_psci_conduit.get_value() == "smc") {
32 return 1;
33 } else if (p_psci_conduit.get_value() == "hvc") {
34 return 2;
35 } else {
36 /* TODO: report warning */
37 return 0;
38 }
39 }
40
41 void add_exclusive_ext(TlmPayload& pl)
42 {
44 ext->add_hop(m_cpu.get_index());
45 pl.set_extension(ext);
46 }
47
48 static uint64_t extract_data_from_payload(const TlmPayload& pl)
49 {
50 uint8_t* ptr = pl.get_data_ptr() + pl.get_data_length() - 1;
51 uint64_t ret = 0;
52
53 /* QEMU never accesses more than 64 bits at the same time */
54 assert(pl.get_data_length() <= 8);
55
56 while (ptr >= pl.get_data_ptr()) {
57 ret = (ret << 8) | *(ptr--);
58 }
59
60 return ret;
61 }
62
63public:
64 cci::cci_param<unsigned int> p_mp_affinity;
65 cci::cci_param<bool> p_start_powered_off;
66 cci::cci_param<std::string> p_psci_conduit;
67 cci::cci_param<uint64_t> p_rvbar;
68 cci::cci_param<uint64_t> p_cntfrq_hz;
69
74
75 QemuInitiatorSignalSocket irq_timer_phys_out;
76 QemuInitiatorSignalSocket irq_timer_virt_out;
77 QemuInitiatorSignalSocket irq_timer_hyp_out;
78 QemuInitiatorSignalSocket irq_timer_sec_out;
79 QemuInitiatorSignalSocket irq_maintenance_out;
80 QemuInitiatorSignalSocket irq_pmu_out;
81 cpu_arm_host(const sc_core::sc_module_name& name, sc_core::sc_object* o)
82 : cpu_arm_host(name, *(dynamic_cast<QemuInstance*>(o)))
83 {
84 }
85 cpu_arm_host(const sc_core::sc_module_name& name, QemuInstance& inst)
86 : QemuCpuArm(name, inst, "host-arm")
87 , p_mp_affinity("mp_affinity", 0, "Multi-processor affinity value")
88 , p_start_powered_off("start_powered_off", false,
89 "Start and reset the CPU "
90 "in powered-off state")
91 , p_psci_conduit("psci_conduit", "disabled",
92 "Set the QEMU PSCI conduit: "
93 "disabled->no conduit, "
94 "hvc->through hvc call, "
95 "smc->through smc call")
96 , p_rvbar("rvbar", 0ull, "Reset vector base address register value")
97 , p_cntfrq_hz("cntfrq_hz", 0ull, "CPU Generic Timer CNTFRQ in Hz")
98
99 , irq_in("irq_in")
100 , fiq_in("fiq_in")
101 , virq_in("virq_in")
102 , vfiq_in("vfiq_in")
103 , irq_timer_phys_out("irq_timer_phys_out")
104 , irq_timer_virt_out("irq_timer_virt_out")
105 , irq_timer_hyp_out("irq_timer_hyp_out")
106 , irq_timer_sec_out("irq_timer_sec_out")
107 , irq_maintenance_out("gicv3_maintenance_interrupt")
108 , irq_pmu_out("pmu_interrupt")
109 {
110 m_external_ev |= irq_in->default_event();
111 m_external_ev |= fiq_in->default_event();
112 m_external_ev |= virq_in->default_event();
113 m_external_ev |= vfiq_in->default_event();
114 }
115
116 void before_end_of_elaboration() override
117 {
118 QemuCpuArm::before_end_of_elaboration();
119
120 qemu::CpuAarch64 cpu(m_cpu);
121 cpu.set_aarch64_mode(true);
122
123 if (!p_mp_affinity.is_default_value()) {
124 cpu.set_prop_int("mp-affinity", p_mp_affinity);
125 }
126
127 cpu.set_prop_bool("start-powered-off", p_start_powered_off);
128 cpu.set_prop_int("psci-conduit", get_psci_conduit_val());
129
130 cpu.set_prop_int("rvbar", p_rvbar);
131 if (!p_cntfrq_hz.is_default_value()) {
132 cpu.set_prop_int("cntfrq", p_cntfrq_hz);
133 }
134 }
135
136 void end_of_elaboration() override
137 {
138 QemuCpuArm::end_of_elaboration();
139
140 irq_in.init(m_dev, 0);
141 fiq_in.init(m_dev, 1);
142 virq_in.init(m_dev, 2);
143 vfiq_in.init(m_dev, 3);
144
145 irq_timer_phys_out.init(m_dev, 0);
146 irq_timer_virt_out.init(m_dev, 1);
147 irq_timer_hyp_out.init(m_dev, 2);
148 irq_timer_sec_out.init(m_dev, 3);
149 irq_maintenance_out.init_named(m_dev, "gicv3-maintenance-interrupt", 0);
150 irq_pmu_out.init_named(m_dev, "pmu-interrupt", 0);
151 }
152
153 void initiator_customize_tlm_payload(TlmPayload& payload) override
154 {
155 uint64_t addr;
157
158 QemuCpu::initiator_customize_tlm_payload(payload);
159
160 addr = payload.get_address();
161
162 if (!arm_cpu.is_in_exclusive_context()) {
163 return;
164 }
165
166 if (addr != arm_cpu.get_exclusive_addr()) {
167 return;
168 }
169
170 /*
171 * We are in the load/store pair of the cmpxchg of the exclusive store
172 * implementation. Add the exclusive access extension to lock the
173 * memory and check for exclusive store success in
174 * initiator_tidy_tlm_payload.
175 */
176 add_exclusive_ext(payload);
177 }
178
179 void initiator_tidy_tlm_payload(TlmPayload& payload) override
180 {
181 using namespace tlm;
182
185
186 QemuCpu::initiator_tidy_tlm_payload(payload);
187
188 payload.get_extension(ext);
189 bool exit_tb = false;
190
191 if (ext == nullptr) {
192 return;
193 }
194
195 if (payload.get_command() == TLM_WRITE_COMMAND) {
196 auto sta = ext->get_exclusive_store_status();
197
198 /* We just executed an exclusive store. Check its status */
199 if (sta == ExclusiveAccessTlmExtension::EXCLUSIVE_STORE_FAILURE) {
200 /*
201 * To actually make the exclusive store fails, we need to trick
202 * QEMU into thinking that the value at the store address has
203 * changed compared to when it did the corresponding ldrex (due
204 * to the way exclusive loads/stores are implemented in QEMU).
205 * For this, we simply smash the exclusive_val field of the ARM
206 * CPU state in case it currently matches with the value in
207 * memory.
208 */
209 uint64_t exclusive_val = arm_cpu.get_exclusive_val();
210 uint64_t mem_val = extract_data_from_payload(payload);
211 uint64_t mask = (payload.get_data_length() == 8) ? -1 : (1 << (8 * payload.get_data_length())) - 1;
212
213 if ((exclusive_val & mask) == mem_val) {
214 arm_cpu.set_exclusive_val(~exclusive_val);
215
216 /*
217 * Exit the execution loop to force QEMU to re-do the
218 * store. This is necessary because we modify exclusive_val
219 * in the CPU env. This field is also mapped on a TCG
220 * global. Even though the qemu_st_ixx TCG opcs are marked
221 * TCG_OPF_CALL_CLOBBER, TCG does not reload the global
222 * after the store as I thought it would do. To force this,
223 * we exit the TB here so that the new exclusive_val value
224 * will be taken into account on the next execution.
225 */
226 exit_tb = true;
227 }
228
229 payload.set_response_status(TLM_OK_RESPONSE);
230 }
231 }
232
233 payload.clear_extension(ext);
234 delete ext;
235
236 if (exit_tb) {
237 /*
238 * FIXME: exiting the CPU loop from here is a bit violent. The
239 * caller won't have a chance to destruct its stack objects. The
240 * object model should be reworked to allow exiting the loop
241 * cleanly.
242 */
243 m_cpu.exit_loop_from_io();
244 }
245 }
246};
247
248extern "C" void module_register();
Exclusive load/store TLM extension.
Definition exclusive-access.h:36
Definition arm.h:14
A QEMU output GPIO exposed as a InitiatorSignalSocket<bool>
Definition qemu-initiator-signal-socket.h:40
void init_named(qemu::Device dev, const char *gpio_name, int gpio_idx)
Initialize this socket with a device, a GPIO namespace, and a GPIO index.
Definition qemu-initiator-signal-socket.h:156
void init(qemu::Device dev, int gpio_idx)
Initialize this socket with a device and a GPIO index.
Definition qemu-initiator-signal-socket.h:138
This class encapsulates a libqemu-cxx qemu::LibQemu instance. It handles QEMU parameters and instance...
Definition qemu-instance.h:86
A QEMU input GPIO exposed as a TargetSignalSocket<bool>
Definition qemu-target-signal-socket.h:29
void init(qemu::Device dev, int gpio_idx)
Initialize this socket with a device and a GPIO index.
Definition qemu-target-signal-socket.h:60
Definition target.h:160
Definition cpu_arm_host.h:25
Definition aarch64.h:36