Skip to main content

strat9_kernel/framebuffer/x86/
mod.rs

1pub mod avx2;
2pub mod avx512;
3pub mod sse2;
4
5use crate::framebuffer::{generic, FramebufferOps};
6use raw_cpuid::CpuId;
7
8#[inline]
9fn kernel_can_use_extended_simd() -> bool {
10    #[cfg(target_os = "none")]
11    {
12        if !crate::arch::x86_64::cpuid::host_uses_xsave() {
13            return false;
14        }
15        // Defensive check: verify AVX is actually enabled in XCR0.
16        // If init_cpu_extensions() ever changes which bits it sets,
17        // this prevents selecting AVX2/AVX512 routines that would #UD.
18        let xcr0 = crate::arch::x86_64::xgetbv(0);
19        (xcr0 & 4) != 0 // XCR0_AVX bit
20    }
21
22    #[cfg(not(target_os = "none"))]
23    {
24        true
25    }
26}
27
28extern "C" {
29    // NASM: cglobal + INIT_YMM avx2 + private_prefix strat9_fb
30    //   => symbol strat9_fb_framebuffer_fill_avx2 / _blit_avx2
31    pub fn strat9_fb_framebuffer_fill_avx2(dst: *mut u32, color: u32, count: usize);
32    pub fn strat9_fb_framebuffer_blit_avx2(dst: *mut u32, src: *const u32, count: usize);
33}
34
35/// Wrapper: cast extern "C" NASM fill fn to Rust ABI fn pointer
36unsafe fn fill_avx2_asm(dst: *mut u32, color: u32, count: usize) {
37    strat9_fb_framebuffer_fill_avx2(dst, color, count);
38}
39
40/// Wrapper: cast extern "C" NASM blit fn to Rust ABI fn pointer
41unsafe fn blit_avx2_asm(dst: *mut u32, src: *const u32, count: usize) {
42    strat9_fb_framebuffer_blit_avx2(dst, src, count);
43}
44
45pub fn detect_and_init_ops() -> FramebufferOps {
46    let cpuid = CpuId::new();
47
48    let has_avx512f = kernel_can_use_extended_simd()
49        && cpuid
50            .get_extended_feature_info()
51            .map_or(false, |info| info.has_avx512f());
52
53    let has_avx2 = kernel_can_use_extended_simd()
54        && cpuid
55            .get_extended_feature_info()
56            .map_or(false, |info| info.has_avx2());
57
58    let has_sse41 = cpuid
59        .get_feature_info()
60        .map_or(false, |info| info.has_sse41());
61
62    let has_ssse3 = cpuid
63        .get_feature_info()
64        .map_or(false, |info| info.has_ssse3());
65
66    let has_sse2 = cpuid
67        .get_feature_info()
68        .map_or(false, |info| info.has_sse2());
69
70    if has_avx512f {
71        FramebufferOps {
72            fill: avx512::fill_avx512,
73            blit: avx512::blit_avx512,
74            blend: avx512::blend_avx512,
75            convert: avx512::convert_bgr_to_argb_avx512,
76        }
77    } else if has_avx2 {
78        FramebufferOps {
79            fill: fill_avx2_asm, // NASM ASM implementation
80            blit: blit_avx2_asm, // NASM ASM implementation
81            blend: avx2::blend_avx2,
82            convert: avx2::convert_bgr_to_argb_avx2,
83        }
84    } else if has_sse41 || has_sse2 {
85        // convert_bgr_to_argb requires SSSE3 (pshufb). Without it, use the scalar path.
86        let convert: crate::framebuffer::FnConvert = if has_ssse3 {
87            sse2::convert_bgr_to_argb_sse2
88        } else {
89            generic::convert_bgr_to_argb_generic
90        };
91        FramebufferOps {
92            fill: sse2::fill_sse2,
93            blit: sse2::blit_sse2,
94            blend: sse2::blend_sse2,
95            convert,
96        }
97    } else {
98        FramebufferOps {
99            fill: generic::fill_generic,
100            blit: generic::blit_generic,
101            blend: generic::blend_generic,
102            convert: generic::convert_bgr_to_argb_generic,
103        }
104    }
105}