Skip to main content

kernel_api/ptr/
x86_64.rs

1use core::arch::asm;
2use core::hint::unreachable_unchecked;
3use core::mem::MaybeUninit;
4
5macro_rules! gen_checked {
6    (@read $name:ident $ty:ty => $reg_constraint:tt $reg_modifier:tt) => {
7		#[inline]
8		pub fn $name (ptr: *const MaybeUninit<$ty>) -> Option<MaybeUninit<$ty>> {
9			let r: MaybeUninit<_>;
10			let success: usize;
11			unsafe {
12				if crate::detect::__detected::smap() {
13					asm!(
14						"stac",
15						concat!("2: mov {", $reg_modifier, "}, [{}]"),
16						"   mov {}, 1",
17						".pushsection .popcorn.deref_handlers.check",
18						".quad 2b",
19						".popsection",
20						".pushsection .popcorn.deref_handlers.handle",
21						".quad 3f",
22						".popsection",
23						"3: ",
24						"clac",
25						inout($reg_constraint) MaybeUninit::<$ty>::uninit() => r, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
26						in(reg) ptr,
27						inout(reg) 0usize => success,
28						options(nostack, preserves_flags, readonly)
29					);	
30				} else {
31					asm!(
32						concat!("2: mov {", $reg_modifier, "}, [{}]"),
33						"   mov {}, 1",
34						".pushsection .popcorn.deref_handlers.check",
35						".quad 2b",
36						".popsection",
37						".pushsection .popcorn.deref_handlers.handle",
38						".quad 3f",
39						".popsection",
40						"3: ",
41						inout($reg_constraint) MaybeUninit::<$ty>::uninit() => r, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
42						in(reg) ptr,
43						inout(reg) 0usize => success,
44						options(nostack, preserves_flags, readonly)
45					);
46				}
47			}
48		
49			match success {
50				0 => None,
51				1 => Some(r),
52				_ => unsafe { unreachable_unchecked() }
53			}
54		}
55    };
56	(@write $name:ident $ty:ty => $reg_constraint:tt $reg_modifier:tt) => {
57		#[inline]
58		pub fn $name(ptr: *mut MaybeUninit<$ty>, val: MaybeUninit<$ty>) -> Option<()> {
59			let success: usize;
60			unsafe {
61				if crate::detect::__detected::smap() {
62					asm!(
63						"stac",
64						".pushsection .popcorn.deref_handlers.check",
65						".quad 2f",
66						".popsection",
67						"2:",
68						concat!("mov [{}], {", $reg_modifier, "}"),
69						"mov {}, 1",
70						".pushsection .popcorn.deref_handlers.handle",
71						".quad 3f",
72						".popsection",
73						"3:",
74						"clac",
75						in(reg) ptr,
76						in($reg_constraint) val,
77						inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
78						options(nostack, preserves_flags, readonly)
79					);
80				} else {
81					asm!(
82						".pushsection .popcorn.deref_handlers.check",
83						".quad 2f",
84						".popsection",
85						"2:",
86						concat!("mov [{}], {", $reg_modifier, "}"),
87						"mov {}, 1",
88						".pushsection .popcorn.deref_handlers.handle",
89						".quad 3f",
90						".popsection",
91						"3:",
92						in(reg) ptr,
93						in($reg_constraint) val,
94						inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
95						options(nostack, preserves_flags, readonly)
96					);
97				}
98			}
99		
100			match success {
101				0 => None,
102				1 => Some(()),
103				_ => unsafe { unreachable_unchecked() }
104			}
105		}	
106	};
107}
108
109gen_checked!(@read checked_read_1 u8 => reg_byte "");
110gen_checked!(@read checked_read_2 u16 => reg ":x");
111gen_checked!(@read checked_read_4 u32 => reg ":e");
112#[cfg(target_arch = "x86_64")] gen_checked!(@read checked_read_8 u64 => reg ":r");
113gen_checked!(@write checked_write_1 u8 => reg_byte "");
114gen_checked!(@write checked_write_2 u16 => reg ":x");
115gen_checked!(@write checked_write_4 u32 => reg ":e");
116#[cfg(target_arch = "x86_64")] gen_checked!(@write checked_write_8 u64 => reg ":r");
117
118#[inline]
119pub fn checked_memcpy(src: *const MaybeUninit<u8>, dest: *mut MaybeUninit<u8>, count: usize) -> Option<()> {
120	let success: usize;
121	unsafe {
122		if crate::detect::__detected::smap() {
123			asm!(
124				"stac",
125				".pushsection .popcorn.deref_handlers.check",
126				".quad 2f",
127				".popsection",
128				"2:",
129				"rep movsb [rdi], [rsi]",
130				"mov {}, 1",
131				".pushsection .popcorn.deref_handlers.handle",
132				".quad 3f",
133				".popsection",
134				"3:",
135				"clac",
136				inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
137				in("rdi") dest,
138				in("rsi") src,
139				inout("rcx") count => _,
140				options(nostack)
141			);
142		} else {
143			asm!(
144				".pushsection .popcorn.deref_handlers.check",
145				".quad 2f",
146				".popsection",
147				"2:",
148				"rep movsb [rdi], [rsi]",
149				"mov {}, 1",
150				".pushsection .popcorn.deref_handlers.handle",
151				".quad 3f",
152				".popsection",
153				"3:",
154				inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
155				in("rdi") dest,
156				in("rsi") src,
157				inout("rcx") count => _,
158				options(nostack)
159			);
160		}
161	}
162
163	match success {
164		0 => None,
165		1 => Some(()),
166		_ => unsafe { unreachable_unchecked() }
167	}
168}
169
170
171#[inline]
172pub fn checked_fill(val: MaybeUninit<u8>, dest: *mut MaybeUninit<u8>, count: usize) -> Option<()> {
173	let success: usize;
174	unsafe {
175		if crate::detect::__detected::smap() {
176			asm!(
177			"stac",
178			".pushsection .popcorn.deref_handlers.check",
179			".quad 2f",
180			".popsection",
181			"2:",
182			"rep stosb [rdi]",
183			"mov {}, 1",
184			".pushsection .popcorn.deref_handlers.handle",
185			".quad 3f",
186			".popsection",
187			"3:",
188			"clac",
189			inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
190			in("rdi") dest,
191			in("al") val,
192			inout("rcx") count => _,
193			options(nostack)
194			);
195		} else {
196			asm!(
197			".pushsection .popcorn.deref_handlers.check",
198			".quad 2f",
199			".popsection",
200			"2:",
201			"rep stosb [rdi]",
202			"mov {}, 1",
203			".pushsection .popcorn.deref_handlers.handle",
204			".quad 3f",
205			".popsection",
206			"3:",
207			inout(reg) 0usize => success, // we need to use `inout` here to ensure the initial value is what we want if the `mov` is never reached
208			in("rdi") dest,
209			in("al") val,
210			inout("rcx") count => _,
211			options(nostack)
212			);
213		}
214	}
215
216	match success {
217		0 => None,
218		1 => Some(()),
219		_ => unsafe { unreachable_unchecked() }
220	}
221}