1 /* Copyright 2018 SiFive, Inc */
3 /* SPDX-License-Identifier: Apache-2.0 */
6 * @brief API for accessing CPU capabilities.
13 #include <metal/interrupt.h>
18 * @brief Function signature for exception handlers
20 typedef void (*metal_exception_handler_t) (struct metal_cpu *cpu, int ecode);
22 struct metal_cpu_vtable {
23 unsigned long long (*timer_get)(struct metal_cpu *cpu);
24 unsigned long long (*timebase_get)(struct metal_cpu *cpu);
25 unsigned long long (*mtime_get)(struct metal_cpu *cpu);
26 int (*mtimecmp_set)(struct metal_cpu *cpu, unsigned long long time);
27 struct metal_interrupt* (*tmr_controller_interrupt)(struct metal_cpu *cpu);
28 int (*get_tmr_interrupt_id)(struct metal_cpu *cpu);
29 struct metal_interrupt* (*sw_controller_interrupt)(struct metal_cpu *cpu);
30 int (*get_sw_interrupt_id)(struct metal_cpu *cpu);
31 int (*set_sw_ipi)(struct metal_cpu *cpu, int hartid);
32 int (*clear_sw_ipi)(struct metal_cpu *cpu, int hartid);
33 int (*get_msip)(struct metal_cpu *cpu, int hartid);
34 struct metal_interrupt* (*controller_interrupt)(struct metal_cpu *cpu);
35 int (*exception_register)(struct metal_cpu *cpu, int ecode, metal_exception_handler_t handler);
36 int (*get_ilen)(struct metal_cpu *cpu, uintptr_t epc);
37 uintptr_t (*get_epc)(struct metal_cpu *cpu);
38 int (*set_epc)(struct metal_cpu *cpu, uintptr_t epc);
41 /*! @brief A device handle for a CPU hart
44 const struct metal_cpu_vtable *vtable;
47 /*! @brief Get a reference to a CPU hart
49 * @param hartid The ID of the desired CPU hart
50 * @return A pointer to the CPU device handle
52 struct metal_cpu* metal_cpu_get(int hartid);
54 /*! @brief Get the hartid of the CPU hart executing this function
56 * @return The hartid of the current CPU hart */
57 int metal_cpu_get_current_hartid();
59 /*! @brief Get the number of CPU harts
61 * @return The number of CPU harts */
62 int metal_cpu_get_num_harts();
64 /*! @brief Get the CPU cycle count timer value
66 * Get the value of the cycle count timer for a given CPU
68 * @param cpu The CPU device handle
69 * @return The value of the CPU cycle count timer
71 inline unsigned long long metal_cpu_get_timer(struct metal_cpu *cpu)
72 { return cpu->vtable->timer_get(cpu); }
74 /*! @brief Get the timebase of the CPU
76 * Get the value of the timebase of the cycle count timer
78 * @param cpu The CPU device handle
79 * @return The value of the cycle count timer timebase
81 inline unsigned long long metal_cpu_get_timebase(struct metal_cpu *cpu)
82 { return cpu->vtable->timebase_get(cpu); }
84 /*! @brief Get the value of the mtime RTC
86 * Get the value of the mtime real-time clock. The CPU interrupt controller
87 * must be initialized before this function is called or the return value
90 * @param cpu The CPU device handle
91 * @return The value of mtime, or 0 if failure
93 inline unsigned long long metal_cpu_get_mtime(struct metal_cpu *cpu)
94 { return cpu->vtable->mtime_get(cpu); }
96 /*! @brief Set the value of the RTC mtimecmp RTC
98 * Set the value of the mtime real-time clock compare register. The CPU
99 * interrupt controller must be initialized before this function is called
100 * or the return value will be -1;
102 * @param cpu The CPU device handle
103 * @param time The value to set the compare register to
104 * @return The value of mtimecmp or -1 if error
106 inline int metal_cpu_set_mtimecmp(struct metal_cpu *cpu, unsigned long long time)
107 { return cpu->vtable->mtimecmp_set(cpu, time); }
109 /*! @brief Get a reference to RTC timer interrupt controller
111 * Get a reference to the interrupt controller for the real-time clock interrupt.
112 * The controller returned by this function must be initialized before any interrupts
113 * are registered or enabled with it.
115 * @param cpu The CPU device handle
116 * @return A pointer to the timer interrupt handle
118 inline struct metal_interrupt* metal_cpu_timer_interrupt_controller(struct metal_cpu *cpu)
119 { return cpu->vtable->tmr_controller_interrupt(cpu); }
121 /*! @brief Get the RTC timer interrupt id
123 * Get the interrupt ID of the real-time clock interrupt
125 * @param cpu The CPU device handle
126 * @return The timer interrupt ID
128 inline int metal_cpu_timer_get_interrupt_id(struct metal_cpu *cpu)
129 { return cpu->vtable->get_tmr_interrupt_id(cpu); }
131 /*! @brief Get a reference to the software interrupt controller
133 * Get a reference to the interrupt controller for the software/inter-process
134 * interrupt. The controller returned by this function must be initialized before
135 * any interrupts are registered or enabled with it.
137 * @param cpu The CPU device handle
138 * @return A pointer to the software interrupt handle
140 inline struct metal_interrupt* metal_cpu_software_interrupt_controller(struct metal_cpu *cpu)
141 { return cpu->vtable->sw_controller_interrupt(cpu); }
143 /*! @brief Get the software interrupt id
145 * Get the interrupt ID for the software/inter-process interrupt
147 * @param cpu The CPU device handle
148 * @return the software interrupt ID
150 inline int metal_cpu_software_get_interrupt_id(struct metal_cpu *cpu)
151 { return cpu->vtable->get_sw_interrupt_id(cpu); }
154 * @brief Set the inter-process interrupt for a hart
156 * Trigger a software/inter-process interrupt for a hart. The CPU interrupt
157 * controller for the CPU handle passed to this function must be initialized
158 * before this function is called.
160 * @param cpu The CPU device handle
161 * @param hartid The CPU hart ID to be interrupted
162 * @return 0 upon success
164 inline int metal_cpu_software_set_ipi(struct metal_cpu *cpu, int hartid)
165 { return cpu->vtable->set_sw_ipi(cpu, hartid); }
168 * @brief Clear the inter-process interrupt for a hart
170 * Clear the software/inter-process interrupt for a hart. The CPU interrupt
171 * controller for the CPU handle passed to this function must be initialized
172 * before this function is called.
174 * @param cpu The CPU device handle
175 * @param hartid The CPU hart ID to clear
176 * @return 0 upon success
178 inline int metal_cpu_software_clear_ipi(struct metal_cpu *cpu, int hartid)
179 { return cpu->vtable->clear_sw_ipi(cpu, hartid); }
182 * @brief Get the value of MSIP for the given hart
184 * Get the value of the machine software interrupt pending bit for
185 * the given hart. The CPU interrupt controller for the CPU handle passed
186 * as argument to this function must be initialized before this function
189 * @param cpu the CPU device handle
190 * @param hartid The CPU hart to read
191 * @return 0 upon success
193 inline int metal_cpu_get_msip(struct metal_cpu *cpu, int hartid)
194 { return cpu->vtable->get_msip(cpu, hartid); }
197 * @brief Get the interrupt controller for the CPU
199 * Get the CPU interrupt controller. The controller returned by this
200 * function must be initialized before any interrupts are registered
201 * or enabled and before any exception handlers are registered with
204 * @param cpu The CPU device handle
205 * @return The handle for the CPU interrupt controller
207 inline struct metal_interrupt* metal_cpu_interrupt_controller(struct metal_cpu *cpu)
208 { return cpu->vtable->controller_interrupt(cpu); }
211 * @brief Register an exception handler
213 * Register an exception handler for the CPU. The CPU interrupt controller must be initialized
214 * before this function is called.
216 * @param cpu The CPU device handle
217 * @param ecode The exception code to register a handler for
218 * @param handler Callback function for the exception handler
219 * @return 0 upon success
221 inline int metal_cpu_exception_register(struct metal_cpu *cpu, int ecode, metal_exception_handler_t handler)
222 { return cpu->vtable->exception_register(cpu, ecode, handler); }
225 * @brief Get the length of an instruction in bytes
227 * Get the length of an instruction in bytes.
229 * On RISC-V platforms, this is useful for detecting whether an instruction is
230 * compressed (2 bytes long) or uncompressed (4 bytes long).
232 * This function is useful in conjuction with `metal_cpu_get_exception_pc()`
233 * and `metal_cpu_set_exception_pc()` in order to cause the exception handler to
234 * return execution after the faulting instruction.
236 * @param cpu The CPU device handle
237 * @param epc The address of the instruction to measure
238 * @return the length of the instruction in bytes
240 inline int metal_cpu_get_instruction_length(struct metal_cpu *cpu, uintptr_t epc)
241 { return cpu->vtable->get_ilen(cpu, epc); }
244 * @brief Get the program counter of the current exception.
246 * This function must be called within an exception handler. The behavior is
247 * undefined outside of an exception handler.
249 * @param cpu The CPU device handle
250 * @return The value of the program counter at the time of the exception
252 inline uintptr_t metal_cpu_get_exception_pc(struct metal_cpu *cpu)
253 { return cpu->vtable->get_epc(cpu); }
256 * @brief Set the exception program counter
258 * This function must be called within an exception handler. The behavior
259 * is undefined outside of an exception handler.
261 * This function can be used to cause an exception handler to return execution
262 * to an address other than the one that caused the exception.
264 * @param cpu the CPU device handle
265 * @param epc The address to set the exception program counter to
266 * @return 0 upon success
268 inline int metal_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t epc)
269 { return cpu->vtable->set_epc(cpu, epc); }