1/****************************************************************************** 2 * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp> 3 * VA Linux Systems Japan K.K. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 */ 20 21#ifndef _ASM_IA64_PARAVIRT_PRIVOP_H 22#define _ASM_IA64_PARAVIRT_PRIVOP_H 23 24#ifdef CONFIG_PARAVIRT 25 26#ifndef __ASSEMBLY__ 27 28#include <linux/types.h> 29#include <asm/kregs.h> /* for IA64_PSR_I */ 30 31/****************************************************************************** 32 * replacement of intrinsics operations. 33 */ 34 35struct pv_cpu_ops { 36 void (*fc)(void *addr); 37 unsigned long (*thash)(unsigned long addr); 38 unsigned long (*get_cpuid)(int index); 39 unsigned long (*get_pmd)(int index); 40 unsigned long (*getreg)(int reg); 41 void (*setreg)(int reg, unsigned long val); 42 void (*ptcga)(unsigned long addr, unsigned long size); 43 unsigned long (*get_rr)(unsigned long index); 44 void (*set_rr)(unsigned long index, unsigned long val); 45 void (*set_rr0_to_rr4)(unsigned long val0, unsigned long val1, 46 unsigned long val2, unsigned long val3, 47 unsigned long val4); 48 void (*ssm_i)(void); 49 void (*rsm_i)(void); 50 unsigned long (*get_psr_i)(void); 51 void (*intrin_local_irq_restore)(unsigned long flags); 52}; 53 54extern struct pv_cpu_ops pv_cpu_ops; 55 56extern void ia64_native_setreg_func(int regnum, unsigned long val); 57extern unsigned long ia64_native_getreg_func(int regnum); 58 59/************************************************/ 60/* Instructions paravirtualized for performance */ 61/************************************************/ 62 63#ifndef ASM_SUPPORTED 64#define paravirt_ssm_i() pv_cpu_ops.ssm_i() 65#define paravirt_rsm_i() pv_cpu_ops.rsm_i() 66#define __paravirt_getreg() pv_cpu_ops.getreg() 67#endif 68 69/* mask for ia64_native_ssm/rsm() must be constant.("i" constraing). 70 * static inline function doesn't satisfy it. */ 71#define paravirt_ssm(mask) \ 72 do { \ 73 if ((mask) == IA64_PSR_I) \ 74 paravirt_ssm_i(); \ 75 else \ 76 ia64_native_ssm(mask); \ 77 } while (0) 78 79#define paravirt_rsm(mask) \ 80 do { \ 81 if ((mask) == IA64_PSR_I) \ 82 paravirt_rsm_i(); \ 83 else \ 84 ia64_native_rsm(mask); \ 85 } while (0) 86 87/* returned ip value should be the one in the caller, 88 * not in __paravirt_getreg() */ 89#define paravirt_getreg(reg) \ 90 ({ \ 91 unsigned long res; \ 92 if ((reg) == _IA64_REG_IP) \ 93 res = ia64_native_getreg(_IA64_REG_IP); \ 94 else \ 95 res = __paravirt_getreg(reg); \ 96 res; \ 97 }) 98 99/****************************************************************************** 100 * replacement of hand written assembly codes. 101 */ 102struct pv_cpu_asm_switch { 103 unsigned long switch_to; 104 unsigned long leave_syscall; 105 unsigned long work_processed_syscall; 106 unsigned long leave_kernel; 107}; 108void paravirt_cpu_asm_init(const struct pv_cpu_asm_switch *cpu_asm_switch); 109 110#endif /* __ASSEMBLY__ */ 111 112#define IA64_PARAVIRT_ASM_FUNC(name) paravirt_ ## name 113 114#else 115 116/* fallback for native case */ 117#define IA64_PARAVIRT_ASM_FUNC(name) ia64_native_ ## name 118 119#endif /* CONFIG_PARAVIRT */ 120 121#if defined(CONFIG_PARAVIRT) && defined(ASM_SUPPORTED) 122#define paravirt_dv_serialize_data() ia64_dv_serialize_data() 123#else 124#define paravirt_dv_serialize_data() /* nothing */ 125#endif 126 127/* these routines utilize privilege-sensitive or performance-sensitive 128 * privileged instructions so the code must be replaced with 129 * paravirtualized versions */ 130#define ia64_switch_to IA64_PARAVIRT_ASM_FUNC(switch_to) 131#define ia64_leave_syscall IA64_PARAVIRT_ASM_FUNC(leave_syscall) 132#define ia64_work_processed_syscall \ 133 IA64_PARAVIRT_ASM_FUNC(work_processed_syscall) 134#define ia64_leave_kernel IA64_PARAVIRT_ASM_FUNC(leave_kernel) 135 136 137#if defined(CONFIG_PARAVIRT) 138/****************************************************************************** 139 * binary patching infrastructure 140 */ 141#define PARAVIRT_PATCH_TYPE_FC 1 142#define PARAVIRT_PATCH_TYPE_THASH 2 143#define PARAVIRT_PATCH_TYPE_GET_CPUID 3 144#define PARAVIRT_PATCH_TYPE_GET_PMD 4 145#define PARAVIRT_PATCH_TYPE_PTCGA 5 146#define PARAVIRT_PATCH_TYPE_GET_RR 6 147#define PARAVIRT_PATCH_TYPE_SET_RR 7 148#define PARAVIRT_PATCH_TYPE_SET_RR0_TO_RR4 8 149#define PARAVIRT_PATCH_TYPE_SSM_I 9 150#define PARAVIRT_PATCH_TYPE_RSM_I 10 151#define PARAVIRT_PATCH_TYPE_GET_PSR_I 11 152#define PARAVIRT_PATCH_TYPE_INTRIN_LOCAL_IRQ_RESTORE 12 153 154/* PARAVIRT_PATY_TYPE_[GS]ETREG + _IA64_REG_xxx */ 155#define PARAVIRT_PATCH_TYPE_GETREG 0x10000000 156#define PARAVIRT_PATCH_TYPE_SETREG 0x20000000 157 158/* 159 * struct task_struct* (*ia64_switch_to)(void* next_task); 160 * void *ia64_leave_syscall; 161 * void *ia64_work_processed_syscall 162 * void *ia64_leave_kernel; 163 */ 164 165#define PARAVIRT_PATCH_TYPE_BR_START 0x30000000 166#define PARAVIRT_PATCH_TYPE_BR_SWITCH_TO \ 167 (PARAVIRT_PATCH_TYPE_BR_START + 0) 168#define PARAVIRT_PATCH_TYPE_BR_LEAVE_SYSCALL \ 169 (PARAVIRT_PATCH_TYPE_BR_START + 1) 170#define PARAVIRT_PATCH_TYPE_BR_WORK_PROCESSED_SYSCALL \ 171 (PARAVIRT_PATCH_TYPE_BR_START + 2) 172#define PARAVIRT_PATCH_TYPE_BR_LEAVE_KERNEL \ 173 (PARAVIRT_PATCH_TYPE_BR_START + 3) 174 175#ifdef ASM_SUPPORTED 176#include <asm/paravirt_patch.h> 177 178/* 179 * pv_cpu_ops calling stub. 180 * normal function call convension can't be written by gcc 181 * inline assembly. 182 * 183 * from the caller's point of view, 184 * the following registers will be clobbered. 185 * r2, r3 186 * r8-r15 187 * r16, r17 188 * b6, b7 189 * p6-p15 190 * ar.ccv 191 * 192 * from the callee's point of view , 193 * the following registers can be used. 194 * r2, r3: scratch 195 * r8: scratch, input argument0 and return value 196 * r0-r15: scratch, input argument1-5 197 * b6: return pointer 198 * b7: scratch 199 * p6-p15: scratch 200 * ar.ccv: scratch 201 * 202 * other registers must not be changed. especially 203 * b0: rp: preserved. gcc ignores b0 in clobbered register. 204 * r16: saved gp 205 */ 206/* 5 bundles */ 207#define __PARAVIRT_BR \ 208 ";;\n" \ 209 "{ .mlx\n" \ 210 "nop 0\n" \ 211 "movl r2 = %[op_addr]\n"/* get function pointer address */ \ 212 ";;\n" \ 213 "}\n" \ 214 "1:\n" \ 215 "{ .mii\n" \ 216 "ld8 r2 = [r2]\n" /* load function descriptor address */ \ 217 "mov r17 = ip\n" /* get ip to calc return address */ \ 218 "mov r16 = gp\n" /* save gp */ \ 219 ";;\n" \ 220 "}\n" \ 221 "{ .mii\n" \ 222 "ld8 r3 = [r2], 8\n" /* load entry address */ \ 223 "adds r17 = 1f - 1b, r17\n" /* calculate return address */ \ 224 ";;\n" \ 225 "mov b7 = r3\n" /* set entry address */ \ 226 "}\n" \ 227 "{ .mib\n" \ 228 "ld8 gp = [r2]\n" /* load gp value */ \ 229 "mov b6 = r17\n" /* set return address */ \ 230 "br.cond.sptk.few b7\n" /* intrinsics are very short isns */ \ 231 "}\n" \ 232 "1:\n" \ 233 "{ .mii\n" \ 234 "mov gp = r16\n" /* restore gp value */ \ 235 "nop 0\n" \ 236 "nop 0\n" \ 237 ";;\n" \ 238 "}\n" 239 240#define PARAVIRT_OP(op) \ 241 [op_addr] "i"(&pv_cpu_ops.op) 242 243#define PARAVIRT_TYPE(type) \ 244 PARAVIRT_PATCH_TYPE_ ## type 245 246#define PARAVIRT_REG_CLOBBERS0 \ 247 "r2", "r3", /*"r8",*/ "r9", "r10", "r11", "r14", \ 248 "r15", "r16", "r17" 249 250#define PARAVIRT_REG_CLOBBERS1 \ 251 "r2","r3", /*"r8",*/ "r9", "r10", "r11", "r14", \ 252 "r15", "r16", "r17" 253 254#define PARAVIRT_REG_CLOBBERS2 \ 255 "r2", "r3", /*"r8", "r9",*/ "r10", "r11", "r14", \ 256 "r15", "r16", "r17" 257 258#define PARAVIRT_REG_CLOBBERS5 \ 259 "r2", "r3", /*"r8", "r9", "r10", "r11", "r14",*/ \ 260 "r15", "r16", "r17" 261 262#define PARAVIRT_BR_CLOBBERS \ 263 "b6", "b7" 264 265#define PARAVIRT_PR_CLOBBERS \ 266 "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15" 267 268#define PARAVIRT_AR_CLOBBERS \ 269 "ar.ccv" 270 271#define PARAVIRT_CLOBBERS0 \ 272 PARAVIRT_REG_CLOBBERS0, \ 273 PARAVIRT_BR_CLOBBERS, \ 274 PARAVIRT_PR_CLOBBERS, \ 275 PARAVIRT_AR_CLOBBERS, \ 276 "memory" 277 278#define PARAVIRT_CLOBBERS1 \ 279 PARAVIRT_REG_CLOBBERS1, \ 280 PARAVIRT_BR_CLOBBERS, \ 281 PARAVIRT_PR_CLOBBERS, \ 282 PARAVIRT_AR_CLOBBERS, \ 283 "memory" 284 285#define PARAVIRT_CLOBBERS2 \ 286 PARAVIRT_REG_CLOBBERS2, \ 287 PARAVIRT_BR_CLOBBERS, \ 288 PARAVIRT_PR_CLOBBERS, \ 289 PARAVIRT_AR_CLOBBERS, \ 290 "memory" 291 292#define PARAVIRT_CLOBBERS5 \ 293 PARAVIRT_REG_CLOBBERS5, \ 294 PARAVIRT_BR_CLOBBERS, \ 295 PARAVIRT_PR_CLOBBERS, \ 296 PARAVIRT_AR_CLOBBERS, \ 297 "memory" 298 299#define PARAVIRT_BR0(op, type) \ 300 register unsigned long ia64_clobber asm ("r8"); \ 301 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 302 PARAVIRT_TYPE(type)) \ 303 : "=r"(ia64_clobber) \ 304 : PARAVIRT_OP(op) \ 305 : PARAVIRT_CLOBBERS0) 306 307#define PARAVIRT_BR0_RET(op, type) \ 308 register unsigned long ia64_intri_res asm ("r8"); \ 309 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 310 PARAVIRT_TYPE(type)) \ 311 : "=r"(ia64_intri_res) \ 312 : PARAVIRT_OP(op) \ 313 : PARAVIRT_CLOBBERS0) 314 315#define PARAVIRT_BR1(op, type, arg1) \ 316 register unsigned long __##arg1 asm ("r8") = arg1; \ 317 register unsigned long ia64_clobber asm ("r8"); \ 318 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 319 PARAVIRT_TYPE(type)) \ 320 : "=r"(ia64_clobber) \ 321 : PARAVIRT_OP(op), "0"(__##arg1) \ 322 : PARAVIRT_CLOBBERS1) 323 324#define PARAVIRT_BR1_RET(op, type, arg1) \ 325 register unsigned long ia64_intri_res asm ("r8"); \ 326 register unsigned long __##arg1 asm ("r8") = arg1; \ 327 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 328 PARAVIRT_TYPE(type)) \ 329 : "=r"(ia64_intri_res) \ 330 : PARAVIRT_OP(op), "0"(__##arg1) \ 331 : PARAVIRT_CLOBBERS1) 332 333#define PARAVIRT_BR1_VOID(op, type, arg1) \ 334 register void *__##arg1 asm ("r8") = arg1; \ 335 register unsigned long ia64_clobber asm ("r8"); \ 336 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 337 PARAVIRT_TYPE(type)) \ 338 : "=r"(ia64_clobber) \ 339 : PARAVIRT_OP(op), "0"(__##arg1) \ 340 : PARAVIRT_CLOBBERS1) 341 342#define PARAVIRT_BR2(op, type, arg1, arg2) \ 343 register unsigned long __##arg1 asm ("r8") = arg1; \ 344 register unsigned long __##arg2 asm ("r9") = arg2; \ 345 register unsigned long ia64_clobber1 asm ("r8"); \ 346 register unsigned long ia64_clobber2 asm ("r9"); \ 347 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 348 PARAVIRT_TYPE(type)) \ 349 : "=r"(ia64_clobber1), "=r"(ia64_clobber2) \ 350 : PARAVIRT_OP(op), "0"(__##arg1), "1"(__##arg2) \ 351 : PARAVIRT_CLOBBERS2) 352 353 354#define PARAVIRT_DEFINE_CPU_OP0(op, type) \ 355 static inline void \ 356 paravirt_ ## op (void) \ 357 { \ 358 PARAVIRT_BR0(op, type); \ 359 } 360 361#define PARAVIRT_DEFINE_CPU_OP0_RET(op, type) \ 362 static inline unsigned long \ 363 paravirt_ ## op (void) \ 364 { \ 365 PARAVIRT_BR0_RET(op, type); \ 366 return ia64_intri_res; \ 367 } 368 369#define PARAVIRT_DEFINE_CPU_OP1_VOID(op, type) \ 370 static inline void \ 371 paravirt_ ## op (void *arg1) \ 372 { \ 373 PARAVIRT_BR1_VOID(op, type, arg1); \ 374 } 375 376#define PARAVIRT_DEFINE_CPU_OP1(op, type) \ 377 static inline void \ 378 paravirt_ ## op (unsigned long arg1) \ 379 { \ 380 PARAVIRT_BR1(op, type, arg1); \ 381 } 382 383#define PARAVIRT_DEFINE_CPU_OP1_RET(op, type) \ 384 static inline unsigned long \ 385 paravirt_ ## op (unsigned long arg1) \ 386 { \ 387 PARAVIRT_BR1_RET(op, type, arg1); \ 388 return ia64_intri_res; \ 389 } 390 391#define PARAVIRT_DEFINE_CPU_OP2(op, type) \ 392 static inline void \ 393 paravirt_ ## op (unsigned long arg1, \ 394 unsigned long arg2) \ 395 { \ 396 PARAVIRT_BR2(op, type, arg1, arg2); \ 397 } 398 399 400PARAVIRT_DEFINE_CPU_OP1_VOID(fc, FC); 401PARAVIRT_DEFINE_CPU_OP1_RET(thash, THASH) 402PARAVIRT_DEFINE_CPU_OP1_RET(get_cpuid, GET_CPUID) 403PARAVIRT_DEFINE_CPU_OP1_RET(get_pmd, GET_PMD) 404PARAVIRT_DEFINE_CPU_OP2(ptcga, PTCGA) 405PARAVIRT_DEFINE_CPU_OP1_RET(get_rr, GET_RR) 406PARAVIRT_DEFINE_CPU_OP2(set_rr, SET_RR) 407PARAVIRT_DEFINE_CPU_OP0(ssm_i, SSM_I) 408PARAVIRT_DEFINE_CPU_OP0(rsm_i, RSM_I) 409PARAVIRT_DEFINE_CPU_OP0_RET(get_psr_i, GET_PSR_I) 410PARAVIRT_DEFINE_CPU_OP1(intrin_local_irq_restore, INTRIN_LOCAL_IRQ_RESTORE) 411 412static inline void 413paravirt_set_rr0_to_rr4(unsigned long val0, unsigned long val1, 414 unsigned long val2, unsigned long val3, 415 unsigned long val4) 416{ 417 register unsigned long __val0 asm ("r8") = val0; 418 register unsigned long __val1 asm ("r9") = val1; 419 register unsigned long __val2 asm ("r10") = val2; 420 register unsigned long __val3 asm ("r11") = val3; 421 register unsigned long __val4 asm ("r14") = val4; 422 423 register unsigned long ia64_clobber0 asm ("r8"); 424 register unsigned long ia64_clobber1 asm ("r9"); 425 register unsigned long ia64_clobber2 asm ("r10"); 426 register unsigned long ia64_clobber3 asm ("r11"); 427 register unsigned long ia64_clobber4 asm ("r14"); 428 429 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, 430 PARAVIRT_TYPE(SET_RR0_TO_RR4)) 431 : "=r"(ia64_clobber0), 432 "=r"(ia64_clobber1), 433 "=r"(ia64_clobber2), 434 "=r"(ia64_clobber3), 435 "=r"(ia64_clobber4) 436 : PARAVIRT_OP(set_rr0_to_rr4), 437 "0"(__val0), "1"(__val1), "2"(__val2), 438 "3"(__val3), "4"(__val4) 439 : PARAVIRT_CLOBBERS5); 440} 441 442/* unsigned long paravirt_getreg(int reg) */ 443#define __paravirt_getreg(reg) \ 444 ({ \ 445 register unsigned long ia64_intri_res asm ("r8"); \ 446 register unsigned long __reg asm ("r8") = (reg); \ 447 \ 448 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 449 PARAVIRT_TYPE(GETREG) \ 450 + (reg)) \ 451 : "=r"(ia64_intri_res) \ 452 : PARAVIRT_OP(getreg), "0"(__reg) \ 453 : PARAVIRT_CLOBBERS1); \ 454 \ 455 ia64_intri_res; \ 456 }) 457 458/* void paravirt_setreg(int reg, unsigned long val) */ 459#define paravirt_setreg(reg, val) \ 460 do { \ 461 register unsigned long __val asm ("r8") = val; \ 462 register unsigned long __reg asm ("r9") = reg; \ 463 register unsigned long ia64_clobber1 asm ("r8"); \ 464 register unsigned long ia64_clobber2 asm ("r9"); \ 465 \ 466 asm volatile (paravirt_alt_bundle(__PARAVIRT_BR, \ 467 PARAVIRT_TYPE(SETREG) \ 468 + (reg)) \ 469 : "=r"(ia64_clobber1), \ 470 "=r"(ia64_clobber2) \ 471 : PARAVIRT_OP(setreg), \ 472 "1"(__reg), "0"(__val) \ 473 : PARAVIRT_CLOBBERS2); \ 474 } while (0) 475 476#endif /* ASM_SUPPORTED */ 477#endif /* CONFIG_PARAVIRT && ASM_SUPPOTED */ 478 479#endif /* _ASM_IA64_PARAVIRT_PRIVOP_H */ 480