xref: /openbmc/linux/arch/x86/xen/xen-asm.S (revision 37b9b9a5)
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Asm versions of Xen pv-ops, suitable for direct use.
4 *
5 * We only bother with direct forms (ie, vcpu in percpu data) of the
6 * operations here; the indirect forms are better handled in C.
7 */
8
9#include <asm/asm-offsets.h>
10#include <asm/percpu.h>
11#include <asm/processor-flags.h>
12#include <asm/frame.h>
13#include <asm/asm.h>
14
15#include <linux/linkage.h>
16
17/*
18 * Enable events.  This clears the event mask and tests the pending
19 * event status with one and operation.  If there are pending events,
20 * then enter the hypervisor to get them handled.
21 */
22SYM_FUNC_START(xen_irq_enable_direct)
23	FRAME_BEGIN
24	/* Unmask events */
25	movb $0, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_mask
26
27	/*
28	 * Preempt here doesn't matter because that will deal with any
29	 * pending interrupts.  The pending check may end up being run
30	 * on the wrong CPU, but that doesn't hurt.
31	 */
32
33	/* Test for pending */
34	testb $0xff, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_pending
35	jz 1f
36
37	call check_events
381:
39	FRAME_END
40	ret
41SYM_FUNC_END(xen_irq_enable_direct)
42
43
44/*
45 * Disabling events is simply a matter of making the event mask
46 * non-zero.
47 */
48SYM_FUNC_START(xen_irq_disable_direct)
49	movb $1, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_mask
50	ret
51SYM_FUNC_END(xen_irq_disable_direct)
52
53/*
54 * (xen_)save_fl is used to get the current interrupt enable status.
55 * Callers expect the status to be in X86_EFLAGS_IF, and other bits
56 * may be set in the return value.  We take advantage of this by
57 * making sure that X86_EFLAGS_IF has the right value (and other bits
58 * in that byte are 0), but other bits in the return value are
59 * undefined.  We need to toggle the state of the bit, because Xen and
60 * x86 use opposite senses (mask vs enable).
61 */
62SYM_FUNC_START(xen_save_fl_direct)
63	testb $0xff, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_mask
64	setz %ah
65	addb %ah, %ah
66	ret
67SYM_FUNC_END(xen_save_fl_direct)
68
69
70/*
71 * In principle the caller should be passing us a value return from
72 * xen_save_fl_direct, but for robustness sake we test only the
73 * X86_EFLAGS_IF flag rather than the whole byte. After setting the
74 * interrupt mask state, it checks for unmasked pending events and
75 * enters the hypervisor to get them delivered if so.
76 */
77SYM_FUNC_START(xen_restore_fl_direct)
78	FRAME_BEGIN
79#ifdef CONFIG_X86_64
80	testw $X86_EFLAGS_IF, %di
81#else
82	testb $X86_EFLAGS_IF>>8, %ah
83#endif
84	setz PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_mask
85	/*
86	 * Preempt here doesn't matter because that will deal with any
87	 * pending interrupts.  The pending check may end up being run
88	 * on the wrong CPU, but that doesn't hurt.
89	 */
90
91	/* check for unmasked and pending */
92	cmpw $0x0001, PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_pending
93	jnz 1f
94	call check_events
951:
96	FRAME_END
97	ret
98SYM_FUNC_END(xen_restore_fl_direct)
99
100
101/*
102 * Force an event check by making a hypercall, but preserve regs
103 * before making the call.
104 */
105SYM_FUNC_START(check_events)
106	FRAME_BEGIN
107#ifdef CONFIG_X86_32
108	push %eax
109	push %ecx
110	push %edx
111	call xen_force_evtchn_callback
112	pop %edx
113	pop %ecx
114	pop %eax
115#else
116	push %rax
117	push %rcx
118	push %rdx
119	push %rsi
120	push %rdi
121	push %r8
122	push %r9
123	push %r10
124	push %r11
125	call xen_force_evtchn_callback
126	pop %r11
127	pop %r10
128	pop %r9
129	pop %r8
130	pop %rdi
131	pop %rsi
132	pop %rdx
133	pop %rcx
134	pop %rax
135#endif
136	FRAME_END
137	ret
138SYM_FUNC_END(check_events)
139
140SYM_FUNC_START(xen_read_cr2)
141	FRAME_BEGIN
142	_ASM_MOV PER_CPU_VAR(xen_vcpu), %_ASM_AX
143	_ASM_MOV XEN_vcpu_info_arch_cr2(%_ASM_AX), %_ASM_AX
144	FRAME_END
145	ret
146SYM_FUNC_END(xen_read_cr2);
147
148SYM_FUNC_START(xen_read_cr2_direct)
149	FRAME_BEGIN
150	_ASM_MOV PER_CPU_VAR(xen_vcpu_info) + XEN_vcpu_info_arch_cr2, %_ASM_AX
151	FRAME_END
152	ret
153SYM_FUNC_END(xen_read_cr2_direct);
154