blob: d91336114344a1ff112d3324678a7d74e7cef81e [file] [log] [blame]
Isaku Yamahata080104c2008-10-17 11:17:58 +09001/******************************************************************************
2 * arch/ia64/xen/xen_pv_ops.c
3 *
4 * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
5 * VA Linux Systems Japan K.K.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <linux/console.h>
24#include <linux/irq.h>
25#include <linux/kernel.h>
26#include <linux/pm.h>
Isaku Yamahata533bd152009-03-04 21:05:35 +090027#include <linux/unistd.h>
Isaku Yamahata080104c2008-10-17 11:17:58 +090028
29#include <asm/xen/hypervisor.h>
30#include <asm/xen/xencomm.h>
31#include <asm/xen/privop.h>
32
Isaku Yamahata7477de92008-10-17 11:18:07 +090033#include "irq_xen.h"
Isaku Yamahatadcbbecd2008-10-17 11:18:08 +090034#include "time.h"
Isaku Yamahata7477de92008-10-17 11:18:07 +090035
Isaku Yamahata080104c2008-10-17 11:17:58 +090036/***************************************************************************
37 * general info
38 */
39static struct pv_info xen_info __initdata = {
40 .kernel_rpl = 2, /* or 1: determin at runtime */
41 .paravirt_enabled = 1,
42 .name = "Xen/ia64",
43};
44
45#define IA64_RSC_PL_SHIFT 2
46#define IA64_RSC_PL_BIT_SIZE 2
47#define IA64_RSC_PL_MASK \
48 (((1UL << IA64_RSC_PL_BIT_SIZE) - 1) << IA64_RSC_PL_SHIFT)
49
50static void __init
51xen_info_init(void)
52{
53 /* Xenified Linux/ia64 may run on pl = 1 or 2.
54 * determin at run time. */
55 unsigned long rsc = ia64_getreg(_IA64_REG_AR_RSC);
56 unsigned int rpl = (rsc & IA64_RSC_PL_MASK) >> IA64_RSC_PL_SHIFT;
57 xen_info.kernel_rpl = rpl;
58}
59
60/***************************************************************************
Isaku Yamahatab5a26e42008-10-17 11:17:59 +090061 * pv_init_ops
62 * initialization hooks.
63 */
64
65static void
66xen_panic_hypercall(struct unw_frame_info *info, void *arg)
67{
68 current->thread.ksp = (__u64)info->sw - 16;
69 HYPERVISOR_shutdown(SHUTDOWN_crash);
70 /* we're never actually going to get here... */
71}
72
73static int
74xen_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
75{
76 unw_init_running(xen_panic_hypercall, NULL);
77 /* we're never actually going to get here... */
78 return NOTIFY_DONE;
79}
80
81static struct notifier_block xen_panic_block = {
82 xen_panic_event, NULL, 0 /* try to go last */
83};
84
85static void xen_pm_power_off(void)
86{
87 local_irq_disable();
88 HYPERVISOR_shutdown(SHUTDOWN_poweroff);
89}
90
91static void __init
92xen_banner(void)
93{
94 printk(KERN_INFO
95 "Running on Xen! pl = %d start_info_pfn=0x%lx nr_pages=%ld "
96 "flags=0x%x\n",
97 xen_info.kernel_rpl,
98 HYPERVISOR_shared_info->arch.start_info_pfn,
99 xen_start_info->nr_pages, xen_start_info->flags);
100}
101
102static int __init
103xen_reserve_memory(struct rsvd_region *region)
104{
105 region->start = (unsigned long)__va(
106 (HYPERVISOR_shared_info->arch.start_info_pfn << PAGE_SHIFT));
107 region->end = region->start + PAGE_SIZE;
108 return 1;
109}
110
111static void __init
112xen_arch_setup_early(void)
113{
114 struct shared_info *s;
115 BUG_ON(!xen_pv_domain());
116
117 s = HYPERVISOR_shared_info;
118 xen_start_info = __va(s->arch.start_info_pfn << PAGE_SHIFT);
119
120 /* Must be done before any hypercall. */
121 xencomm_initialize();
122
123 xen_setup_features();
124 /* Register a call for panic conditions. */
125 atomic_notifier_chain_register(&panic_notifier_list,
126 &xen_panic_block);
127 pm_power_off = xen_pm_power_off;
128
129 xen_ia64_enable_opt_feature();
130}
131
132static void __init
133xen_arch_setup_console(char **cmdline_p)
134{
135 add_preferred_console("xenboot", 0, NULL);
136 add_preferred_console("tty", 0, NULL);
137 /* use hvc_xen */
138 add_preferred_console("hvc", 0, NULL);
139
140#if !defined(CONFIG_VT) || !defined(CONFIG_DUMMY_CONSOLE)
141 conswitchp = NULL;
142#endif
143}
144
145static int __init
146xen_arch_setup_nomca(void)
147{
148 return 1;
149}
150
151static void __init
152xen_post_smp_prepare_boot_cpu(void)
153{
154 xen_setup_vcpu_info_placement();
155}
156
Tony Luckec8148d2009-02-19 12:05:00 -0800157static const struct pv_init_ops xen_init_ops __initconst = {
Isaku Yamahatab5a26e42008-10-17 11:17:59 +0900158 .banner = xen_banner,
159
160 .reserve_memory = xen_reserve_memory,
161
162 .arch_setup_early = xen_arch_setup_early,
163 .arch_setup_console = xen_arch_setup_console,
164 .arch_setup_nomca = xen_arch_setup_nomca,
165
166 .post_smp_prepare_boot_cpu = xen_post_smp_prepare_boot_cpu,
167};
168
169/***************************************************************************
Isaku Yamahata533bd152009-03-04 21:05:35 +0900170 * pv_fsys_data
171 * addresses for fsys
172 */
173
174extern unsigned long xen_fsyscall_table[NR_syscalls];
175extern char xen_fsys_bubble_down[];
176struct pv_fsys_data xen_fsys_data __initdata = {
177 .fsyscall_table = (unsigned long *)xen_fsyscall_table,
178 .fsys_bubble_down = (void *)xen_fsys_bubble_down,
179};
180
181/***************************************************************************
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900182 * pv_cpu_ops
183 * intrinsics hooks.
184 */
185
Isaku Yamahata496203b2009-03-04 21:05:39 +0900186static void
187xen_set_itm_with_offset(unsigned long val)
188{
189 /* ia64_cpu_local_tick() calls this with interrupt enabled. */
190 /* WARN_ON(!irqs_disabled()); */
191 xen_set_itm(val - XEN_MAPPEDREGS->itc_offset);
192}
193
194static unsigned long
195xen_get_itm_with_offset(void)
196{
197 /* unused at this moment */
198 printk(KERN_DEBUG "%s is called.\n", __func__);
199
200 WARN_ON(!irqs_disabled());
201 return ia64_native_getreg(_IA64_REG_CR_ITM) +
202 XEN_MAPPEDREGS->itc_offset;
203}
204
205/* ia64_set_itc() is only called by
206 * cpu_init() with ia64_set_itc(0) and ia64_sync_itc().
207 * So XEN_MAPPEDRESG->itc_offset cal be considered as almost constant.
208 */
209static void
210xen_set_itc(unsigned long val)
211{
212 unsigned long mitc;
213
214 WARN_ON(!irqs_disabled());
215 mitc = ia64_native_getreg(_IA64_REG_AR_ITC);
216 XEN_MAPPEDREGS->itc_offset = val - mitc;
217 XEN_MAPPEDREGS->itc_last = val;
218}
219
220static unsigned long
221xen_get_itc(void)
222{
223 unsigned long res;
224 unsigned long itc_offset;
225 unsigned long itc_last;
226 unsigned long ret_itc_last;
227
228 itc_offset = XEN_MAPPEDREGS->itc_offset;
229 do {
230 itc_last = XEN_MAPPEDREGS->itc_last;
231 res = ia64_native_getreg(_IA64_REG_AR_ITC);
232 res += itc_offset;
233 if (itc_last >= res)
234 res = itc_last + 1;
235 ret_itc_last = cmpxchg(&XEN_MAPPEDREGS->itc_last,
236 itc_last, res);
237 } while (unlikely(ret_itc_last != itc_last));
238 return res;
239
240#if 0
241 /* ia64_itc_udelay() calls ia64_get_itc() with interrupt enabled.
242 Should it be paravirtualized instead? */
243 WARN_ON(!irqs_disabled());
244 itc_offset = XEN_MAPPEDREGS->itc_offset;
245 itc_last = XEN_MAPPEDREGS->itc_last;
246 res = ia64_native_getreg(_IA64_REG_AR_ITC);
247 res += itc_offset;
248 if (itc_last >= res)
249 res = itc_last + 1;
250 XEN_MAPPEDREGS->itc_last = res;
251 return res;
252#endif
253}
254
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900255static void xen_setreg(int regnum, unsigned long val)
256{
257 switch (regnum) {
258 case _IA64_REG_AR_KR0 ... _IA64_REG_AR_KR7:
259 xen_set_kr(regnum - _IA64_REG_AR_KR0, val);
260 break;
261#ifdef CONFIG_IA32_SUPPORT
262 case _IA64_REG_AR_EFLAG:
263 xen_set_eflag(val);
264 break;
265#endif
Isaku Yamahata496203b2009-03-04 21:05:39 +0900266 case _IA64_REG_AR_ITC:
267 xen_set_itc(val);
268 break;
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900269 case _IA64_REG_CR_TPR:
270 xen_set_tpr(val);
271 break;
272 case _IA64_REG_CR_ITM:
Isaku Yamahata496203b2009-03-04 21:05:39 +0900273 xen_set_itm_with_offset(val);
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900274 break;
275 case _IA64_REG_CR_EOI:
276 xen_eoi(val);
277 break;
278 default:
279 ia64_native_setreg_func(regnum, val);
280 break;
281 }
282}
283
284static unsigned long xen_getreg(int regnum)
285{
286 unsigned long res;
287
288 switch (regnum) {
289 case _IA64_REG_PSR:
290 res = xen_get_psr();
291 break;
292#ifdef CONFIG_IA32_SUPPORT
293 case _IA64_REG_AR_EFLAG:
294 res = xen_get_eflag();
295 break;
296#endif
Isaku Yamahata496203b2009-03-04 21:05:39 +0900297 case _IA64_REG_AR_ITC:
298 res = xen_get_itc();
299 break;
300 case _IA64_REG_CR_ITM:
301 res = xen_get_itm_with_offset();
302 break;
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900303 case _IA64_REG_CR_IVR:
304 res = xen_get_ivr();
305 break;
306 case _IA64_REG_CR_TPR:
307 res = xen_get_tpr();
308 break;
309 default:
310 res = ia64_native_getreg_func(regnum);
311 break;
312 }
313 return res;
314}
315
316/* turning on interrupts is a bit more complicated.. write to the
317 * memory-mapped virtual psr.i bit first (to avoid race condition),
318 * then if any interrupts were pending, we have to execute a hyperprivop
319 * to ensure the pending interrupt gets delivered; else we're done! */
320static void
321xen_ssm_i(void)
322{
323 int old = xen_get_virtual_psr_i();
324 xen_set_virtual_psr_i(1);
325 barrier();
326 if (!old && xen_get_virtual_pend())
327 xen_hyper_ssm_i();
328}
329
330/* turning off interrupts can be paravirtualized simply by writing
331 * to a memory-mapped virtual psr.i bit (implemented as a 16-bit bool) */
332static void
333xen_rsm_i(void)
334{
335 xen_set_virtual_psr_i(0);
336 barrier();
337}
338
339static unsigned long
340xen_get_psr_i(void)
341{
342 return xen_get_virtual_psr_i() ? IA64_PSR_I : 0;
343}
344
345static void
346xen_intrin_local_irq_restore(unsigned long mask)
347{
348 if (mask & IA64_PSR_I)
349 xen_ssm_i();
350 else
351 xen_rsm_i();
352}
353
Isaku Yamahatae8c3b422009-03-04 21:05:32 +0900354static const struct pv_cpu_ops xen_cpu_ops __initconst = {
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900355 .fc = xen_fc,
356 .thash = xen_thash,
357 .get_cpuid = xen_get_cpuid,
358 .get_pmd = xen_get_pmd,
359 .getreg = xen_getreg,
360 .setreg = xen_setreg,
361 .ptcga = xen_ptcga,
362 .get_rr = xen_get_rr,
363 .set_rr = xen_set_rr,
364 .set_rr0_to_rr4 = xen_set_rr0_to_rr4,
365 .ssm_i = xen_ssm_i,
366 .rsm_i = xen_rsm_i,
367 .get_psr_i = xen_get_psr_i,
368 .intrin_local_irq_restore
369 = xen_intrin_local_irq_restore,
370};
371
Isaku Yamahata16583bc2008-10-17 11:18:04 +0900372/******************************************************************************
373 * replacement of hand written assembly codes.
374 */
375
376extern char xen_switch_to;
377extern char xen_leave_syscall;
378extern char xen_work_processed_syscall;
379extern char xen_leave_kernel;
380
381const struct pv_cpu_asm_switch xen_cpu_asm_switch = {
382 .switch_to = (unsigned long)&xen_switch_to,
383 .leave_syscall = (unsigned long)&xen_leave_syscall,
384 .work_processed_syscall = (unsigned long)&xen_work_processed_syscall,
385 .leave_kernel = (unsigned long)&xen_leave_kernel,
386};
387
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900388/***************************************************************************
Isaku Yamahatabcdd4872008-10-17 11:18:05 +0900389 * pv_iosapic_ops
390 * iosapic read/write hooks.
391 */
392static void
393xen_pcat_compat_init(void)
394{
395 /* nothing */
396}
397
398static struct irq_chip*
399xen_iosapic_get_irq_chip(unsigned long trigger)
400{
401 return NULL;
402}
403
404static unsigned int
405xen_iosapic_read(char __iomem *iosapic, unsigned int reg)
406{
407 struct physdev_apic apic_op;
408 int ret;
409
410 apic_op.apic_physbase = (unsigned long)iosapic -
411 __IA64_UNCACHED_OFFSET;
412 apic_op.reg = reg;
413 ret = HYPERVISOR_physdev_op(PHYSDEVOP_apic_read, &apic_op);
414 if (ret)
415 return ret;
416 return apic_op.value;
417}
418
419static void
420xen_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
421{
422 struct physdev_apic apic_op;
423
424 apic_op.apic_physbase = (unsigned long)iosapic -
425 __IA64_UNCACHED_OFFSET;
426 apic_op.reg = reg;
427 apic_op.value = val;
428 HYPERVISOR_physdev_op(PHYSDEVOP_apic_write, &apic_op);
429}
430
Tony Luckec8148d2009-02-19 12:05:00 -0800431static const struct pv_iosapic_ops xen_iosapic_ops __initconst = {
Isaku Yamahatabcdd4872008-10-17 11:18:05 +0900432 .pcat_compat_init = xen_pcat_compat_init,
433 .__get_irq_chip = xen_iosapic_get_irq_chip,
434
435 .__read = xen_iosapic_read,
436 .__write = xen_iosapic_write,
437};
438
439/***************************************************************************
Isaku Yamahata080104c2008-10-17 11:17:58 +0900440 * pv_ops initialization
441 */
442
443void __init
444xen_setup_pv_ops(void)
445{
446 xen_info_init();
447 pv_info = xen_info;
Isaku Yamahatab5a26e42008-10-17 11:17:59 +0900448 pv_init_ops = xen_init_ops;
Isaku Yamahata533bd152009-03-04 21:05:35 +0900449 pv_fsys_data = xen_fsys_data;
Isaku Yamahata4b83ce42008-10-17 11:18:00 +0900450 pv_cpu_ops = xen_cpu_ops;
Isaku Yamahatabcdd4872008-10-17 11:18:05 +0900451 pv_iosapic_ops = xen_iosapic_ops;
Isaku Yamahata7477de92008-10-17 11:18:07 +0900452 pv_irq_ops = xen_irq_ops;
Isaku Yamahatadcbbecd2008-10-17 11:18:08 +0900453 pv_time_ops = xen_time_ops;
Isaku Yamahata16583bc2008-10-17 11:18:04 +0900454
455 paravirt_cpu_asm_init(&xen_cpu_asm_switch);
Isaku Yamahata080104c2008-10-17 11:17:58 +0900456}