kernel/arch/riscv64/switch.rs
1//! RISC-V kernel context switching implementation
2//!
3//! This module provides low-level context switching functionality for RISC-V,
4//! enabling kernel tasks to yield execution and resume later at the same point.
5
6use crate::arch::KernelContext;
7use core::arch::naked_asm;
8
9/// Switch from the current kernel context to the next kernel context
10///
11/// This function performs a complete kernel context switch:
12/// 1. Saves callee-saved registers (sp, ra, s0-s11) to prev_ctx
13/// 2. Restores callee-saved registers from next_ctx
14/// 3. Returns to the point where next_ctx was previously switched out
15///
16/// # Arguments
17/// * `prev_ctx` - Mutable reference to store the current context
18/// * `next_ctx` - Reference to the context to switch to
19///
20/// # Safety
21/// This function must only be called from kernel code with valid contexts.
22/// The stack pointers in both contexts must point to valid, allocated stacks.
23///
24/// # Returns
25/// This function returns twice:
26/// - Once immediately (when switching away from this context)
27/// - Once when this context is resumed later
28#[unsafe(naked)]
29pub unsafe extern "C" fn switch_to(prev_ctx: *mut KernelContext, next_ctx: *const KernelContext) {
30 naked_asm!(
31 // Save current context (prev_ctx)
32 // a0 = prev_ctx, a1 = next_ctx
33
34 // Save stack pointer
35 "sd sp, 0(a0)",
36 // Save return address
37 "sd ra, 8(a0)",
38 // Save callee-saved registers s0-s11
39 "sd s0, 16(a0)",
40 "sd s1, 24(a0)",
41 "sd s2, 32(a0)",
42 "sd s3, 40(a0)",
43 "sd s4, 48(a0)",
44 "sd s5, 56(a0)",
45 "sd s6, 64(a0)",
46 "sd s7, 72(a0)",
47 "sd s8, 80(a0)",
48 "sd s9, 88(a0)",
49 "sd s10, 96(a0)",
50 "sd s11, 104(a0)",
51 // Restore next context (next_ctx)
52 // Load stack pointer
53 "ld sp, 0(a1)",
54 // Load return address
55 "ld ra, 8(a1)",
56 // Load callee-saved registers s0-s11
57 "ld s0, 16(a1)",
58 "ld s1, 24(a1)",
59 "ld s2, 32(a1)",
60 "ld s3, 40(a1)",
61 "ld s4, 48(a1)",
62 "ld s5, 56(a1)",
63 "ld s6, 64(a1)",
64 "ld s7, 72(a1)",
65 "ld s8, 80(a1)",
66 "ld s9, 88(a1)",
67 "ld s10, 96(a1)",
68 "ld s11, 104(a1)",
69 // Return to the saved return address
70 // This will either:
71 // - Return to the original caller (first time)
72 // - Resume where this context was previously switched out
73 "ret",
74 );
75}
76
77/// Initialize a kernel context for first-time execution
78///
79/// This function sets up a kernel context to start executing at the specified
80/// entry point when first switched to.
81///
82/// # Arguments
83/// * `ctx` - Mutable reference to the context to initialize
84/// * `entry_point` - Function pointer to start executing
85/// * `stack_top` - Top of the stack for this context
86pub fn init_kernel_context(ctx: &mut KernelContext, entry_point: fn(), stack_top: u64) {
87 // Set up initial state for first-time execution
88 ctx.sp = stack_top;
89 ctx.ra = entry_point as u64;
90
91 // Clear all saved registers
92 ctx.s = [0; 12];
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::environment::TASK_KERNEL_STACK_SIZE;
99 use alloc::boxed::Box;
100
101 /// Test kernel context initialization
102 #[test_case]
103 fn test_init_kernel_context() {
104 let mut ctx = KernelContext::new();
105 let stack = Box::new([0u8; TASK_KERNEL_STACK_SIZE]);
106 let stack_top = stack.as_ptr() as u64 + TASK_KERNEL_STACK_SIZE as u64;
107
108 fn test_entry() {
109 // Test entry point
110 }
111
112 init_kernel_context(&mut ctx, test_entry, stack_top);
113
114 assert_eq!(ctx.sp, stack_top);
115 assert_eq!(ctx.ra, test_entry as u64);
116 assert_eq!(ctx.s, [0; 12]);
117 }
118}