Skip to content

Commit dc39435

Browse files
Adds HypervisorCall support.
Only tested on Armv8-R because Rust's Armv7-A targets don't have virtualization support enabled.
1 parent 32da61b commit dc39435

14 files changed

Lines changed: 432 additions & 1 deletion

File tree

aarch32-cpu/src/lib.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,193 @@ macro_rules! svc6 {
235235
retval
236236
} }
237237
}
238+
239+
/// Generate an HVC call with the given argument.
240+
#[macro_export]
241+
macro_rules! hvc {
242+
($r0:expr) => {
243+
unsafe {
244+
core::arch::asm!("hvc {arg}", arg = const $r0, out("lr") _);
245+
}
246+
}
247+
}
248+
249+
/// Generate an HVC call with 1 parameters
250+
///
251+
/// Puts the first argument in the instruction, and the parameter in r0. Gives you back
252+
/// the value left in `r0` by the handler.
253+
///
254+
/// ```rust,ignore
255+
/// const HYPERCALL_FOO: u32 = 0x100;
256+
/// let result = hvc1!(0x00, HYPERCALL_FOO);
257+
/// ```
258+
#[macro_export]
259+
macro_rules! hvc1 {
260+
($num:expr, $arg0:expr) => { {
261+
let retval: u32;
262+
let arg0: u32 = $arg0;
263+
unsafe {
264+
core::arch::asm!(
265+
// Do the Hyper-call
266+
"hvc {arg}",
267+
arg = const $num,
268+
inout("r0") arg0 => retval);
269+
}
270+
retval
271+
} }
272+
}
273+
274+
/// Generate an HVC call with 2 parameters
275+
///
276+
/// Puts the first argument in the instruction, and the parameters in r0-r1. Gives you back
277+
/// the value left in `r0` by the handler.
278+
///
279+
/// ```rust,ignore
280+
/// const HYPERCALL_FOO: u32 = 0x100;
281+
/// let result = hvc2!(0x00, HYPERCALL_FOO, 1);
282+
/// ```
283+
#[macro_export]
284+
macro_rules! hvc2 {
285+
($num:expr, $arg0:expr, $arg1:expr) => { {
286+
let retval: u32;
287+
let arg0: u32 = $arg0;
288+
let arg1: u32 = $arg1;
289+
unsafe {
290+
core::arch::asm!(
291+
// Do the Hyper-call
292+
"hvc {arg}",
293+
arg = const $num,
294+
inout("r0") arg0 => retval,
295+
in("r1") arg1);
296+
}
297+
retval
298+
} }
299+
}
300+
301+
/// Generate an HVC call with 3 parameters
302+
///
303+
/// Puts the first argument in the instruction, and the parameters in r0-r2. Gives you back
304+
/// the value left in `r0` by the handler.
305+
///
306+
/// ```rust,ignore
307+
/// const HYPERCALL_FOO: u32 = 0x100;
308+
/// let result = hvc3!(0x00, HYPERCALL_FOO, 1, 2);
309+
/// ```
310+
#[macro_export]
311+
macro_rules! hvc3 {
312+
($num:expr, $arg0:expr, $arg1:expr, $arg2:expr) => { {
313+
let retval: u32;
314+
let arg0: u32 = $arg0;
315+
let arg1: u32 = $arg1;
316+
let arg2: u32 = $arg2;
317+
unsafe {
318+
core::arch::asm!(
319+
// Do the Hyper-call
320+
"hvc {arg}",
321+
arg = const $num,
322+
inout("r0") arg0 => retval,
323+
in("r1") arg1,
324+
in("r2") arg2);
325+
}
326+
retval
327+
} }
328+
}
329+
330+
/// Generate an HVC call with 4 parameters
331+
///
332+
/// Puts the first argument in the instruction, and the parameters in r0-r3. Gives you back
333+
/// the value left in `r0` by the handler.
334+
///
335+
/// ```rust,ignore
336+
/// const HYPERCALL_FOO: u32 = 0x100;
337+
/// let result = hvc4!(0x00, HYPERCALL_FOO, 1, 2, 3);
338+
/// ```
339+
#[macro_export]
340+
macro_rules! hvc4 {
341+
($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr) => { {
342+
let retval: u32;
343+
let arg0: u32 = $arg0;
344+
let arg1: u32 = $arg1;
345+
let arg2: u32 = $arg2;
346+
let arg3: u32 = $arg3;
347+
unsafe {
348+
core::arch::asm!(
349+
// Do the Hyper-call
350+
"hvc {arg}",
351+
arg = const $num,
352+
inout("r0") arg0 => retval,
353+
in("r1") arg1,
354+
in("r2") arg2,
355+
in("r3") arg3);
356+
}
357+
retval
358+
} }
359+
}
360+
361+
/// Generate an HVC call with 5 parameters
362+
///
363+
/// Puts the first argument in the instruction, and the parameters in r0-r4. Gives you back
364+
/// the value left in `r0` by the handler.
365+
///
366+
/// ```rust,ignore
367+
/// const HYPERCALL_FOO: u32 = 0x100;
368+
/// let result = hvc5!(0x00, HYPERCALL_FOO, 1, 2, 3, 4);
369+
/// ```
370+
#[macro_export]
371+
macro_rules! hvc5 {
372+
($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr) => { {
373+
let retval: u32;
374+
let arg0: u32 = $arg0;
375+
let arg1: u32 = $arg1;
376+
let arg2: u32 = $arg2;
377+
let arg3: u32 = $arg3;
378+
let arg4: u32 = $arg4;
379+
unsafe {
380+
core::arch::asm!(
381+
// Do the Hyper-call
382+
"hvc {arg}",
383+
arg = const $num,
384+
inout("r0") arg0 => retval,
385+
in("r1") arg1,
386+
in("r2") arg2,
387+
in("r3") arg3,
388+
in("r4") arg4);
389+
}
390+
retval
391+
} }
392+
}
393+
394+
/// Generate an HVC call with 6 parameters
395+
///
396+
/// Puts the first argument in the instruction, and the parameters in r0-r5. Gives you back
397+
/// the value left in `r0` by the handler.
398+
///
399+
/// ```rust,ignore
400+
/// const HYPERCALL_FOO: u32 = 0x100;
401+
/// let result = hvc6!(0x00, HYPERCALL_FOO, 1, 2, 3, 4, 5);
402+
/// ```
403+
#[macro_export]
404+
macro_rules! hvc6 {
405+
($num:expr, $arg0:expr, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => { {
406+
let retval: u32;
407+
let arg0: u32 = $arg0;
408+
let arg1: u32 = $arg1;
409+
let arg2: u32 = $arg2;
410+
let arg3: u32 = $arg3;
411+
let arg4: u32 = $arg4;
412+
let arg5: u32 = $arg5;
413+
unsafe {
414+
core::arch::asm!(
415+
// Do the Hyper-call
416+
"hvc {arg}",
417+
arg = const $num,
418+
inout("r0") arg0 => retval,
419+
in("r1") arg1,
420+
in("r2") arg2,
421+
in("r3") arg3,
422+
in("r4") arg4,
423+
in("r5") arg5);
424+
}
425+
retval
426+
} }
427+
}

aarch32-rt-macros/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
114114
enum Exception {
115115
Undefined,
116116
SupervisorCall,
117+
HypervisorCall,
117118
PrefetchAbort,
118119
DataAbort,
119120
Irq,
@@ -124,6 +125,7 @@ impl std::fmt::Display for Exception {
124125
match self {
125126
Exception::Undefined => write!(f, "Undefined"),
126127
Exception::SupervisorCall => write!(f, "SupervisorCall"),
128+
Exception::HypervisorCall => write!(f, "HypervisorCall"),
127129
Exception::PrefetchAbort => write!(f, "PrefetchAbort"),
128130
Exception::DataAbort => write!(f, "DataAbort"),
129131
Exception::Irq => write!(f, "Irq"),
@@ -163,6 +165,7 @@ impl std::fmt::Display for Exception {
163165
///
164166
/// * Undefined (creates `_undefined_handler`)
165167
/// * SupervisorCall (creates `_svc_handler`)
168+
/// * HypervisorCall (creates `_hvc_handler`)
166169
/// * PrefetchAbort (creates `_prefetch_abort_handler`)
167170
/// * DataAbort (creates `_data_abort_handler`)
168171
/// * Irq (creates `_irq_handler`) - although people should prefer `#[irq]`.
@@ -265,6 +268,7 @@ fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> Tok
265268
Exception::Undefined
266269
}
267270
"SupervisorCall" => Exception::SupervisorCall,
271+
"HypervisorCall" => Exception::HypervisorCall,
268272
"PrefetchAbort" => {
269273
if !returns_never && f.sig.unsafety.is_none() {
270274
return parse::Error::new(
@@ -403,6 +407,21 @@ fn handle_vector(args: TokenStream, input: TokenStream, kind: VectorKind) -> Tok
403407
}
404408
)
405409
}
410+
// extern "C" fn _hvc_handler(arg: u32, args: &Frame) -> u32;
411+
Exception::HypervisorCall => {
412+
let tramp_ident = Ident::new("__aarch32_rt_hvc_handler", Span::call_site());
413+
quote!(
414+
#(#cfgs)*
415+
#(#attrs)*
416+
#[doc(hidden)]
417+
#[export_name = "_hvc_handler"]
418+
pub unsafe extern "C" fn #tramp_ident(arg: u32, frame: &aarch32_rt::Frame) -> u32 {
419+
#f
420+
421+
#func_name(arg, frame)
422+
}
423+
)
424+
}
406425
// extern "C" fn _irq_handler(addr: usize);
407426
Exception::Irq => {
408427
let tramp_ident = Ident::new("__aarch32_rt_irq_handler", Span::call_site());

aarch32-rt/link.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ PROVIDE(_pack_stacks = 0); /* set this to 1 to remove the filler section pushing
140140
PROVIDE(_start = _default_start);
141141
PROVIDE(_asm_undefined_handler = _asm_default_undefined_handler);
142142
PROVIDE(_asm_svc_handler = _asm_default_svc_handler);
143+
PROVIDE(_asm_hvc_handler = _asm_default_hvc_handler);
143144
PROVIDE(_asm_prefetch_abort_handler = _asm_default_prefetch_abort_handler);
144145
PROVIDE(_asm_data_abort_handler = _asm_default_data_abort_handler);
145146
PROVIDE(_asm_irq_handler = _asm_default_irq_handler);
@@ -148,6 +149,7 @@ PROVIDE(_asm_fiq_handler = _asm_default_fiq_handler);
148149
/* Weak aliases for C default handlers */
149150
PROVIDE(_undefined_handler = _default_handler);
150151
PROVIDE(_svc_handler = _default_handler);
152+
PROVIDE(_hvc_handler = _default_handler);
151153
PROVIDE(_prefetch_abort_handler = _default_handler);
152154
PROVIDE(_data_abort_handler = _default_handler);
153155
PROVIDE(_irq_handler = _default_handler);

aarch32-rt/src/arch_v4/hvc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Dummy hypervisor handler for architectures without HYP mode
2+
3+
#[cfg(target_arch = "arm")]
4+
core::arch::global_asm!(
5+
r#"
6+
// Work around https://github.com/rust-lang/rust/issues/127269
7+
.fpu vfp2
8+
9+
// Never called but makes the linker happy
10+
.section .text._asm_default_hvc_handler
11+
.arm
12+
.global _asm_default_hvc_handler
13+
.type _asm_default_hvc_handler, %function
14+
_asm_default_hvc_handler:
15+
b .
16+
.size _asm_default_hvc_handler, . - _asm_default_hvc_handler
17+
"#,
18+
);

aarch32-rt/src/arch_v4/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! ASM routines for Armv4 to Armv6
22
33
mod abort;
4+
mod hvc;
45
mod interrupt;
56
mod svc;
67
mod undefined;

aarch32-rt/src/arch_v7/hvc.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! HVC handler for Armv7 and higher
2+
3+
#[cfg(target_arch = "arm")]
4+
#[cfg(arm_architecture = "v8-r")]
5+
core::arch::global_asm!(
6+
r#"
7+
// Work around https://github.com/rust-lang/rust/issues/127269
8+
.fpu vfp3
9+
10+
.section .text._asm_default_hvc_handler
11+
12+
// Called from the vector table when we have an hypervisor call.
13+
// Saves state and calls a C-compatible handler like
14+
// `extern "C" fn _hvc_handler(hsr: u32, frame: &Frame) -> u32;`
15+
.global _asm_default_hvc_handler
16+
.type _asm_default_hvc_handler, %function
17+
_asm_default_hvc_handler:
18+
push {{ r12, lr }} // push state to stack
19+
push {{ r0-r5 }} // push frame to stack
20+
mov r12, sp // r12 = pointer to Frame
21+
"#,
22+
crate::save_fpu_context!(),
23+
r#"
24+
mrc p15, 4, r0, c5, c2, 0 // r0 = HSR value
25+
mov r1, r12 // r1 = frame pointer
26+
bl _hvc_handler
27+
mov r12, r0
28+
"#,
29+
crate::restore_fpu_context!(),
30+
r#"
31+
pop {{ r0-r5 }} // restore frame
32+
mov r0, r12 // replace return value
33+
pop {{ r12, lr }} // pop state from stack
34+
eret // Return from the asm handler
35+
.size _asm_default_hvc_handler, . - _asm_default_hvc_handler
36+
"#,
37+
);
38+
39+
#[cfg(target_arch = "arm")]
40+
#[cfg(not(arm_architecture = "v8-r"))]
41+
core::arch::global_asm!(
42+
r#"
43+
// Work around https://github.com/rust-lang/rust/issues/127269
44+
.fpu vfp2
45+
46+
47+
// Never called but makes the linker happy
48+
.section .text._asm_default_hvc_handler
49+
.arm
50+
.global _asm_default_hvc_handler
51+
.type _asm_default_hvc_handler, %function
52+
_asm_default_hvc_handler:
53+
b .
54+
.size _asm_default_hvc_handler, . - _asm_default_hvc_handler
55+
"#,
56+
);

aarch32-rt/src/arch_v7/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! ASM routines for for Armv7 and higher
22
33
mod abort;
4+
mod hvc;
45
mod interrupt;
56
mod svc;
67
mod undefined;

0 commit comments

Comments
 (0)