Skip to main content

ax_cpu/loongarch64/
unaligned.rs

1// Modified from https://github.com/LoongsonLab/StarryOS-LoongArch/blob/main/modules/axhal/src/arch/loongarch64/unaligned.rs
2
3use core::{arch::asm, fmt};
4
5use loongArch64::register::badv;
6
7use crate::{GeneralRegisters, TrapFrame};
8
9core::arch::global_asm!(include_asm_macros!(), include_str!("unaligned.S"));
10
11unsafe extern "C" {
12    fn _unaligned_read(addr: u64, value: &mut u64, n: u64, symbol: bool) -> i32;
13    fn _unaligned_write(addr: u64, value: u64, n: u64) -> i32;
14}
15
16/// Error type for unaligned access operations.
17#[derive(Copy, Eq, PartialEq, Clone, Debug)]
18pub struct UnalignedError {
19    addr: u64,
20    n: Option<u64>,
21}
22
23impl fmt::Display for UnalignedError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        if let Some(n) = self.n {
26            write!(f, "unaligned access at {:#x} (n={})", self.addr, n)
27        } else {
28            write!(f, "unaligned access at {:#x} (unknown op)", self.addr)
29        }
30    }
31}
32
33impl core::error::Error for UnalignedError {}
34
35fn unaligned_read(addr: u64, value: &mut u64, n: u64, symbol: bool) -> Result<(), UnalignedError> {
36    if unsafe { _unaligned_read(addr, value, n, symbol) } == -1 {
37        return Err(UnalignedError { addr, n: Some(n) });
38    }
39    Ok(())
40}
41
42fn unaligned_write(addr: u64, value: u64, n: u64) -> Result<(), UnalignedError> {
43    if unsafe { _unaligned_write(addr, value, n) } == -1 {
44        return Err(UnalignedError { addr, n: Some(n) });
45    }
46    Ok(())
47}
48
49#[inline]
50fn asm_write_fpr_0(val: u64) {
51    unsafe { asm!("movgr2fr.d $f0,  {val} ", val = in(reg) val) }
52}
53
54#[inline]
55fn asm_write_fpr_1(val: u64) {
56    unsafe { asm!("movgr2fr.d $f1,  {val} ", val = in(reg) val) }
57}
58
59#[inline]
60fn asm_write_fpr_2(val: u64) {
61    unsafe { asm!("movgr2fr.d $f2,  {val} ", val = in(reg) val) }
62}
63
64#[inline]
65fn asm_write_fpr_3(val: u64) {
66    unsafe { asm!("movgr2fr.d $f3,  {val} ", val = in(reg) val) }
67}
68
69#[inline]
70fn asm_write_fpr_4(val: u64) {
71    unsafe { asm!("movgr2fr.d $f4,  {val} ", val = in(reg) val) }
72}
73
74#[inline]
75fn asm_write_fpr_5(val: u64) {
76    unsafe { asm!("movgr2fr.d $f5,  {val} ", val = in(reg) val) }
77}
78
79#[inline]
80fn asm_write_fpr_6(val: u64) {
81    unsafe { asm!("movgr2fr.d $f6,  {val} ", val = in(reg) val) }
82}
83
84#[inline]
85fn asm_write_fpr_7(val: u64) {
86    unsafe { asm!("movgr2fr.d $f7,  {val} ", val = in(reg) val) }
87}
88
89#[inline]
90fn asm_write_fpr_8(val: u64) {
91    unsafe { asm!("movgr2fr.d $f8,  {val} ", val = in(reg) val) }
92}
93
94#[inline]
95fn asm_write_fpr_9(val: u64) {
96    unsafe { asm!("movgr2fr.d $f9,  {val} ", val = in(reg) val) }
97}
98
99#[inline]
100fn asm_write_fpr_10(val: u64) {
101    unsafe { asm!("movgr2fr.d $f10,  {val} ", val = in(reg) val) }
102}
103
104#[inline]
105fn asm_write_fpr_11(val: u64) {
106    unsafe { asm!("movgr2fr.d $f11,  {val} ", val = in(reg) val) }
107}
108
109#[inline]
110fn asm_write_fpr_12(val: u64) {
111    unsafe { asm!("movgr2fr.d $f12,  {val} ", val = in(reg) val) }
112}
113
114#[inline]
115fn asm_write_fpr_13(val: u64) {
116    unsafe { asm!("movgr2fr.d $f13,  {val} ", val = in(reg) val) }
117}
118
119#[inline]
120fn asm_write_fpr_14(val: u64) {
121    unsafe { asm!("movgr2fr.d $f14,  {val} ", val = in(reg) val) }
122}
123
124#[inline]
125fn asm_write_fpr_15(val: u64) {
126    unsafe { asm!("movgr2fr.d $f15,  {val} ", val = in(reg) val) }
127}
128
129#[inline]
130fn asm_write_fpr_16(val: u64) {
131    unsafe { asm!("movgr2fr.d $f16,  {val} ", val = in(reg) val) }
132}
133
134#[inline]
135fn asm_write_fpr_17(val: u64) {
136    unsafe { asm!("movgr2fr.d $f17,  {val} ", val = in(reg) val) }
137}
138
139#[inline]
140fn asm_write_fpr_18(val: u64) {
141    unsafe { asm!("movgr2fr.d $f18,  {val} ", val = in(reg) val) }
142}
143
144#[inline]
145fn asm_write_fpr_19(val: u64) {
146    unsafe { asm!("movgr2fr.d $f19,  {val} ", val = in(reg) val) }
147}
148
149#[inline]
150fn asm_write_fpr_20(val: u64) {
151    unsafe { asm!("movgr2fr.d $f20,  {val} ", val = in(reg) val) }
152}
153
154#[inline]
155fn asm_write_fpr_21(val: u64) {
156    unsafe { asm!("movgr2fr.d $f21,  {val} ", val = in(reg) val) }
157}
158
159#[inline]
160fn asm_write_fpr_22(val: u64) {
161    unsafe { asm!("movgr2fr.d $f22,  {val} ", val = in(reg) val) }
162}
163
164#[inline]
165fn asm_write_fpr_23(val: u64) {
166    unsafe { asm!("movgr2fr.d $f23,  {val} ", val = in(reg) val) }
167}
168
169#[inline]
170fn asm_write_fpr_24(val: u64) {
171    unsafe { asm!("movgr2fr.d $f24,  {val} ", val = in(reg) val) }
172}
173
174#[inline]
175fn asm_write_fpr_25(val: u64) {
176    unsafe { asm!("movgr2fr.d $f25,  {val} ", val = in(reg) val) }
177}
178
179#[inline]
180fn asm_write_fpr_26(val: u64) {
181    unsafe { asm!("movgr2fr.d $f26,  {val} ", val = in(reg) val) }
182}
183
184#[inline]
185fn asm_write_fpr_27(val: u64) {
186    unsafe { asm!("movgr2fr.d $f27,  {val} ", val = in(reg) val) }
187}
188
189#[inline]
190fn asm_write_fpr_28(val: u64) {
191    unsafe { asm!("movgr2fr.d $f28,  {val} ", val = in(reg) val) }
192}
193
194#[inline]
195fn asm_write_fpr_29(val: u64) {
196    unsafe { asm!("movgr2fr.d $f29,  {val} ", val = in(reg) val) }
197}
198
199#[inline]
200fn asm_write_fpr_30(val: u64) {
201    unsafe { asm!("movgr2fr.d $f30,  {val} ", val = in(reg) val) }
202}
203
204#[inline]
205fn asm_write_fpr_31(val: u64) {
206    unsafe { asm!("movgr2fr.d $f31,  {val} ", val = in(reg) val) }
207}
208
209#[inline]
210fn asm_read_fpr_0() -> u64 {
211    let mut value: u64;
212    unsafe { asm!( "movfr2gr.d {val}, $f0", val = out(reg) value) }
213    value
214}
215
216#[inline]
217fn asm_read_fpr_1() -> u64 {
218    let mut value: u64;
219    unsafe { asm!( "movfr2gr.d {val}, $f1", val = out(reg) value) }
220    value
221}
222
223#[inline]
224fn asm_read_fpr_2() -> u64 {
225    let mut value: u64;
226    unsafe { asm!( "movfr2gr.d {val}, $f2", val = out(reg) value) }
227    value
228}
229
230#[inline]
231fn asm_read_fpr_3() -> u64 {
232    let mut value: u64;
233    unsafe { asm!( "movfr2gr.d {val}, $f3", val = out(reg) value) }
234    value
235}
236
237#[inline]
238fn asm_read_fpr_4() -> u64 {
239    let mut value: u64;
240    unsafe { asm!( "movfr2gr.d {val}, $f4", val = out(reg) value) }
241    value
242}
243
244#[inline]
245fn asm_read_fpr_5() -> u64 {
246    let mut value: u64;
247    unsafe { asm!( "movfr2gr.d {val}, $f5", val = out(reg) value) }
248    value
249}
250
251#[inline]
252fn asm_read_fpr_6() -> u64 {
253    let mut value: u64;
254    unsafe { asm!( "movfr2gr.d {val}, $f6", val = out(reg) value) }
255    value
256}
257
258#[inline]
259fn asm_read_fpr_7() -> u64 {
260    let mut value: u64;
261    unsafe { asm!( "movfr2gr.d {val}, $f7", val = out(reg) value) }
262    value
263}
264
265#[inline]
266fn asm_read_fpr_8() -> u64 {
267    let mut value: u64;
268    unsafe { asm!( "movfr2gr.d {val}, $f8", val = out(reg) value) }
269    value
270}
271
272#[inline]
273fn asm_read_fpr_9() -> u64 {
274    let mut value: u64;
275    unsafe { asm!( "movfr2gr.d {val}, $f9", val = out(reg) value) }
276    value
277}
278
279#[inline]
280fn asm_read_fpr_10() -> u64 {
281    let mut value: u64;
282    unsafe { asm!( "movfr2gr.d {val}, $f10", val = out(reg) value) }
283    value
284}
285
286#[inline]
287fn asm_read_fpr_11() -> u64 {
288    let mut value: u64;
289    unsafe { asm!( "movfr2gr.d {val}, $f11", val = out(reg) value) }
290    value
291}
292
293#[inline]
294fn asm_read_fpr_12() -> u64 {
295    let mut value: u64;
296    unsafe { asm!( "movfr2gr.d {val}, $f12", val = out(reg) value) }
297    value
298}
299
300#[inline]
301fn asm_read_fpr_13() -> u64 {
302    let mut value: u64;
303    unsafe { asm!( "movfr2gr.d {val}, $f13", val = out(reg) value) }
304    value
305}
306
307#[inline]
308fn asm_read_fpr_14() -> u64 {
309    let mut value: u64;
310    unsafe { asm!( "movfr2gr.d {val}, $f14", val = out(reg) value) }
311    value
312}
313
314#[inline]
315fn asm_read_fpr_15() -> u64 {
316    let mut value: u64;
317    unsafe { asm!( "movfr2gr.d {val}, $f15", val = out(reg) value) }
318    value
319}
320
321#[inline]
322fn asm_read_fpr_16() -> u64 {
323    let mut value: u64;
324    unsafe { asm!( "movfr2gr.d {val}, $f16", val = out(reg) value) }
325    value
326}
327
328#[inline]
329fn asm_read_fpr_17() -> u64 {
330    let mut value: u64;
331    unsafe { asm!( "movfr2gr.d {val}, $f17", val = out(reg) value) }
332    value
333}
334
335#[inline]
336fn asm_read_fpr_18() -> u64 {
337    let mut value: u64;
338    unsafe { asm!( "movfr2gr.d {val}, $f18", val = out(reg) value) }
339    value
340}
341
342#[inline]
343fn asm_read_fpr_19() -> u64 {
344    let mut value: u64;
345    unsafe { asm!( "movfr2gr.d {val}, $f19", val = out(reg) value) }
346    value
347}
348
349#[inline]
350fn asm_read_fpr_20() -> u64 {
351    let mut value: u64;
352    unsafe { asm!( "movfr2gr.d {val}, $f20", val = out(reg) value) }
353    value
354}
355
356#[inline]
357fn asm_read_fpr_21() -> u64 {
358    let mut value: u64;
359    unsafe { asm!( "movfr2gr.d {val}, $f21", val = out(reg) value) }
360    value
361}
362
363#[inline]
364fn asm_read_fpr_22() -> u64 {
365    let mut value: u64;
366    unsafe { asm!( "movfr2gr.d {val}, $f22", val = out(reg) value) }
367    value
368}
369
370#[inline]
371fn asm_read_fpr_23() -> u64 {
372    let mut value: u64;
373    unsafe { asm!( "movfr2gr.d {val}, $f23", val = out(reg) value) }
374    value
375}
376
377#[inline]
378fn asm_read_fpr_24() -> u64 {
379    let mut value: u64;
380    unsafe { asm!( "movfr2gr.d {val}, $f24", val = out(reg) value) }
381    value
382}
383
384#[inline]
385fn asm_read_fpr_25() -> u64 {
386    let mut value: u64;
387    unsafe { asm!( "movfr2gr.d {val}, $f25", val = out(reg) value) }
388    value
389}
390
391#[inline]
392fn asm_read_fpr_26() -> u64 {
393    let mut value: u64;
394    unsafe { asm!( "movfr2gr.d {val}, $f26", val = out(reg) value) }
395    value
396}
397
398#[inline]
399fn asm_read_fpr_27() -> u64 {
400    let mut value: u64;
401    unsafe { asm!( "movfr2gr.d {val}, $f27", val = out(reg) value) }
402    value
403}
404
405#[inline]
406fn asm_read_fpr_28() -> u64 {
407    let mut value: u64;
408    unsafe { asm!( "movfr2gr.d {val}, $f28", val = out(reg) value) }
409    value
410}
411
412#[inline]
413fn asm_read_fpr_29() -> u64 {
414    let mut value: u64;
415    unsafe { asm!( "movfr2gr.d {val}, $f29", val = out(reg) value) }
416    value
417}
418
419#[inline]
420fn asm_read_fpr_30() -> u64 {
421    let mut value: u64;
422    unsafe { asm!( "movfr2gr.d {val}, $f30", val = out(reg) value) }
423    value
424}
425
426#[inline]
427fn asm_read_fpr_31() -> u64 {
428    let mut value: u64;
429    unsafe { asm!( "movfr2gr.d {val}, $f31", val = out(reg) value) }
430    value
431}
432
433pub fn write_fpr(fd: usize, val: u64) {
434    match fd {
435        0 => asm_write_fpr_0(val),
436        1 => asm_write_fpr_1(val),
437        2 => asm_write_fpr_2(val),
438        3 => asm_write_fpr_3(val),
439        4 => asm_write_fpr_4(val),
440        5 => asm_write_fpr_5(val),
441        6 => asm_write_fpr_6(val),
442        7 => asm_write_fpr_7(val),
443        8 => asm_write_fpr_8(val),
444        9 => asm_write_fpr_9(val),
445        10 => asm_write_fpr_10(val),
446        11 => asm_write_fpr_11(val),
447        12 => asm_write_fpr_12(val),
448        13 => asm_write_fpr_13(val),
449        14 => asm_write_fpr_14(val),
450        15 => asm_write_fpr_15(val),
451        16 => asm_write_fpr_16(val),
452        17 => asm_write_fpr_17(val),
453        18 => asm_write_fpr_18(val),
454        19 => asm_write_fpr_19(val),
455        20 => asm_write_fpr_20(val),
456        21 => asm_write_fpr_21(val),
457        22 => asm_write_fpr_22(val),
458        23 => asm_write_fpr_23(val),
459        24 => asm_write_fpr_24(val),
460        25 => asm_write_fpr_25(val),
461        26 => asm_write_fpr_26(val),
462        27 => asm_write_fpr_27(val),
463        28 => asm_write_fpr_28(val),
464        29 => asm_write_fpr_29(val),
465        30 => asm_write_fpr_30(val),
466        31 => asm_write_fpr_31(val),
467        _ => {
468            panic!("Undefined Float Register")
469        }
470    }
471}
472
473pub fn read_fpr(fd: usize) -> u64 {
474    let value: u64;
475    match fd {
476        0 => value = asm_read_fpr_0(),
477        1 => value = asm_read_fpr_1(),
478        2 => value = asm_read_fpr_2(),
479        3 => value = asm_read_fpr_3(),
480        4 => value = asm_read_fpr_4(),
481        5 => value = asm_read_fpr_5(),
482        6 => value = asm_read_fpr_6(),
483        7 => value = asm_read_fpr_7(),
484        8 => value = asm_read_fpr_8(),
485        9 => value = asm_read_fpr_9(),
486        10 => value = asm_read_fpr_10(),
487        11 => value = asm_read_fpr_11(),
488        12 => value = asm_read_fpr_12(),
489        13 => value = asm_read_fpr_13(),
490        14 => value = asm_read_fpr_14(),
491        15 => value = asm_read_fpr_15(),
492        16 => value = asm_read_fpr_16(),
493        17 => value = asm_read_fpr_17(),
494        18 => value = asm_read_fpr_18(),
495        19 => value = asm_read_fpr_19(),
496        20 => value = asm_read_fpr_20(),
497        21 => value = asm_read_fpr_21(),
498        22 => value = asm_read_fpr_22(),
499        23 => value = asm_read_fpr_23(),
500        24 => value = asm_read_fpr_24(),
501        25 => value = asm_read_fpr_25(),
502        26 => value = asm_read_fpr_26(),
503        27 => value = asm_read_fpr_27(),
504        28 => value = asm_read_fpr_28(),
505        29 => value = asm_read_fpr_29(),
506        30 => value = asm_read_fpr_30(),
507        31 => value = asm_read_fpr_31(),
508        _ => {
509            panic!("Undefined Float Register")
510        }
511    }
512    value
513}
514
515const LDH_OP: u32 = 0xa1;
516const LDHU_OP: u32 = 0xa9;
517const LDW_OP: u32 = 0xa2;
518const LDWU_OP: u32 = 0xaa;
519const LDD_OP: u32 = 0xa3;
520const STH_OP: u32 = 0xa5;
521const STW_OP: u32 = 0xa6;
522const STD_OP: u32 = 0xa7;
523
524const LDPTRW_OP: u32 = 0x24;
525const LDPTRD_OP: u32 = 0x26;
526const STPTRW_OP: u32 = 0x25;
527const STPTRD_OP: u32 = 0x27;
528
529const LDXH_OP: u32 = 0x7048;
530const LDXHU_OP: u32 = 0x7008;
531const LDXW_OP: u32 = 0x7010;
532const LDXWU_OP: u32 = 0x7050;
533const LDXD_OP: u32 = 0x7018;
534const STXH_OP: u32 = 0x7028;
535const STXW_OP: u32 = 0x7030;
536const STXD_OP: u32 = 0x7038;
537
538const FLDS_OP: u32 = 0xac;
539const FLDD_OP: u32 = 0xae;
540const FSTS_OP: u32 = 0xad;
541const FSTD_OP: u32 = 0xaf;
542
543const FSTXS_OP: u32 = 0x7070;
544const FSTXD_OP: u32 = 0x7078;
545const FLDXS_OP: u32 = 0x7060;
546const FLDXD_OP: u32 = 0x7068;
547
548impl TrapFrame {
549    /// Emulates an unaligned memory access triggered by a trap.
550    ///
551    /// # Safety
552    /// This function uses raw pointers and inline assembly to handle unaligned memory accesses,
553    /// so it must only be called in a valid trap context with a properly initialized TrapFrame.
554    pub unsafe fn emulate_unaligned(&mut self) -> Result<(), UnalignedError> {
555        let mut value: u64 = 0;
556
557        let badv = badv::read().vaddr() as u64;
558        let badi = unsafe { core::ptr::read(self.era as *const u32) };
559        let rd = (badi & 0x1f) as usize;
560
561        let regs = unsafe {
562            core::mem::transmute::<&mut GeneralRegisters, &mut [usize; 32]>(&mut self.regs)
563        };
564
565        if (badi >> 22) == LDD_OP || (badi >> 24) == LDPTRD_OP || (badi >> 15) == LDXD_OP {
566            unaligned_read(badv, &mut value, 8, true)?;
567            regs[rd] = value as usize;
568        } else if (badi >> 22) == LDW_OP || (badi >> 24) == LDPTRW_OP || (badi >> 15) == LDXW_OP {
569            unaligned_read(badv, &mut value, 4, true)?;
570            regs[rd] = value as usize;
571        } else if (badi >> 22) == LDWU_OP || (badi >> 15) == LDXWU_OP {
572            unaligned_read(badv, &mut value, 4, false)?;
573            regs[rd] = value as usize;
574        } else if (badi >> 22) == LDH_OP || (badi >> 15) == LDXH_OP {
575            unaligned_read(badv, &mut value, 2, true)?;
576            regs[rd] = value as usize;
577        } else if (badi >> 22) == LDHU_OP || (badi >> 15) == LDXHU_OP {
578            unaligned_read(badv, &mut value, 2, false)?;
579            regs[rd] = value as usize;
580        } else if (badi >> 22) == STD_OP || (badi >> 24) == STPTRD_OP || (badi >> 15) == STXD_OP {
581            value = regs[rd] as u64;
582            unaligned_write(badv, value, 8)?;
583        } else if (badi >> 22) == STW_OP || (badi >> 24) == STPTRW_OP || (badi >> 15) == STXW_OP {
584            value = regs[rd] as u64;
585            unaligned_write(badv, value, 4)?;
586        } else if (badi >> 22) == STH_OP || (badi >> 15) == STXH_OP {
587            value = regs[rd] as u64;
588            unaligned_write(badv, value, 2)?;
589        } else if (badi >> 22) == FLDD_OP || (badi >> 15) == FLDXD_OP {
590            unaligned_read(badv, &mut value, 8, true)?;
591            write_fpr(rd, value);
592        } else if (badi >> 22) == FLDS_OP || (badi >> 15) == FLDXS_OP {
593            unaligned_read(badv, &mut value, 4, true)?;
594            write_fpr(rd, value);
595        } else if (badi >> 22) == FSTD_OP || (badi >> 15) == FSTXD_OP {
596            value = read_fpr(rd);
597            unaligned_write(badv, value, 8)?;
598        } else if (badi >> 22) == FSTS_OP || (badi >> 15) == FSTXS_OP {
599            value = read_fpr(rd);
600            unaligned_write(badv, value, 4)?;
601        } else {
602            return Err(UnalignedError {
603                addr: badv,
604                n: None,
605            });
606        }
607
608        self.era += 4;
609
610        Ok(())
611    }
612}