ia64/linux-2.6.18-xen.hg

annotate arch/ia64/xen/hypervisor.c @ 26:a533be77c572

Imported linux-2.6-xen-sparse from xen-unstable.hg 15200:bd3d6b4c52ec
author Ian Campbell <ian.campbell@xensource.com>
date Mon Jun 04 10:05:28 2007 +0100 (2007-06-04)
parents
children 245902ee7ce0
rev   line source
ian@26 1 /******************************************************************************
ian@26 2 * include/asm-ia64/shadow.h
ian@26 3 *
ian@26 4 * Copyright (c) 2006 Isaku Yamahata <yamahata at valinux co jp>
ian@26 5 * VA Linux Systems Japan K.K.
ian@26 6 *
ian@26 7 * This program is free software; you can redistribute it and/or modify
ian@26 8 * it under the terms of the GNU General Public License as published by
ian@26 9 * the Free Software Foundation; either version 2 of the License, or
ian@26 10 * (at your option) any later version.
ian@26 11 *
ian@26 12 * This program is distributed in the hope that it will be useful,
ian@26 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ian@26 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
ian@26 15 * GNU General Public License for more details.
ian@26 16 *
ian@26 17 * You should have received a copy of the GNU General Public License
ian@26 18 * along with this program; if not, write to the Free Software
ian@26 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
ian@26 20 *
ian@26 21 */
ian@26 22
ian@26 23 //#include <linux/kernel.h>
ian@26 24 #include <linux/spinlock.h>
ian@26 25 #include <linux/bootmem.h>
ian@26 26 #include <linux/module.h>
ian@26 27 #include <linux/vmalloc.h>
ian@26 28 #include <linux/efi.h>
ian@26 29 #include <asm/page.h>
ian@26 30 #include <asm/pgalloc.h>
ian@26 31 #include <asm/meminit.h>
ian@26 32 #include <asm/hypervisor.h>
ian@26 33 #include <asm/hypercall.h>
ian@26 34 #include <xen/interface/memory.h>
ian@26 35 #include <xen/xencons.h>
ian@26 36 #include <xen/balloon.h>
ian@26 37
ian@26 38 shared_info_t *HYPERVISOR_shared_info = (shared_info_t *)XSI_BASE;
ian@26 39 EXPORT_SYMBOL(HYPERVISOR_shared_info);
ian@26 40
ian@26 41 start_info_t *xen_start_info;
ian@26 42 EXPORT_SYMBOL(xen_start_info);
ian@26 43
ian@26 44 int running_on_xen;
ian@26 45 EXPORT_SYMBOL(running_on_xen);
ian@26 46
ian@26 47 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M
ian@26 48 static int p2m_expose_init(void);
ian@26 49 #else
ian@26 50 #define p2m_expose_init() (-ENOSYS)
ian@26 51 #define p2m_expose_resume() ((void)0)
ian@26 52 #endif
ian@26 53
ian@26 54 EXPORT_SYMBOL(__hypercall);
ian@26 55
ian@26 56 void __init
ian@26 57 xen_setup(char **cmdline_p)
ian@26 58 {
ian@26 59 extern void dig_setup(char **cmdline_p);
ian@26 60 if (ia64_platform_is("xen"))
ian@26 61 dig_setup(cmdline_p);
ian@26 62
ian@26 63 if (!is_running_on_xen() || !is_initial_xendomain())
ian@26 64 return;
ian@26 65
ian@26 66 if (xen_start_info->console.dom0.info_size >=
ian@26 67 sizeof(struct dom0_vga_console_info)) {
ian@26 68 const struct dom0_vga_console_info *info =
ian@26 69 (struct dom0_vga_console_info *)(
ian@26 70 (char *)xen_start_info +
ian@26 71 xen_start_info->console.dom0.info_off);
ian@26 72 dom0_init_screen_info(info);
ian@26 73 }
ian@26 74 xen_start_info->console.domU.mfn = 0;
ian@26 75 xen_start_info->console.domU.evtchn = 0;
ian@26 76 }
ian@26 77
ian@26 78 void __cpuinit
ian@26 79 xen_cpu_init(void)
ian@26 80 {
ian@26 81 extern void xen_smp_intr_init(void);
ian@26 82 xen_smp_intr_init();
ian@26 83 }
ian@26 84
ian@26 85 //XXX same as i386, x86_64 contiguous_bitmap_set(), contiguous_bitmap_clear()
ian@26 86 // move those to lib/contiguous_bitmap?
ian@26 87 //XXX discontigmem/sparsemem
ian@26 88
ian@26 89 /*
ian@26 90 * Bitmap is indexed by page number. If bit is set, the page is part of a
ian@26 91 * xen_create_contiguous_region() area of memory.
ian@26 92 */
ian@26 93 unsigned long *contiguous_bitmap;
ian@26 94
ian@26 95 #ifdef CONFIG_VIRTUAL_MEM_MAP
ian@26 96 /* Following logic is stolen from create_mem_map_table() for virtual memmap */
ian@26 97 static int
ian@26 98 create_contiguous_bitmap(u64 start, u64 end, void *arg)
ian@26 99 {
ian@26 100 unsigned long address, start_page, end_page;
ian@26 101 unsigned long bitmap_start, bitmap_end;
ian@26 102 unsigned char *bitmap;
ian@26 103 int node;
ian@26 104 pgd_t *pgd;
ian@26 105 pud_t *pud;
ian@26 106 pmd_t *pmd;
ian@26 107 pte_t *pte;
ian@26 108
ian@26 109 bitmap_start = (unsigned long)contiguous_bitmap +
ian@26 110 ((__pa(start) >> PAGE_SHIFT) >> 3);
ian@26 111 bitmap_end = (unsigned long)contiguous_bitmap +
ian@26 112 (((__pa(end) >> PAGE_SHIFT) + 2 * BITS_PER_LONG) >> 3);
ian@26 113
ian@26 114 start_page = bitmap_start & PAGE_MASK;
ian@26 115 end_page = PAGE_ALIGN(bitmap_end);
ian@26 116 node = paddr_to_nid(__pa(start));
ian@26 117
ian@26 118 bitmap = alloc_bootmem_pages_node(NODE_DATA(node),
ian@26 119 end_page - start_page);
ian@26 120 BUG_ON(!bitmap);
ian@26 121 memset(bitmap, 0, end_page - start_page);
ian@26 122
ian@26 123 for (address = start_page; address < end_page; address += PAGE_SIZE) {
ian@26 124 pgd = pgd_offset_k(address);
ian@26 125 if (pgd_none(*pgd))
ian@26 126 pgd_populate(&init_mm, pgd,
ian@26 127 alloc_bootmem_pages_node(NODE_DATA(node),
ian@26 128 PAGE_SIZE));
ian@26 129 pud = pud_offset(pgd, address);
ian@26 130
ian@26 131 if (pud_none(*pud))
ian@26 132 pud_populate(&init_mm, pud,
ian@26 133 alloc_bootmem_pages_node(NODE_DATA(node),
ian@26 134 PAGE_SIZE));
ian@26 135 pmd = pmd_offset(pud, address);
ian@26 136
ian@26 137 if (pmd_none(*pmd))
ian@26 138 pmd_populate_kernel(&init_mm, pmd,
ian@26 139 alloc_bootmem_pages_node
ian@26 140 (NODE_DATA(node), PAGE_SIZE));
ian@26 141 pte = pte_offset_kernel(pmd, address);
ian@26 142
ian@26 143 if (pte_none(*pte))
ian@26 144 set_pte(pte,
ian@26 145 pfn_pte(__pa(bitmap + (address - start_page))
ian@26 146 >> PAGE_SHIFT, PAGE_KERNEL));
ian@26 147 }
ian@26 148 return 0;
ian@26 149 }
ian@26 150 #endif
ian@26 151
ian@26 152 static void
ian@26 153 __contiguous_bitmap_init(unsigned long size)
ian@26 154 {
ian@26 155 contiguous_bitmap = alloc_bootmem_pages(size);
ian@26 156 BUG_ON(!contiguous_bitmap);
ian@26 157 memset(contiguous_bitmap, 0, size);
ian@26 158 }
ian@26 159
ian@26 160 void
ian@26 161 contiguous_bitmap_init(unsigned long end_pfn)
ian@26 162 {
ian@26 163 unsigned long size = (end_pfn + 2 * BITS_PER_LONG) >> 3;
ian@26 164 #ifndef CONFIG_VIRTUAL_MEM_MAP
ian@26 165 __contiguous_bitmap_init(size);
ian@26 166 #else
ian@26 167 unsigned long max_gap = 0;
ian@26 168
ian@26 169 efi_memmap_walk(find_largest_hole, (u64*)&max_gap);
ian@26 170 if (max_gap < LARGE_GAP) {
ian@26 171 __contiguous_bitmap_init(size);
ian@26 172 } else {
ian@26 173 unsigned long map_size = PAGE_ALIGN(size);
ian@26 174 vmalloc_end -= map_size;
ian@26 175 contiguous_bitmap = (unsigned long*)vmalloc_end;
ian@26 176 efi_memmap_walk(create_contiguous_bitmap, NULL);
ian@26 177 }
ian@26 178 #endif
ian@26 179 }
ian@26 180
ian@26 181 #if 0
ian@26 182 int
ian@26 183 contiguous_bitmap_test(void* p)
ian@26 184 {
ian@26 185 return test_bit(__pa(p) >> PAGE_SHIFT, contiguous_bitmap);
ian@26 186 }
ian@26 187 #endif
ian@26 188
ian@26 189 static void contiguous_bitmap_set(
ian@26 190 unsigned long first_page, unsigned long nr_pages)
ian@26 191 {
ian@26 192 unsigned long start_off, end_off, curr_idx, end_idx;
ian@26 193
ian@26 194 curr_idx = first_page / BITS_PER_LONG;
ian@26 195 start_off = first_page & (BITS_PER_LONG-1);
ian@26 196 end_idx = (first_page + nr_pages) / BITS_PER_LONG;
ian@26 197 end_off = (first_page + nr_pages) & (BITS_PER_LONG-1);
ian@26 198
ian@26 199 if (curr_idx == end_idx) {
ian@26 200 contiguous_bitmap[curr_idx] |=
ian@26 201 ((1UL<<end_off)-1) & -(1UL<<start_off);
ian@26 202 } else {
ian@26 203 contiguous_bitmap[curr_idx] |= -(1UL<<start_off);
ian@26 204 while ( ++curr_idx < end_idx )
ian@26 205 contiguous_bitmap[curr_idx] = ~0UL;
ian@26 206 contiguous_bitmap[curr_idx] |= (1UL<<end_off)-1;
ian@26 207 }
ian@26 208 }
ian@26 209
ian@26 210 static void contiguous_bitmap_clear(
ian@26 211 unsigned long first_page, unsigned long nr_pages)
ian@26 212 {
ian@26 213 unsigned long start_off, end_off, curr_idx, end_idx;
ian@26 214
ian@26 215 curr_idx = first_page / BITS_PER_LONG;
ian@26 216 start_off = first_page & (BITS_PER_LONG-1);
ian@26 217 end_idx = (first_page + nr_pages) / BITS_PER_LONG;
ian@26 218 end_off = (first_page + nr_pages) & (BITS_PER_LONG-1);
ian@26 219
ian@26 220 if (curr_idx == end_idx) {
ian@26 221 contiguous_bitmap[curr_idx] &=
ian@26 222 -(1UL<<end_off) | ((1UL<<start_off)-1);
ian@26 223 } else {
ian@26 224 contiguous_bitmap[curr_idx] &= (1UL<<start_off)-1;
ian@26 225 while ( ++curr_idx != end_idx )
ian@26 226 contiguous_bitmap[curr_idx] = 0;
ian@26 227 contiguous_bitmap[curr_idx] &= -(1UL<<end_off);
ian@26 228 }
ian@26 229 }
ian@26 230
ian@26 231 // __xen_create_contiguous_region(), __xen_destroy_contiguous_region()
ian@26 232 // are based on i386 xen_create_contiguous_region(),
ian@26 233 // xen_destroy_contiguous_region()
ian@26 234
ian@26 235 /* Protected by balloon_lock. */
ian@26 236 #define MAX_CONTIG_ORDER 7
ian@26 237 static unsigned long discontig_frames[1<<MAX_CONTIG_ORDER];
ian@26 238
ian@26 239 /* Ensure multi-page extents are contiguous in machine memory. */
ian@26 240 int
ian@26 241 __xen_create_contiguous_region(unsigned long vstart,
ian@26 242 unsigned int order, unsigned int address_bits)
ian@26 243 {
ian@26 244 unsigned long error = 0;
ian@26 245 unsigned long gphys = __pa(vstart);
ian@26 246 unsigned long start_gpfn = gphys >> PAGE_SHIFT;
ian@26 247 unsigned long num_gpfn = 1 << order;
ian@26 248 unsigned long i;
ian@26 249 unsigned long flags;
ian@26 250
ian@26 251 unsigned long *in_frames = discontig_frames, out_frame;
ian@26 252 int success;
ian@26 253 struct xen_memory_exchange exchange = {
ian@26 254 .in = {
ian@26 255 .nr_extents = num_gpfn,
ian@26 256 .extent_order = 0,
ian@26 257 .domid = DOMID_SELF
ian@26 258 },
ian@26 259 .out = {
ian@26 260 .nr_extents = 1,
ian@26 261 .extent_order = order,
ian@26 262 .address_bits = address_bits,
ian@26 263 .domid = DOMID_SELF
ian@26 264 },
ian@26 265 .nr_exchanged = 0
ian@26 266 };
ian@26 267
ian@26 268 if (unlikely(order > MAX_CONTIG_ORDER))
ian@26 269 return -ENOMEM;
ian@26 270
ian@26 271 set_xen_guest_handle(exchange.in.extent_start, in_frames);
ian@26 272 set_xen_guest_handle(exchange.out.extent_start, &out_frame);
ian@26 273
ian@26 274 scrub_pages(vstart, num_gpfn);
ian@26 275
ian@26 276 balloon_lock(flags);
ian@26 277
ian@26 278 /* Get a new contiguous memory extent. */
ian@26 279 for (i = 0; i < num_gpfn; i++) {
ian@26 280 in_frames[i] = start_gpfn + i;
ian@26 281 }
ian@26 282 out_frame = start_gpfn;
ian@26 283 error = HYPERVISOR_memory_op(XENMEM_exchange, &exchange);
ian@26 284 success = (exchange.nr_exchanged == num_gpfn);
ian@26 285 BUG_ON(!success && ((exchange.nr_exchanged != 0) || (error == 0)));
ian@26 286 BUG_ON(success && (error != 0));
ian@26 287 if (unlikely(error == -ENOSYS)) {
ian@26 288 /* Compatibility when XENMEM_exchange is unsupported. */
ian@26 289 error = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
ian@26 290 &exchange.in);
ian@26 291 BUG_ON(error != num_gpfn);
ian@26 292 error = HYPERVISOR_memory_op(XENMEM_populate_physmap,
ian@26 293 &exchange.out);
ian@26 294 if (error != 1) {
ian@26 295 /* Couldn't get special memory: fall back to normal. */
ian@26 296 for (i = 0; i < num_gpfn; i++) {
ian@26 297 in_frames[i] = start_gpfn + i;
ian@26 298 }
ian@26 299 error = HYPERVISOR_memory_op(XENMEM_populate_physmap,
ian@26 300 &exchange.in);
ian@26 301 BUG_ON(error != num_gpfn);
ian@26 302 success = 0;
ian@26 303 } else
ian@26 304 success = 1;
ian@26 305 }
ian@26 306 if (success)
ian@26 307 contiguous_bitmap_set(start_gpfn, num_gpfn);
ian@26 308 #if 0
ian@26 309 if (success) {
ian@26 310 unsigned long mfn;
ian@26 311 unsigned long mfn_prev = ~0UL;
ian@26 312 for (i = 0; i < num_gpfn; i++) {
ian@26 313 mfn = pfn_to_mfn_for_dma(start_gpfn + i);
ian@26 314 if (mfn_prev != ~0UL && mfn != mfn_prev + 1) {
ian@26 315 xprintk("\n");
ian@26 316 xprintk("%s:%d order %d "
ian@26 317 "start 0x%lx bus 0x%lx "
ian@26 318 "machine 0x%lx\n",
ian@26 319 __func__, __LINE__, order,
ian@26 320 vstart, virt_to_bus((void*)vstart),
ian@26 321 phys_to_machine_for_dma(gphys));
ian@26 322 xprintk("mfn: ");
ian@26 323 for (i = 0; i < num_gpfn; i++) {
ian@26 324 mfn = pfn_to_mfn_for_dma(
ian@26 325 start_gpfn + i);
ian@26 326 xprintk("0x%lx ", mfn);
ian@26 327 }
ian@26 328 xprintk("\n");
ian@26 329 break;
ian@26 330 }
ian@26 331 mfn_prev = mfn;
ian@26 332 }
ian@26 333 }
ian@26 334 #endif
ian@26 335 balloon_unlock(flags);
ian@26 336 return success? 0: -ENOMEM;
ian@26 337 }
ian@26 338
ian@26 339 void
ian@26 340 __xen_destroy_contiguous_region(unsigned long vstart, unsigned int order)
ian@26 341 {
ian@26 342 unsigned long flags;
ian@26 343 unsigned long error = 0;
ian@26 344 unsigned long start_gpfn = __pa(vstart) >> PAGE_SHIFT;
ian@26 345 unsigned long num_gpfn = 1UL << order;
ian@26 346 unsigned long i;
ian@26 347
ian@26 348 unsigned long *out_frames = discontig_frames, in_frame;
ian@26 349 int success;
ian@26 350 struct xen_memory_exchange exchange = {
ian@26 351 .in = {
ian@26 352 .nr_extents = 1,
ian@26 353 .extent_order = order,
ian@26 354 .domid = DOMID_SELF
ian@26 355 },
ian@26 356 .out = {
ian@26 357 .nr_extents = num_gpfn,
ian@26 358 .extent_order = 0,
ian@26 359 .address_bits = 0,
ian@26 360 .domid = DOMID_SELF
ian@26 361 },
ian@26 362 .nr_exchanged = 0
ian@26 363 };
ian@26 364
ian@26 365
ian@26 366 if (!test_bit(start_gpfn, contiguous_bitmap))
ian@26 367 return;
ian@26 368
ian@26 369 if (unlikely(order > MAX_CONTIG_ORDER))
ian@26 370 return;
ian@26 371
ian@26 372 set_xen_guest_handle(exchange.in.extent_start, &in_frame);
ian@26 373 set_xen_guest_handle(exchange.out.extent_start, out_frames);
ian@26 374
ian@26 375 scrub_pages(vstart, num_gpfn);
ian@26 376
ian@26 377 balloon_lock(flags);
ian@26 378
ian@26 379 contiguous_bitmap_clear(start_gpfn, num_gpfn);
ian@26 380
ian@26 381 /* Do the exchange for non-contiguous MFNs. */
ian@26 382 in_frame = start_gpfn;
ian@26 383 for (i = 0; i < num_gpfn; i++) {
ian@26 384 out_frames[i] = start_gpfn + i;
ian@26 385 }
ian@26 386 error = HYPERVISOR_memory_op(XENMEM_exchange, &exchange);
ian@26 387 success = (exchange.nr_exchanged == 1);
ian@26 388 BUG_ON(!success && ((exchange.nr_exchanged != 0) || (error == 0)));
ian@26 389 BUG_ON(success && (error != 0));
ian@26 390 if (unlikely(error == -ENOSYS)) {
ian@26 391 /* Compatibility when XENMEM_exchange is unsupported. */
ian@26 392 error = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
ian@26 393 &exchange.in);
ian@26 394 BUG_ON(error != 1);
ian@26 395
ian@26 396 error = HYPERVISOR_memory_op(XENMEM_populate_physmap,
ian@26 397 &exchange.out);
ian@26 398 BUG_ON(error != num_gpfn);
ian@26 399 }
ian@26 400 balloon_unlock(flags);
ian@26 401 }
ian@26 402
ian@26 403
ian@26 404 ///////////////////////////////////////////////////////////////////////////
ian@26 405 // grant table hack
ian@26 406 // cmd: GNTTABOP_xxx
ian@26 407
ian@26 408 #include <linux/mm.h>
ian@26 409 #include <xen/interface/xen.h>
ian@26 410 #include <xen/gnttab.h>
ian@26 411
ian@26 412 static void
ian@26 413 gnttab_map_grant_ref_pre(struct gnttab_map_grant_ref *uop)
ian@26 414 {
ian@26 415 uint32_t flags;
ian@26 416
ian@26 417 flags = uop->flags;
ian@26 418
ian@26 419 if (flags & GNTMAP_host_map) {
ian@26 420 if (flags & GNTMAP_application_map) {
ian@26 421 xprintd("GNTMAP_application_map is not supported yet: flags 0x%x\n", flags);
ian@26 422 BUG();
ian@26 423 }
ian@26 424 if (flags & GNTMAP_contains_pte) {
ian@26 425 xprintd("GNTMAP_contains_pte is not supported yet flags 0x%x\n", flags);
ian@26 426 BUG();
ian@26 427 }
ian@26 428 } else if (flags & GNTMAP_device_map) {
ian@26 429 xprintd("GNTMAP_device_map is not supported yet 0x%x\n", flags);
ian@26 430 BUG();//XXX not yet. actually this flag is not used.
ian@26 431 } else {
ian@26 432 BUG();
ian@26 433 }
ian@26 434 }
ian@26 435
ian@26 436 int
ian@26 437 HYPERVISOR_grant_table_op(unsigned int cmd, void *uop, unsigned int count)
ian@26 438 {
ian@26 439 if (cmd == GNTTABOP_map_grant_ref) {
ian@26 440 unsigned int i;
ian@26 441 for (i = 0; i < count; i++) {
ian@26 442 gnttab_map_grant_ref_pre(
ian@26 443 (struct gnttab_map_grant_ref*)uop + i);
ian@26 444 }
ian@26 445 }
ian@26 446 return xencomm_mini_hypercall_grant_table_op(cmd, uop, count);
ian@26 447 }
ian@26 448 EXPORT_SYMBOL(HYPERVISOR_grant_table_op);
ian@26 449
ian@26 450 ///////////////////////////////////////////////////////////////////////////
ian@26 451 // foreign mapping
ian@26 452 #include <linux/efi.h>
ian@26 453 #include <asm/meminit.h> // for IA64_GRANULE_SIZE, GRANULEROUND{UP,DOWN}()
ian@26 454
ian@26 455 static unsigned long privcmd_resource_min = 0;
ian@26 456 // Xen/ia64 currently can handle pseudo physical address bits up to
ian@26 457 // (PAGE_SHIFT * 3)
ian@26 458 static unsigned long privcmd_resource_max = GRANULEROUNDDOWN((1UL << (PAGE_SHIFT * 3)) - 1);
ian@26 459 static unsigned long privcmd_resource_align = IA64_GRANULE_SIZE;
ian@26 460
ian@26 461 static unsigned long
ian@26 462 md_end_addr(const efi_memory_desc_t *md)
ian@26 463 {
ian@26 464 return md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT);
ian@26 465 }
ian@26 466
ian@26 467 #define XEN_IA64_PRIVCMD_LEAST_GAP_SIZE (1024 * 1024 * 1024UL)
ian@26 468 static int
ian@26 469 xen_ia64_privcmd_check_size(unsigned long start, unsigned long end)
ian@26 470 {
ian@26 471 return (start < end &&
ian@26 472 (end - start) > XEN_IA64_PRIVCMD_LEAST_GAP_SIZE);
ian@26 473 }
ian@26 474
ian@26 475 static int __init
ian@26 476 xen_ia64_privcmd_init(void)
ian@26 477 {
ian@26 478 void *efi_map_start, *efi_map_end, *p;
ian@26 479 u64 efi_desc_size;
ian@26 480 efi_memory_desc_t *md;
ian@26 481 unsigned long tmp_min;
ian@26 482 unsigned long tmp_max;
ian@26 483 unsigned long gap_size;
ian@26 484 unsigned long prev_end;
ian@26 485
ian@26 486 if (!is_running_on_xen())
ian@26 487 return -1;
ian@26 488
ian@26 489 efi_map_start = __va(ia64_boot_param->efi_memmap);
ian@26 490 efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size;
ian@26 491 efi_desc_size = ia64_boot_param->efi_memdesc_size;
ian@26 492
ian@26 493 // at first check the used highest address
ian@26 494 for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
ian@26 495 // nothing
ian@26 496 }
ian@26 497 md = p - efi_desc_size;
ian@26 498 privcmd_resource_min = GRANULEROUNDUP(md_end_addr(md));
ian@26 499 if (xen_ia64_privcmd_check_size(privcmd_resource_min,
ian@26 500 privcmd_resource_max)) {
ian@26 501 goto out;
ian@26 502 }
ian@26 503
ian@26 504 // the used highest address is too large. try to find the largest gap.
ian@26 505 tmp_min = privcmd_resource_max;
ian@26 506 tmp_max = 0;
ian@26 507 gap_size = 0;
ian@26 508 prev_end = 0;
ian@26 509 for (p = efi_map_start;
ian@26 510 p < efi_map_end - efi_desc_size;
ian@26 511 p += efi_desc_size) {
ian@26 512 unsigned long end;
ian@26 513 efi_memory_desc_t* next;
ian@26 514 unsigned long next_start;
ian@26 515
ian@26 516 md = p;
ian@26 517 end = md_end_addr(md);
ian@26 518 if (end > privcmd_resource_max) {
ian@26 519 break;
ian@26 520 }
ian@26 521 if (end < prev_end) {
ian@26 522 // work around.
ian@26 523 // Xen may pass incompletely sorted memory
ian@26 524 // descriptors like
ian@26 525 // [x, x + length]
ian@26 526 // [x, x]
ian@26 527 // this order should be reversed.
ian@26 528 continue;
ian@26 529 }
ian@26 530 next = p + efi_desc_size;
ian@26 531 next_start = next->phys_addr;
ian@26 532 if (next_start > privcmd_resource_max) {
ian@26 533 next_start = privcmd_resource_max;
ian@26 534 }
ian@26 535 if (end < next_start && gap_size < (next_start - end)) {
ian@26 536 tmp_min = end;
ian@26 537 tmp_max = next_start;
ian@26 538 gap_size = tmp_max - tmp_min;
ian@26 539 }
ian@26 540 prev_end = end;
ian@26 541 }
ian@26 542
ian@26 543 privcmd_resource_min = GRANULEROUNDUP(tmp_min);
ian@26 544 if (xen_ia64_privcmd_check_size(privcmd_resource_min, tmp_max)) {
ian@26 545 privcmd_resource_max = tmp_max;
ian@26 546 goto out;
ian@26 547 }
ian@26 548
ian@26 549 privcmd_resource_min = tmp_min;
ian@26 550 privcmd_resource_max = tmp_max;
ian@26 551 if (!xen_ia64_privcmd_check_size(privcmd_resource_min,
ian@26 552 privcmd_resource_max)) {
ian@26 553 // Any large enough gap isn't found.
ian@26 554 // go ahead anyway with the warning hoping that large region
ian@26 555 // won't be requested.
ian@26 556 printk(KERN_WARNING "xen privcmd: large enough region for privcmd mmap is not found.\n");
ian@26 557 }
ian@26 558
ian@26 559 out:
ian@26 560 printk(KERN_INFO "xen privcmd uses pseudo physical addr range [0x%lx, 0x%lx] (%ldMB)\n",
ian@26 561 privcmd_resource_min, privcmd_resource_max,
ian@26 562 (privcmd_resource_max - privcmd_resource_min) >> 20);
ian@26 563 BUG_ON(privcmd_resource_min >= privcmd_resource_max);
ian@26 564
ian@26 565 // XXX this should be somewhere appropriate
ian@26 566 (void)p2m_expose_init();
ian@26 567
ian@26 568 return 0;
ian@26 569 }
ian@26 570 late_initcall(xen_ia64_privcmd_init);
ian@26 571
ian@26 572 struct xen_ia64_privcmd_entry {
ian@26 573 atomic_t map_count;
ian@26 574 #define INVALID_GPFN (~0UL)
ian@26 575 unsigned long gpfn;
ian@26 576 };
ian@26 577
ian@26 578 struct xen_ia64_privcmd_range {
ian@26 579 atomic_t ref_count;
ian@26 580 unsigned long pgoff; // in PAGE_SIZE
ian@26 581 struct resource* res;
ian@26 582
ian@26 583 unsigned long num_entries;
ian@26 584 struct xen_ia64_privcmd_entry entries[0];
ian@26 585 };
ian@26 586
ian@26 587 struct xen_ia64_privcmd_vma {
ian@26 588 int is_privcmd_mmapped;
ian@26 589 struct xen_ia64_privcmd_range* range;
ian@26 590
ian@26 591 unsigned long num_entries;
ian@26 592 struct xen_ia64_privcmd_entry* entries;
ian@26 593 };
ian@26 594
ian@26 595 static void
ian@26 596 xen_ia64_privcmd_init_entry(struct xen_ia64_privcmd_entry* entry)
ian@26 597 {
ian@26 598 atomic_set(&entry->map_count, 0);
ian@26 599 entry->gpfn = INVALID_GPFN;
ian@26 600 }
ian@26 601
ian@26 602 static int
ian@26 603 xen_ia64_privcmd_entry_mmap(struct vm_area_struct* vma,
ian@26 604 unsigned long addr,
ian@26 605 struct xen_ia64_privcmd_range* privcmd_range,
ian@26 606 int i,
ian@26 607 unsigned long gmfn,
ian@26 608 pgprot_t prot,
ian@26 609 domid_t domid)
ian@26 610 {
ian@26 611 int error = 0;
ian@26 612 struct xen_ia64_privcmd_entry* entry = &privcmd_range->entries[i];
ian@26 613 unsigned long gpfn;
ian@26 614 unsigned long flags;
ian@26 615
ian@26 616 if ((addr & ~PAGE_MASK) != 0 || gmfn == INVALID_MFN) {
ian@26 617 error = -EINVAL;
ian@26 618 goto out;
ian@26 619 }
ian@26 620
ian@26 621 if (entry->gpfn != INVALID_GPFN) {
ian@26 622 error = -EBUSY;
ian@26 623 goto out;
ian@26 624 }
ian@26 625 gpfn = (privcmd_range->res->start >> PAGE_SHIFT) + i;
ian@26 626
ian@26 627 flags = ASSIGN_writable;
ian@26 628 if (pgprot_val(prot) == PROT_READ) {
ian@26 629 flags = ASSIGN_readonly;
ian@26 630 }
ian@26 631 error = HYPERVISOR_add_physmap_with_gmfn(gpfn, gmfn, flags, domid);
ian@26 632 if (error != 0) {
ian@26 633 goto out;
ian@26 634 }
ian@26 635
ian@26 636 prot = vma->vm_page_prot;
ian@26 637 error = remap_pfn_range(vma, addr, gpfn, 1 << PAGE_SHIFT, prot);
ian@26 638 if (error != 0) {
ian@26 639 error = HYPERVISOR_zap_physmap(gpfn, 0);
ian@26 640 if (error) {
ian@26 641 BUG();//XXX
ian@26 642 }
ian@26 643 } else {
ian@26 644 atomic_inc(&entry->map_count);
ian@26 645 entry->gpfn = gpfn;
ian@26 646 }
ian@26 647
ian@26 648 out:
ian@26 649 return error;
ian@26 650 }
ian@26 651
ian@26 652 static void
ian@26 653 xen_ia64_privcmd_entry_munmap(struct xen_ia64_privcmd_range* privcmd_range,
ian@26 654 int i)
ian@26 655 {
ian@26 656 struct xen_ia64_privcmd_entry* entry = &privcmd_range->entries[i];
ian@26 657 unsigned long gpfn = entry->gpfn;
ian@26 658 //gpfn = (privcmd_range->res->start >> PAGE_SHIFT) +
ian@26 659 // (vma->vm_pgoff - privcmd_range->pgoff);
ian@26 660 int error;
ian@26 661
ian@26 662 error = HYPERVISOR_zap_physmap(gpfn, 0);
ian@26 663 if (error) {
ian@26 664 BUG();//XXX
ian@26 665 }
ian@26 666 entry->gpfn = INVALID_GPFN;
ian@26 667 }
ian@26 668
ian@26 669 static void
ian@26 670 xen_ia64_privcmd_entry_open(struct xen_ia64_privcmd_range* privcmd_range,
ian@26 671 int i)
ian@26 672 {
ian@26 673 struct xen_ia64_privcmd_entry* entry = &privcmd_range->entries[i];
ian@26 674 if (entry->gpfn != INVALID_GPFN) {
ian@26 675 atomic_inc(&entry->map_count);
ian@26 676 } else {
ian@26 677 BUG_ON(atomic_read(&entry->map_count) != 0);
ian@26 678 }
ian@26 679 }
ian@26 680
ian@26 681 static void
ian@26 682 xen_ia64_privcmd_entry_close(struct xen_ia64_privcmd_range* privcmd_range,
ian@26 683 int i)
ian@26 684 {
ian@26 685 struct xen_ia64_privcmd_entry* entry = &privcmd_range->entries[i];
ian@26 686 if (entry->gpfn != INVALID_GPFN &&
ian@26 687 atomic_dec_and_test(&entry->map_count)) {
ian@26 688 xen_ia64_privcmd_entry_munmap(privcmd_range, i);
ian@26 689 }
ian@26 690 }
ian@26 691
ian@26 692 static void xen_ia64_privcmd_vma_open(struct vm_area_struct* vma);
ian@26 693 static void xen_ia64_privcmd_vma_close(struct vm_area_struct* vma);
ian@26 694
ian@26 695 struct vm_operations_struct xen_ia64_privcmd_vm_ops = {
ian@26 696 .open = &xen_ia64_privcmd_vma_open,
ian@26 697 .close = &xen_ia64_privcmd_vma_close,
ian@26 698 };
ian@26 699
ian@26 700 static void
ian@26 701 __xen_ia64_privcmd_vma_open(struct vm_area_struct* vma,
ian@26 702 struct xen_ia64_privcmd_vma* privcmd_vma,
ian@26 703 struct xen_ia64_privcmd_range* privcmd_range)
ian@26 704 {
ian@26 705 unsigned long entry_offset = vma->vm_pgoff - privcmd_range->pgoff;
ian@26 706 unsigned long num_entries = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
ian@26 707 unsigned long i;
ian@26 708
ian@26 709 BUG_ON(entry_offset < 0);
ian@26 710 BUG_ON(entry_offset + num_entries > privcmd_range->num_entries);
ian@26 711
ian@26 712 privcmd_vma->range = privcmd_range;
ian@26 713 privcmd_vma->num_entries = num_entries;
ian@26 714 privcmd_vma->entries = &privcmd_range->entries[entry_offset];
ian@26 715 vma->vm_private_data = privcmd_vma;
ian@26 716 for (i = 0; i < privcmd_vma->num_entries; i++) {
ian@26 717 xen_ia64_privcmd_entry_open(privcmd_range, entry_offset + i);
ian@26 718 }
ian@26 719
ian@26 720 vma->vm_private_data = privcmd_vma;
ian@26 721 vma->vm_ops = &xen_ia64_privcmd_vm_ops;
ian@26 722 }
ian@26 723
ian@26 724 static void
ian@26 725 xen_ia64_privcmd_vma_open(struct vm_area_struct* vma)
ian@26 726 {
ian@26 727 struct xen_ia64_privcmd_vma* old_privcmd_vma = (struct xen_ia64_privcmd_vma*)vma->vm_private_data;
ian@26 728 struct xen_ia64_privcmd_vma* privcmd_vma = (struct xen_ia64_privcmd_vma*)vma->vm_private_data;
ian@26 729 struct xen_ia64_privcmd_range* privcmd_range = privcmd_vma->range;
ian@26 730
ian@26 731 atomic_inc(&privcmd_range->ref_count);
ian@26 732 // vm_op->open() can't fail.
ian@26 733 privcmd_vma = kmalloc(sizeof(*privcmd_vma), GFP_KERNEL | __GFP_NOFAIL);
ian@26 734 // copy original value if necessary
ian@26 735 privcmd_vma->is_privcmd_mmapped = old_privcmd_vma->is_privcmd_mmapped;
ian@26 736
ian@26 737 __xen_ia64_privcmd_vma_open(vma, privcmd_vma, privcmd_range);
ian@26 738 }
ian@26 739
ian@26 740 static void
ian@26 741 xen_ia64_privcmd_vma_close(struct vm_area_struct* vma)
ian@26 742 {
ian@26 743 struct xen_ia64_privcmd_vma* privcmd_vma =
ian@26 744 (struct xen_ia64_privcmd_vma*)vma->vm_private_data;
ian@26 745 struct xen_ia64_privcmd_range* privcmd_range = privcmd_vma->range;
ian@26 746 unsigned long entry_offset = vma->vm_pgoff - privcmd_range->pgoff;
ian@26 747 unsigned long i;
ian@26 748
ian@26 749 for (i = 0; i < privcmd_vma->num_entries; i++) {
ian@26 750 xen_ia64_privcmd_entry_close(privcmd_range, entry_offset + i);
ian@26 751 }
ian@26 752 vma->vm_private_data = NULL;
ian@26 753 kfree(privcmd_vma);
ian@26 754
ian@26 755 if (atomic_dec_and_test(&privcmd_range->ref_count)) {
ian@26 756 #if 1
ian@26 757 for (i = 0; i < privcmd_range->num_entries; i++) {
ian@26 758 struct xen_ia64_privcmd_entry* entry =
ian@26 759 &privcmd_range->entries[i];
ian@26 760 BUG_ON(atomic_read(&entry->map_count) != 0);
ian@26 761 BUG_ON(entry->gpfn != INVALID_GPFN);
ian@26 762 }
ian@26 763 #endif
ian@26 764 release_resource(privcmd_range->res);
ian@26 765 kfree(privcmd_range->res);
ian@26 766 vfree(privcmd_range);
ian@26 767 }
ian@26 768 }
ian@26 769
ian@26 770 int
ian@26 771 privcmd_enforce_singleshot_mapping(struct vm_area_struct *vma)
ian@26 772 {
ian@26 773 struct xen_ia64_privcmd_vma* privcmd_vma =
ian@26 774 (struct xen_ia64_privcmd_vma *)vma->vm_private_data;
ian@26 775 return (xchg(&privcmd_vma->is_privcmd_mmapped, 1) == 0);
ian@26 776 }
ian@26 777
ian@26 778 int
ian@26 779 privcmd_mmap(struct file * file, struct vm_area_struct * vma)
ian@26 780 {
ian@26 781 int error;
ian@26 782 unsigned long size = vma->vm_end - vma->vm_start;
ian@26 783 unsigned long num_entries = size >> PAGE_SHIFT;
ian@26 784 struct xen_ia64_privcmd_range* privcmd_range = NULL;
ian@26 785 struct xen_ia64_privcmd_vma* privcmd_vma = NULL;
ian@26 786 struct resource* res = NULL;
ian@26 787 unsigned long i;
ian@26 788 BUG_ON(!is_running_on_xen());
ian@26 789
ian@26 790 BUG_ON(file->private_data != NULL);
ian@26 791
ian@26 792 error = -ENOMEM;
ian@26 793 privcmd_range =
ian@26 794 vmalloc(sizeof(*privcmd_range) +
ian@26 795 sizeof(privcmd_range->entries[0]) * num_entries);
ian@26 796 if (privcmd_range == NULL) {
ian@26 797 goto out_enomem0;
ian@26 798 }
ian@26 799 privcmd_vma = kmalloc(sizeof(*privcmd_vma), GFP_KERNEL);
ian@26 800 if (privcmd_vma == NULL) {
ian@26 801 goto out_enomem1;
ian@26 802 }
ian@26 803 privcmd_vma->is_privcmd_mmapped = 0;
ian@26 804
ian@26 805 res = kzalloc(sizeof(*res), GFP_KERNEL);
ian@26 806 if (res == NULL) {
ian@26 807 goto out_enomem1;
ian@26 808 }
ian@26 809 res->name = "Xen privcmd mmap";
ian@26 810 error = allocate_resource(&iomem_resource, res, size,
ian@26 811 privcmd_resource_min, privcmd_resource_max,
ian@26 812 privcmd_resource_align, NULL, NULL);
ian@26 813 if (error) {
ian@26 814 goto out_enomem1;
ian@26 815 }
ian@26 816 privcmd_range->res = res;
ian@26 817
ian@26 818 /* DONTCOPY is essential for Xen as copy_page_range is broken. */
ian@26 819 vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY | VM_PFNMAP;
ian@26 820
ian@26 821 atomic_set(&privcmd_range->ref_count, 1);
ian@26 822 privcmd_range->pgoff = vma->vm_pgoff;
ian@26 823 privcmd_range->num_entries = num_entries;
ian@26 824 for (i = 0; i < privcmd_range->num_entries; i++) {
ian@26 825 xen_ia64_privcmd_init_entry(&privcmd_range->entries[i]);
ian@26 826 }
ian@26 827
ian@26 828 __xen_ia64_privcmd_vma_open(vma, privcmd_vma, privcmd_range);
ian@26 829 return 0;
ian@26 830
ian@26 831 out_enomem1:
ian@26 832 kfree(res);
ian@26 833 kfree(privcmd_vma);
ian@26 834 out_enomem0:
ian@26 835 vfree(privcmd_range);
ian@26 836 return error;
ian@26 837 }
ian@26 838
ian@26 839 int
ian@26 840 direct_remap_pfn_range(struct vm_area_struct *vma,
ian@26 841 unsigned long address, // process virtual address
ian@26 842 unsigned long gmfn, // gmfn, gmfn + 1, ... gmfn + size/PAGE_SIZE
ian@26 843 unsigned long size,
ian@26 844 pgprot_t prot,
ian@26 845 domid_t domid) // target domain
ian@26 846 {
ian@26 847 struct xen_ia64_privcmd_vma* privcmd_vma =
ian@26 848 (struct xen_ia64_privcmd_vma*)vma->vm_private_data;
ian@26 849 struct xen_ia64_privcmd_range* privcmd_range = privcmd_vma->range;
ian@26 850 unsigned long entry_offset = vma->vm_pgoff - privcmd_range->pgoff;
ian@26 851
ian@26 852 unsigned long i;
ian@26 853 unsigned long offset;
ian@26 854 int error = 0;
ian@26 855 BUG_ON(!is_running_on_xen());
ian@26 856
ian@26 857 #if 0
ian@26 858 if (prot != vm->vm_page_prot) {
ian@26 859 return -EINVAL;
ian@26 860 }
ian@26 861 #endif
ian@26 862
ian@26 863 i = (address - vma->vm_start) >> PAGE_SHIFT;
ian@26 864 for (offset = 0; offset < size; offset += PAGE_SIZE) {
ian@26 865 error = xen_ia64_privcmd_entry_mmap(vma, (address + offset) & PAGE_MASK, privcmd_range, entry_offset + i, gmfn, prot, domid);
ian@26 866 if (error != 0) {
ian@26 867 break;
ian@26 868 }
ian@26 869
ian@26 870 i++;
ian@26 871 gmfn++;
ian@26 872 }
ian@26 873
ian@26 874 return error;
ian@26 875 }
ian@26 876
ian@26 877
ian@26 878 ///////////////////////////////////////////////////////////////////////////
ian@26 879 // expose p2m table
ian@26 880 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M
ian@26 881 #include <linux/cpu.h>
ian@26 882 #include <asm/uaccess.h>
ian@26 883
ian@26 884 int p2m_initialized __read_mostly = 0;
ian@26 885
ian@26 886 unsigned long p2m_min_low_pfn __read_mostly;
ian@26 887 unsigned long p2m_max_low_pfn __read_mostly;
ian@26 888 unsigned long p2m_convert_min_pfn __read_mostly;
ian@26 889 unsigned long p2m_convert_max_pfn __read_mostly;
ian@26 890
ian@26 891 static struct resource p2m_resource = {
ian@26 892 .name = "Xen p2m table",
ian@26 893 .flags = IORESOURCE_MEM,
ian@26 894 };
ian@26 895 static unsigned long p2m_assign_start_pfn __read_mostly;
ian@26 896 static unsigned long p2m_assign_end_pfn __read_mostly;
ian@26 897 static unsigned long p2m_expose_size; // this is referenced only when resume.
ian@26 898 // so __read_mostly doesn't make sense.
ian@26 899 volatile const pte_t* p2m_pte __read_mostly;
ian@26 900
ian@26 901 #define GRNULE_PFN PTRS_PER_PTE
ian@26 902 static unsigned long p2m_granule_pfn __read_mostly = GRNULE_PFN;
ian@26 903
ian@26 904 #define ROUNDDOWN(x, y) ((x) & ~((y) - 1))
ian@26 905 #define ROUNDUP(x, y) (((x) + (y) - 1) & ~((y) - 1))
ian@26 906
ian@26 907 #define P2M_PREFIX "Xen p2m: "
ian@26 908
ian@26 909 static int xen_ia64_p2m_expose __read_mostly = 1;
ian@26 910 module_param(xen_ia64_p2m_expose, int, 0);
ian@26 911 MODULE_PARM_DESC(xen_ia64_p2m_expose,
ian@26 912 "enable/disable xen/ia64 p2m exposure optimization\n");
ian@26 913
ian@26 914 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 915 static int xen_ia64_p2m_expose_use_dtr __read_mostly = 1;
ian@26 916 module_param(xen_ia64_p2m_expose_use_dtr, int, 0);
ian@26 917 MODULE_PARM_DESC(xen_ia64_p2m_expose_use_dtr,
ian@26 918 "use/unuse dtr to map exposed p2m table\n");
ian@26 919
ian@26 920 static const int p2m_page_shifts[] = {
ian@26 921 _PAGE_SIZE_4K,
ian@26 922 _PAGE_SIZE_8K,
ian@26 923 _PAGE_SIZE_16K,
ian@26 924 _PAGE_SIZE_64K,
ian@26 925 _PAGE_SIZE_256K,
ian@26 926 _PAGE_SIZE_1M,
ian@26 927 _PAGE_SIZE_4M,
ian@26 928 _PAGE_SIZE_16M,
ian@26 929 _PAGE_SIZE_64M,
ian@26 930 _PAGE_SIZE_256M,
ian@26 931 };
ian@26 932
ian@26 933 struct p2m_itr_arg {
ian@26 934 unsigned long vaddr;
ian@26 935 unsigned long pteval;
ian@26 936 unsigned long log_page_size;
ian@26 937 };
ian@26 938 static struct p2m_itr_arg p2m_itr_arg __read_mostly;
ian@26 939
ian@26 940 // This should be in asm-ia64/kregs.h
ian@26 941 #define IA64_TR_P2M_TABLE 3
ian@26 942
ian@26 943 static void
ian@26 944 p2m_itr(void* info)
ian@26 945 {
ian@26 946 struct p2m_itr_arg* arg = (struct p2m_itr_arg*)info;
ian@26 947 ia64_itr(0x2, IA64_TR_P2M_TABLE,
ian@26 948 arg->vaddr, arg->pteval, arg->log_page_size);
ian@26 949 ia64_srlz_d();
ian@26 950 }
ian@26 951
ian@26 952 static int
ian@26 953 p2m_expose_dtr_call(struct notifier_block *self,
ian@26 954 unsigned long event, void* ptr)
ian@26 955 {
ian@26 956 unsigned int cpu = (unsigned int)(long)ptr;
ian@26 957 if (event != CPU_ONLINE)
ian@26 958 return 0;
ian@26 959 if (p2m_initialized && xen_ia64_p2m_expose_use_dtr) {
ian@26 960 unsigned int me = get_cpu();
ian@26 961 if (cpu == me)
ian@26 962 p2m_itr(&p2m_itr_arg);
ian@26 963 else
ian@26 964 smp_call_function_single(cpu, &p2m_itr, &p2m_itr_arg,
ian@26 965 1, 1);
ian@26 966 put_cpu();
ian@26 967 }
ian@26 968 return 0;
ian@26 969 }
ian@26 970
ian@26 971 static struct notifier_block p2m_expose_dtr_hotplug_notifier = {
ian@26 972 .notifier_call = p2m_expose_dtr_call,
ian@26 973 .next = NULL,
ian@26 974 .priority = 0
ian@26 975 };
ian@26 976 #endif
ian@26 977
ian@26 978 static int
ian@26 979 p2m_expose_init(void)
ian@26 980 {
ian@26 981 unsigned long num_pfn;
ian@26 982 unsigned long p2m_size = 0;
ian@26 983 unsigned long align = ~0UL;
ian@26 984 int error = 0;
ian@26 985 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 986 int i;
ian@26 987 unsigned long page_size;
ian@26 988 unsigned long log_page_size = 0;
ian@26 989 #endif
ian@26 990
ian@26 991 if (!xen_ia64_p2m_expose)
ian@26 992 return -ENOSYS;
ian@26 993 if (p2m_initialized)
ian@26 994 return 0;
ian@26 995
ian@26 996 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 997 error = register_cpu_notifier(&p2m_expose_dtr_hotplug_notifier);
ian@26 998 if (error < 0)
ian@26 999 return error;
ian@26 1000 #endif
ian@26 1001
ian@26 1002 lock_cpu_hotplug();
ian@26 1003 if (p2m_initialized)
ian@26 1004 goto out;
ian@26 1005
ian@26 1006 #ifdef CONFIG_DISCONTIGMEM
ian@26 1007 p2m_min_low_pfn = min_low_pfn;
ian@26 1008 p2m_max_low_pfn = max_low_pfn;
ian@26 1009 #else
ian@26 1010 p2m_min_low_pfn = 0;
ian@26 1011 p2m_max_low_pfn = max_pfn;
ian@26 1012 #endif
ian@26 1013
ian@26 1014 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 1015 if (xen_ia64_p2m_expose_use_dtr) {
ian@26 1016 unsigned long granule_pfn = 0;
ian@26 1017 p2m_size = p2m_max_low_pfn - p2m_min_low_pfn;
ian@26 1018 for (i = 0;
ian@26 1019 i < sizeof(p2m_page_shifts)/sizeof(p2m_page_shifts[0]);
ian@26 1020 i++) {
ian@26 1021 log_page_size = p2m_page_shifts[i];
ian@26 1022 page_size = 1UL << log_page_size;
ian@26 1023 if (page_size < p2m_size)
ian@26 1024 continue;
ian@26 1025
ian@26 1026 granule_pfn = max(page_size >> PAGE_SHIFT,
ian@26 1027 p2m_granule_pfn);
ian@26 1028 p2m_convert_min_pfn = ROUNDDOWN(p2m_min_low_pfn,
ian@26 1029 granule_pfn);
ian@26 1030 p2m_convert_max_pfn = ROUNDUP(p2m_max_low_pfn,
ian@26 1031 granule_pfn);
ian@26 1032 num_pfn = p2m_convert_max_pfn - p2m_convert_min_pfn;
ian@26 1033 p2m_expose_size = num_pfn << PAGE_SHIFT;
ian@26 1034 p2m_size = num_pfn / PTRS_PER_PTE;
ian@26 1035 p2m_size = ROUNDUP(p2m_size, granule_pfn << PAGE_SHIFT);
ian@26 1036 if (p2m_size == page_size)
ian@26 1037 break;
ian@26 1038 }
ian@26 1039 if (p2m_size != page_size) {
ian@26 1040 printk(KERN_ERR "p2m_size != page_size\n");
ian@26 1041 error = -EINVAL;
ian@26 1042 goto out;
ian@26 1043 }
ian@26 1044 align = max(privcmd_resource_align, granule_pfn << PAGE_SHIFT);
ian@26 1045 } else
ian@26 1046 #endif
ian@26 1047 {
ian@26 1048 BUG_ON(p2m_granule_pfn & (p2m_granule_pfn - 1));
ian@26 1049 p2m_convert_min_pfn = ROUNDDOWN(p2m_min_low_pfn,
ian@26 1050 p2m_granule_pfn);
ian@26 1051 p2m_convert_max_pfn = ROUNDUP(p2m_max_low_pfn, p2m_granule_pfn);
ian@26 1052 num_pfn = p2m_convert_max_pfn - p2m_convert_min_pfn;
ian@26 1053 p2m_expose_size = num_pfn << PAGE_SHIFT;
ian@26 1054 p2m_size = num_pfn / PTRS_PER_PTE;
ian@26 1055 p2m_size = ROUNDUP(p2m_size, p2m_granule_pfn << PAGE_SHIFT);
ian@26 1056 align = max(privcmd_resource_align,
ian@26 1057 p2m_granule_pfn << PAGE_SHIFT);
ian@26 1058 }
ian@26 1059
ian@26 1060 // use privcmd region
ian@26 1061 error = allocate_resource(&iomem_resource, &p2m_resource, p2m_size,
ian@26 1062 privcmd_resource_min, privcmd_resource_max,
ian@26 1063 align, NULL, NULL);
ian@26 1064 if (error) {
ian@26 1065 printk(KERN_ERR P2M_PREFIX
ian@26 1066 "can't allocate region for p2m exposure "
ian@26 1067 "[0x%016lx, 0x%016lx) 0x%016lx\n",
ian@26 1068 p2m_convert_min_pfn, p2m_convert_max_pfn, p2m_size);
ian@26 1069 goto out;
ian@26 1070 }
ian@26 1071
ian@26 1072 p2m_assign_start_pfn = p2m_resource.start >> PAGE_SHIFT;
ian@26 1073 p2m_assign_end_pfn = p2m_resource.end >> PAGE_SHIFT;
ian@26 1074
ian@26 1075 error = HYPERVISOR_expose_p2m(p2m_convert_min_pfn,
ian@26 1076 p2m_assign_start_pfn,
ian@26 1077 p2m_expose_size, p2m_granule_pfn);
ian@26 1078 if (error) {
ian@26 1079 printk(KERN_ERR P2M_PREFIX "failed expose p2m hypercall %d\n",
ian@26 1080 error);
ian@26 1081 printk(KERN_ERR P2M_PREFIX "conv 0x%016lx assign 0x%016lx "
ian@26 1082 "expose_size 0x%016lx granule 0x%016lx\n",
ian@26 1083 p2m_convert_min_pfn, p2m_assign_start_pfn,
ian@26 1084 p2m_expose_size, p2m_granule_pfn);;
ian@26 1085 release_resource(&p2m_resource);
ian@26 1086 goto out;
ian@26 1087 }
ian@26 1088 p2m_pte = (volatile const pte_t*)pfn_to_kaddr(p2m_assign_start_pfn);
ian@26 1089 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 1090 if (xen_ia64_p2m_expose_use_dtr) {
ian@26 1091 p2m_itr_arg.vaddr = (unsigned long)__va(p2m_assign_start_pfn
ian@26 1092 << PAGE_SHIFT);
ian@26 1093 p2m_itr_arg.pteval = pte_val(pfn_pte(p2m_assign_start_pfn,
ian@26 1094 PAGE_KERNEL));
ian@26 1095 p2m_itr_arg.log_page_size = log_page_size;
ian@26 1096 smp_mb();
ian@26 1097 smp_call_function(&p2m_itr, &p2m_itr_arg, 1, 1);
ian@26 1098 p2m_itr(&p2m_itr_arg);
ian@26 1099 }
ian@26 1100 #endif
ian@26 1101 smp_mb();
ian@26 1102 p2m_initialized = 1;
ian@26 1103 printk(P2M_PREFIX "assign p2m table of [0x%016lx, 0x%016lx)\n",
ian@26 1104 p2m_convert_min_pfn << PAGE_SHIFT,
ian@26 1105 p2m_convert_max_pfn << PAGE_SHIFT);
ian@26 1106 printk(P2M_PREFIX "to [0x%016lx, 0x%016lx) (%ld KBytes)\n",
ian@26 1107 p2m_assign_start_pfn << PAGE_SHIFT,
ian@26 1108 p2m_assign_end_pfn << PAGE_SHIFT,
ian@26 1109 p2m_size / 1024);
ian@26 1110 out:
ian@26 1111 unlock_cpu_hotplug();
ian@26 1112 return error;
ian@26 1113 }
ian@26 1114
ian@26 1115 #ifdef notyet
ian@26 1116 void
ian@26 1117 p2m_expose_cleanup(void)
ian@26 1118 {
ian@26 1119 BUG_ON(!p2m_initialized);
ian@26 1120 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 1121 unregister_cpu_notifier(&p2m_expose_dtr_hotplug_notifier);
ian@26 1122 #endif
ian@26 1123 release_resource(&p2m_resource);
ian@26 1124 }
ian@26 1125 #endif
ian@26 1126
ian@26 1127 static void
ian@26 1128 p2m_expose_resume(void)
ian@26 1129 {
ian@26 1130 int error;
ian@26 1131
ian@26 1132 if (!xen_ia64_p2m_expose || !p2m_initialized)
ian@26 1133 return;
ian@26 1134
ian@26 1135 /*
ian@26 1136 * We can't call {lock, unlock}_cpu_hotplug() because
ian@26 1137 * they require process context.
ian@26 1138 * We don't need them because we're the only one cpu and
ian@26 1139 * interrupts are masked when resume.
ian@26 1140 */
ian@26 1141 error = HYPERVISOR_expose_p2m(p2m_convert_min_pfn,
ian@26 1142 p2m_assign_start_pfn,
ian@26 1143 p2m_expose_size, p2m_granule_pfn);
ian@26 1144 if (error) {
ian@26 1145 printk(KERN_ERR P2M_PREFIX "failed expose p2m hypercall %d\n",
ian@26 1146 error);
ian@26 1147 printk(KERN_ERR P2M_PREFIX "conv 0x%016lx assign 0x%016lx "
ian@26 1148 "expose_size 0x%016lx granule 0x%016lx\n",
ian@26 1149 p2m_convert_min_pfn, p2m_assign_start_pfn,
ian@26 1150 p2m_expose_size, p2m_granule_pfn);;
ian@26 1151 p2m_initialized = 0;
ian@26 1152 smp_mb();
ian@26 1153 ia64_ptr(0x2, p2m_itr_arg.vaddr, p2m_itr_arg.log_page_size);
ian@26 1154
ian@26 1155 /*
ian@26 1156 * We can't call those clean up functions because they
ian@26 1157 * require process context.
ian@26 1158 */
ian@26 1159 #if 0
ian@26 1160 #ifdef CONFIG_XEN_IA64_EXPOSE_P2M_USE_DTR
ian@26 1161 if (xen_ia64_p2m_expose_use_dtr)
ian@26 1162 unregister_cpu_notifier(
ian@26 1163 &p2m_expose_dtr_hotplug_notifier);
ian@26 1164 #endif
ian@26 1165 release_resource(&p2m_resource);
ian@26 1166 #endif
ian@26 1167 }
ian@26 1168 }
ian@26 1169
ian@26 1170 //XXX inlinize?
ian@26 1171 unsigned long
ian@26 1172 p2m_phystomach(unsigned long gpfn)
ian@26 1173 {
ian@26 1174 volatile const pte_t* pte;
ian@26 1175 unsigned long mfn;
ian@26 1176 unsigned long pteval;
ian@26 1177
ian@26 1178 if (!p2m_initialized ||
ian@26 1179 gpfn < p2m_min_low_pfn || gpfn > p2m_max_low_pfn
ian@26 1180 /* || !pfn_valid(gpfn) */)
ian@26 1181 return INVALID_MFN;
ian@26 1182 pte = p2m_pte + (gpfn - p2m_convert_min_pfn);
ian@26 1183
ian@26 1184 mfn = INVALID_MFN;
ian@26 1185 if (likely(__get_user(pteval, (unsigned long __user *)pte) == 0 &&
ian@26 1186 pte_present(__pte(pteval)) &&
ian@26 1187 pte_pfn(__pte(pteval)) != (INVALID_MFN >> PAGE_SHIFT)))
ian@26 1188 mfn = (pteval & _PFN_MASK) >> PAGE_SHIFT;
ian@26 1189
ian@26 1190 return mfn;
ian@26 1191 }
ian@26 1192
ian@26 1193 EXPORT_SYMBOL_GPL(p2m_initialized);
ian@26 1194 EXPORT_SYMBOL_GPL(p2m_min_low_pfn);
ian@26 1195 EXPORT_SYMBOL_GPL(p2m_max_low_pfn);
ian@26 1196 EXPORT_SYMBOL_GPL(p2m_convert_min_pfn);
ian@26 1197 EXPORT_SYMBOL_GPL(p2m_convert_max_pfn);
ian@26 1198 EXPORT_SYMBOL_GPL(p2m_pte);
ian@26 1199 EXPORT_SYMBOL_GPL(p2m_phystomach);
ian@26 1200 #endif
ian@26 1201
ian@26 1202 ///////////////////////////////////////////////////////////////////////////
ian@26 1203 // for xenoprof
ian@26 1204
ian@26 1205 struct resource*
ian@26 1206 xen_ia64_allocate_resource(unsigned long size)
ian@26 1207 {
ian@26 1208 struct resource* res;
ian@26 1209 int error;
ian@26 1210
ian@26 1211 res = kmalloc(sizeof(*res), GFP_KERNEL);
ian@26 1212 if (res == NULL)
ian@26 1213 return ERR_PTR(-ENOMEM);
ian@26 1214
ian@26 1215 res->name = "Xen";
ian@26 1216 res->flags = IORESOURCE_MEM;
ian@26 1217 error = allocate_resource(&iomem_resource, res, PAGE_ALIGN(size),
ian@26 1218 privcmd_resource_min, privcmd_resource_max,
ian@26 1219 IA64_GRANULE_SIZE, NULL, NULL);
ian@26 1220 if (error) {
ian@26 1221 kfree(res);
ian@26 1222 return ERR_PTR(error);
ian@26 1223 }
ian@26 1224 return res;
ian@26 1225 }
ian@26 1226 EXPORT_SYMBOL_GPL(xen_ia64_allocate_resource);
ian@26 1227
ian@26 1228 void
ian@26 1229 xen_ia64_release_resource(struct resource* res)
ian@26 1230 {
ian@26 1231 release_resource(res);
ian@26 1232 kfree(res);
ian@26 1233 }
ian@26 1234 EXPORT_SYMBOL_GPL(xen_ia64_release_resource);
ian@26 1235
ian@26 1236 void
ian@26 1237 xen_ia64_unmap_resource(struct resource* res)
ian@26 1238 {
ian@26 1239 unsigned long gpfn = res->start >> PAGE_SHIFT;
ian@26 1240 unsigned long nr_pages = (res->end - res->start) >> PAGE_SHIFT;
ian@26 1241 unsigned long i;
ian@26 1242
ian@26 1243 for (i = 0; i < nr_pages; i++) {
ian@26 1244 int error = HYPERVISOR_zap_physmap(gpfn + i, 0);
ian@26 1245 if (error)
ian@26 1246 printk(KERN_ERR
ian@26 1247 "%s:%d zap_phsymap failed %d gpfn %lx\n",
ian@26 1248 __func__, __LINE__, error, gpfn + i);
ian@26 1249 }
ian@26 1250 xen_ia64_release_resource(res);
ian@26 1251 }
ian@26 1252 EXPORT_SYMBOL_GPL(xen_ia64_unmap_resource);
ian@26 1253
ian@26 1254 ///////////////////////////////////////////////////////////////////////////
ian@26 1255 // suspend/resume
ian@26 1256 void
ian@26 1257 xen_post_suspend(int suspend_cancelled)
ian@26 1258 {
ian@26 1259 if (suspend_cancelled)
ian@26 1260 return;
ian@26 1261
ian@26 1262 p2m_expose_resume();
ian@26 1263 /* add more if necessary */
ian@26 1264 }