1use std::arch::global_asm;
20
21#[repr(u32)]
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum Mode {
25 Reference = 0,
27 Reload = 1,
30 ForcedExit = 2,
32}
33
34#[repr(u32)]
37#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum Kind {
39 XrstorInit = 0,
41 XrstorHeaderSet = 1,
44 WrpkruZero = 2,
46 Sigreturn = 3,
48 Vzeroupper = 4,
50 LegacyPxor = 5,
52 Vex128Zero = 6,
54 Hi16Zero = 7,
56 PxorVzeroupperHi16 = 8,
58 X87FullInit = 9,
60 X87DirtyFullInit = 10,
62 CompactedXrstorInit = 11,
65 FxrstorInit = 12,
67 OpmaskZero = 13,
69 VzeroallAll = 14,
71}
72
73#[repr(C)]
76#[derive(Debug)]
77pub struct ArmCtx {
78 pub kind: u32, pub component: u32, pub mxcsr: u32, pub mode: u32, pub xcr0: u64, pub save_area: *mut u8, pub image: *const u8, pub fd_to_peer: i32, pub fd_from_peer: i32, pub tgid: i32, pub tid: i32, pub ones: *const u8, pub pkru_out: u32, pub avx512: u32, pub cpuid_cycles: u64, pub seq: u8, pub reply: u8, pub pkru: u8, pub _pad: u8, pub status: u32, pub saved_pkru: u32, pub saved_mxcsr: u32, }
101
102#[repr(u32)]
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub enum ArmStatus {
106 Ok = 0,
107 WriteFailed = 1,
108 ReadFailed = 2,
109 WrongReply = 3,
110 TgkillFailed = 4,
111}
112
113impl ArmStatus {
114 pub fn from_raw(raw: u32) -> Option<Self> {
115 Some(match raw {
116 0 => ArmStatus::Ok,
117 1 => ArmStatus::WriteFailed,
118 2 => ArmStatus::ReadFailed,
119 3 => ArmStatus::WrongReply,
120 4 => ArmStatus::TgkillFailed,
121 _ => return None,
122 })
123 }
124}
125
126#[repr(u32)]
128#[derive(Clone, Copy, Debug, PartialEq, Eq)]
129pub enum PeerState {
130 DirtyPkruDiffers = 0,
132 DirtyWrpkruZero = 1,
134 XrstorInitAll = 2,
136 DirtyPkruHeaderLoaded = 3,
138 DirtyPkruSigreturn = 4,
140 InitValuesAllHeaderBits = 5,
142}
143
144#[repr(C)]
146#[derive(Debug)]
147pub struct PeerCtx {
148 pub state: u32, pub fd_in: i32, pub fd_out: i32, pub avx512: u32, pub image: *const u8, pub xcr0: u64, pub ones: *const u8, pub tgid: i32, pub tid: i32, pub expected_cpu: u32, pub wrong_cpu: u32, pub status: u32, pub rounds: u32, pub byte: u8, pub reply: u8, pub pkru: u8, pub _pad: u8, pub cpu_out: u32, pub saved_pkru: u32, pub saved_mxcsr: u32, }
169
170pub const PEER_EXIT: u8 = 0xff;
172
173unsafe extern "C" {
174 pub fn hermit_xinuse_arm(ctx: *mut ArmCtx) -> u32;
177 pub fn hermit_xinuse_peer_loop(ctx: *mut PeerCtx) -> u32;
179 pub fn hermit_xinuse_save_restore_cycles(area: *mut u8, xcr0: u64) -> u64;
181 pub fn hermit_xinuse_cpuid_cycles() -> u64;
183 pub fn hermit_xinuse_syscall_cycles() -> u64;
185}
186
187global_asm!(
190 r#"
191.macro XINUSE_DIRTY ctxreg, avxoff, onesoff
192 mov rsi, [\ctxreg + \onesoff]
193 cmp dword ptr [\ctxreg + \avxoff], 0
194 je 2f
195 .irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
196 vmovdqu32 zmm\r, [rsi]
197 .endr
198 .irp k,1,2,3,4,5,6,7
199 kxnorq k\k, k\k, k\k
200 .endr
201 kxorq k0, k0, k0
202 jmp 3f
2032:
204 .irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
205 vmovdqu ymm\r, [rsi]
206 .endr
2073:
208 fninit
209 .rept 6
210 fld1
211 .endr
212 fldpi
213 fldl2e
214.endm
215
216// WRPKRU and RDPKRU raise #UD unless the kernel enables protection keys
217// (CR4.PKE), so both run only when the context's PKRU byte is set; without it
218// the PKRU value read is 0.
219.macro XINUSE_WRPKRU value, ctxreg, flagoff
220 cmp byte ptr [\ctxreg + \flagoff], 0
221 je 91f
222 mov eax, \value
223 xor ecx, ecx
224 xor edx, edx
225 wrpkru
22691:
227.endm
228
229.macro XINUSE_RDPKRU ctxreg, flagoff
230 xor eax, eax
231 cmp byte ptr [\ctxreg + \flagoff], 0
232 je 92f
233 xor ecx, ecx
234 rdpkru
23592:
236.endm
237
238.macro XINUSE_RESTORE_PKRU ctxreg, flagoff, savedoff
239 cmp byte ptr [\ctxreg + \flagoff], 0
240 je 93f
241 mov eax, [\ctxreg + \savedoff]
242 xor ecx, ecx
243 xor edx, edx
244 wrpkru
24593:
246.endm
247
248.macro XINUSE_XRSTOR_MASK ctxreg, imgoff, maskreg
249 mov rax, \maskreg
250 mov rdx, rax
251 shr rdx, 32
252 mov r8, [\ctxreg + \imgoff]
253 xrstor [r8]
254.endm
255"#
256);
257
258global_asm!(
259 r#"
260 .text
261 .globl hermit_xinuse_arm
262 .type hermit_xinuse_arm,@function
263hermit_xinuse_arm:
264 push rbx
265 push r12
266 push r13
267 mov r12, rdi
268 XINUSE_RDPKRU r12, 82
269 mov [r12 + 88], eax
270 stmxcsr dword ptr [r12 + 92]
271 XINUSE_WRPKRU 0x55555554, r12, 82
272 XINUSE_DIRTY r12, 68, 56
273 ldmxcsr dword ptr [r12 + 8]
274
275 mov eax, [r12 + 0]
276 cmp eax, 0
277 je .Larm_xrstor
278 cmp eax, 1
279 je .Larm_xrstor
280 cmp eax, 11
281 je .Larm_xrstor
282 cmp eax, 2
283 je .Larm_wrpkru
284 cmp eax, 3
285 je .Larm_sigreturn
286 cmp eax, 4
287 je .Larm_vzeroupper
288 cmp eax, 5
289 je .Larm_legacy_pxor
290 cmp eax, 6
291 je .Larm_vex128
292 cmp eax, 7
293 je .Larm_hi16
294 cmp eax, 8
295 je .Larm_mix
296 cmp eax, 9
297 je .Larm_x87_full
298 cmp eax, 10
299 je .Larm_x87_dirty_full
300 cmp eax, 12
301 je .Larm_fxrstor
302 cmp eax, 13
303 je .Larm_opmask
304 cmp eax, 14
305 je .Larm_vzeroall
306 jmp .Larm_mode
307
308.Larm_xrstor:
309 mov ecx, [r12 + 4]
310 mov r13, 1
311 shl r13, cl
312 XINUSE_XRSTOR_MASK r12, 32, r13
313 jmp .Larm_mode
314.Larm_wrpkru:
315 XINUSE_WRPKRU 0, r12, 82
316 jmp .Larm_mode
317.Larm_sigreturn:
318 XINUSE_WRPKRU 0, r12, 82
319 mov eax, 234
320 movsxd rdi, dword ptr [r12 + 48]
321 movsxd rsi, dword ptr [r12 + 52]
322 mov edx, 10
323 syscall
324 test rax, rax
325 jz .Larm_mode
326 mov dword ptr [r12 + 84], 4
327 jmp .Larm_finish
328.Larm_vzeroupper:
329 vzeroupper
330 jmp .Larm_mode
331.Larm_legacy_pxor:
332 .irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
333 pxor xmm\r, xmm\r
334 .endr
335 jmp .Larm_mode
336.Larm_vex128:
337 .irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
338 vpxor xmm\r, xmm\r, xmm\r
339 .endr
340 jmp .Larm_mode
341.Larm_hi16:
342 .irp r,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
343 vpxord zmm\r, zmm\r, zmm\r
344 .endr
345 jmp .Larm_mode
346.Larm_mix:
347 .irp r,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
348 pxor xmm\r, xmm\r
349 .endr
350 vzeroupper
351 .irp r,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
352 vpxord zmm\r, zmm\r, zmm\r
353 .endr
354 jmp .Larm_mode
355.Larm_x87_dirty_full:
356 fld1
357 fldpi
358.Larm_x87_full:
359 fninit
360 .rept 8
361 fldz
362 .endr
363 fninit
364 jmp .Larm_mode
365.Larm_fxrstor:
366 mov r8, [r12 + 32]
367 fxrstor [r8]
368 jmp .Larm_mode
369.Larm_opmask:
370 .irp k,0,1,2,3,4,5,6,7
371 kxorq k\k, k\k, k\k
372 .endr
373 jmp .Larm_mode
374.Larm_vzeroall:
375 vzeroall
376 cmp dword ptr [r12 + 68], 0
377 je .Larm_mode
378 .irp r,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
379 vpxord zmm\r, zmm\r, zmm\r
380 .endr
381
382.Larm_mode:
383 mov eax, [r12 + 12]
384 cmp eax, 1
385 je .Larm_reload
386 cmp eax, 2
387 je .Larm_exit
388 jmp .Larm_save
389.Larm_reload:
390 mov eax, 1
391 movsxd rdi, dword ptr [r12 + 40]
392 lea rsi, [r12 + 80]
393 mov edx, 1
394 syscall
395 cmp rax, 1
396 je 4f
397 mov dword ptr [r12 + 84], 1
398 jmp .Larm_finish
3994:
400 xor eax, eax
401 movsxd rdi, dword ptr [r12 + 44]
402 lea rsi, [r12 + 81]
403 mov edx, 1
404 syscall
405 cmp rax, 1
406 je 5f
407 mov dword ptr [r12 + 84], 2
408 jmp .Larm_finish
4095:
410 movzx eax, byte ptr [r12 + 80]
411 inc al
412 cmp al, byte ptr [r12 + 81]
413 je .Larm_save
414 mov dword ptr [r12 + 84], 3
415 jmp .Larm_finish
416.Larm_exit:
417 rdtscp
418 shl rdx, 32
419 or rax, rdx
420 mov r13, rax
421 xor eax, eax
422 xor ecx, ecx
423 cpuid
424 rdtscp
425 shl rdx, 32
426 or rax, rdx
427 sub rax, r13
428 mov [r12 + 72], rax
429
430.Larm_save:
431 mov rax, [r12 + 16]
432 mov rdx, rax
433 shr rdx, 32
434 mov r8, [r12 + 24]
435 xsave [r8]
436 XINUSE_RDPKRU r12, 82
437 mov [r12 + 64], eax
438
439.Larm_finish:
440 XINUSE_RESTORE_PKRU r12, 82, 88
441 ldmxcsr dword ptr [r12 + 92]
442 fninit
443 vzeroupper
444 mov eax, [r12 + 84]
445 pop r13
446 pop r12
447 pop rbx
448 ret
449 .size hermit_xinuse_arm, .-hermit_xinuse_arm
450
451 .globl hermit_xinuse_peer_loop
452 .type hermit_xinuse_peer_loop,@function
453hermit_xinuse_peer_loop:
454 push rbx
455 push r12
456 push r13
457 mov r12, rdi
458 XINUSE_RDPKRU r12, 66
459 mov [r12 + 72], eax
460 stmxcsr dword ptr [r12 + 76]
461.Lpeer_round:
462 xor eax, eax
463 movsxd rdi, dword ptr [r12 + 4]
464 lea rsi, [r12 + 64]
465 mov edx, 1
466 syscall
467 cmp rax, 1
468 je 6f
469 mov dword ptr [r12 + 56], 2
470 jmp .Lpeer_finish
4716:
472 cmp byte ptr [r12 + 64], 0xff
473 je .Lpeer_finish
474
475 mov eax, [r12 + 0]
476 cmp eax, 0
477 je .Lpeer_dirty_differs
478 cmp eax, 1
479 je .Lpeer_dirty_wrpkru
480 cmp eax, 2
481 je .Lpeer_xrstor_all
482 cmp eax, 3
483 je .Lpeer_header_loaded
484 cmp eax, 4
485 je .Lpeer_sigreturn
486 jmp .Lpeer_init_values
487
488.Lpeer_dirty_differs:
489 XINUSE_DIRTY r12, 12, 32
490 XINUSE_WRPKRU 0x55555550, r12, 66
491 jmp .Lpeer_reply
492.Lpeer_dirty_wrpkru:
493 XINUSE_DIRTY r12, 12, 32
494 XINUSE_WRPKRU 0, r12, 66
495 jmp .Lpeer_reply
496.Lpeer_xrstor_all:
497 XINUSE_WRPKRU 0x55555550, r12, 66
498 mov r13, [r12 + 24]
499 XINUSE_XRSTOR_MASK r12, 16, r13
500 jmp .Lpeer_reply
501.Lpeer_header_loaded:
502 XINUSE_DIRTY r12, 12, 32
503 mov r13, 0x200
504 XINUSE_XRSTOR_MASK r12, 16, r13
505 jmp .Lpeer_reply
506.Lpeer_sigreturn:
507 XINUSE_DIRTY r12, 12, 32
508 XINUSE_WRPKRU 0, r12, 66
509 mov eax, 234
510 movsxd rdi, dword ptr [r12 + 40]
511 movsxd rsi, dword ptr [r12 + 44]
512 mov edx, 10
513 syscall
514 test rax, rax
515 jz .Lpeer_reply
516 mov dword ptr [r12 + 56], 4
517 jmp .Lpeer_finish
518.Lpeer_init_values:
519 mov r13, [r12 + 24]
520 XINUSE_XRSTOR_MASK r12, 16, r13
521
522.Lpeer_reply:
523 mov eax, 309
524 lea rdi, [r12 + 68]
525 xor esi, esi
526 xor edx, edx
527 syscall
528 mov eax, [r12 + 68]
529 cmp eax, [r12 + 48]
530 je 7f
531 inc dword ptr [r12 + 52]
5327:
533 inc dword ptr [r12 + 60]
534 movzx eax, byte ptr [r12 + 64]
535 inc al
536 mov [r12 + 65], al
537 mov eax, 1
538 movsxd rdi, dword ptr [r12 + 8]
539 lea rsi, [r12 + 65]
540 mov edx, 1
541 syscall
542 cmp rax, 1
543 je .Lpeer_round
544 mov dword ptr [r12 + 56], 1
545
546.Lpeer_finish:
547 XINUSE_RESTORE_PKRU r12, 66, 72
548 ldmxcsr dword ptr [r12 + 76]
549 fninit
550 vzeroupper
551 mov eax, [r12 + 56]
552 pop r13
553 pop r12
554 pop rbx
555 ret
556 .size hermit_xinuse_peer_loop, .-hermit_xinuse_peer_loop
557
558 .globl hermit_xinuse_save_restore_cycles
559 .type hermit_xinuse_save_restore_cycles,@function
560hermit_xinuse_save_restore_cycles:
561 push rbx
562 mov r8, rdi
563 mov r9, rsi
564 rdtscp
565 shl rdx, 32
566 or rax, rdx
567 mov r10, rax
568 mov rax, r9
569 mov rdx, r9
570 shr rdx, 32
571 xsave [r8]
572 mov rax, r9
573 mov rdx, r9
574 shr rdx, 32
575 xrstor [r8]
576 rdtscp
577 shl rdx, 32
578 or rax, rdx
579 sub rax, r10
580 vzeroupper
581 pop rbx
582 ret
583 .size hermit_xinuse_save_restore_cycles, .-hermit_xinuse_save_restore_cycles
584
585 .globl hermit_xinuse_cpuid_cycles
586 .type hermit_xinuse_cpuid_cycles,@function
587hermit_xinuse_cpuid_cycles:
588 push rbx
589 rdtscp
590 shl rdx, 32
591 or rax, rdx
592 mov r8, rax
593 xor eax, eax
594 xor ecx, ecx
595 cpuid
596 rdtscp
597 shl rdx, 32
598 or rax, rdx
599 sub rax, r8
600 pop rbx
601 ret
602 .size hermit_xinuse_cpuid_cycles, .-hermit_xinuse_cpuid_cycles
603
604 .globl hermit_xinuse_syscall_cycles
605 .type hermit_xinuse_syscall_cycles,@function
606hermit_xinuse_syscall_cycles:
607 rdtscp
608 shl rdx, 32
609 or rax, rdx
610 mov r8, rax
611 mov eax, 110
612 syscall
613 rdtscp
614 shl rdx, 32
615 or rax, rdx
616 sub rax, r8
617 ret
618 .size hermit_xinuse_syscall_cycles, .-hermit_xinuse_syscall_cycles
619"#
620);
621
622#[cfg(test)]
623mod tests {
624 use std::mem::offset_of;
625
626 use super::*;
627
628 #[test]
629 fn arm_ctx_layout_matches_the_assembly() {
630 assert_eq!(offset_of!(ArmCtx, kind), 0);
631 assert_eq!(offset_of!(ArmCtx, component), 4);
632 assert_eq!(offset_of!(ArmCtx, mxcsr), 8);
633 assert_eq!(offset_of!(ArmCtx, mode), 12);
634 assert_eq!(offset_of!(ArmCtx, xcr0), 16);
635 assert_eq!(offset_of!(ArmCtx, save_area), 24);
636 assert_eq!(offset_of!(ArmCtx, image), 32);
637 assert_eq!(offset_of!(ArmCtx, fd_to_peer), 40);
638 assert_eq!(offset_of!(ArmCtx, fd_from_peer), 44);
639 assert_eq!(offset_of!(ArmCtx, tgid), 48);
640 assert_eq!(offset_of!(ArmCtx, tid), 52);
641 assert_eq!(offset_of!(ArmCtx, ones), 56);
642 assert_eq!(offset_of!(ArmCtx, pkru_out), 64);
643 assert_eq!(offset_of!(ArmCtx, avx512), 68);
644 assert_eq!(offset_of!(ArmCtx, cpuid_cycles), 72);
645 assert_eq!(offset_of!(ArmCtx, seq), 80);
646 assert_eq!(offset_of!(ArmCtx, pkru), 82);
647 assert_eq!(offset_of!(ArmCtx, reply), 81);
648 assert_eq!(offset_of!(ArmCtx, status), 84);
649 assert_eq!(offset_of!(ArmCtx, saved_pkru), 88);
650 assert_eq!(offset_of!(ArmCtx, saved_mxcsr), 92);
651 }
652
653 #[test]
654 fn peer_ctx_layout_matches_the_assembly() {
655 assert_eq!(offset_of!(PeerCtx, state), 0);
656 assert_eq!(offset_of!(PeerCtx, fd_in), 4);
657 assert_eq!(offset_of!(PeerCtx, fd_out), 8);
658 assert_eq!(offset_of!(PeerCtx, avx512), 12);
659 assert_eq!(offset_of!(PeerCtx, image), 16);
660 assert_eq!(offset_of!(PeerCtx, xcr0), 24);
661 assert_eq!(offset_of!(PeerCtx, ones), 32);
662 assert_eq!(offset_of!(PeerCtx, tgid), 40);
663 assert_eq!(offset_of!(PeerCtx, tid), 44);
664 assert_eq!(offset_of!(PeerCtx, expected_cpu), 48);
665 assert_eq!(offset_of!(PeerCtx, wrong_cpu), 52);
666 assert_eq!(offset_of!(PeerCtx, status), 56);
667 assert_eq!(offset_of!(PeerCtx, rounds), 60);
668 assert_eq!(offset_of!(PeerCtx, byte), 64);
669 assert_eq!(offset_of!(PeerCtx, reply), 65);
670 assert_eq!(offset_of!(PeerCtx, pkru), 66);
671 assert_eq!(offset_of!(PeerCtx, cpu_out), 68);
672 assert_eq!(offset_of!(PeerCtx, saved_pkru), 72);
673 assert_eq!(offset_of!(PeerCtx, saved_mxcsr), 76);
674 }
675}