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