ia64/xen-unstable

annotate xen/common/memory.c @ 17349:4e2e98c2098e

Clean up handling of IS_PRIV_FOR() and rcu_[un]lock_domain().

In particular this *removes* some IS_PRIV_FOR() checks. *Especially*
in particular, all domctls are executable only by dom0. Several of
them were really unsafe for execution by a stub domain as they can
affect global system resource usage.

This probably breaks stub domains. Where necessary, some of these
reversions can themselves be reverted where they are judged both
necessary and safe.

Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Mar 28 17:50:10 2008 +0000 (2008-03-28)
parents cff4c8a1aa28
children 57febe0264e1
rev   line source
kaf24@6486 1 /******************************************************************************
kaf24@6486 2 * memory.c
kaf24@6486 3 *
kaf24@6486 4 * Code to handle memory-related requests.
kaf24@6486 5 *
kaf24@6486 6 * Copyright (c) 2003-2004, B Dragovic
kaf24@6486 7 * Copyright (c) 2003-2005, K A Fraser
kaf24@6486 8 */
kaf24@6486 9
kaf24@6486 10 #include <xen/config.h>
kaf24@6486 11 #include <xen/types.h>
kaf24@6486 12 #include <xen/lib.h>
kaf24@6486 13 #include <xen/mm.h>
kaf24@6486 14 #include <xen/perfc.h>
kaf24@6486 15 #include <xen/sched.h>
kaf24@6486 16 #include <xen/event.h>
Tim@15635 17 #include <xen/paging.h>
kaf24@8468 18 #include <xen/iocap.h>
kaf24@9068 19 #include <xen/guest_access.h>
ack@13295 20 #include <xen/hypercall.h>
kaf24@11219 21 #include <xen/errno.h>
kaf24@6486 22 #include <asm/current.h>
kaf24@6486 23 #include <asm/hardirq.h>
kaf24@6486 24 #include <public/memory.h>
kfraser@15815 25 #include <xsm/xsm.h>
kaf24@6486 26
kfraser@12374 27 struct memop_args {
kfraser@12374 28 /* INPUT */
kfraser@12374 29 struct domain *domain; /* Domain to be affected. */
kfraser@12374 30 XEN_GUEST_HANDLE(xen_pfn_t) extent_list; /* List of extent base addrs. */
kfraser@12374 31 unsigned int nr_extents; /* Number of extents to allocate or free. */
kfraser@12374 32 unsigned int extent_order; /* Size of each extent. */
kfraser@12374 33 unsigned int memflags; /* Allocation flags. */
kfraser@12374 34
kfraser@12374 35 /* INPUT/OUTPUT */
kfraser@12374 36 unsigned int nr_done; /* Number of extents processed so far. */
kfraser@12374 37 int preempted; /* Was the hypercall preempted? */
kfraser@12374 38 };
kfraser@12374 39
kfraser@12374 40 static unsigned int select_local_cpu(struct domain *d)
kfraser@12374 41 {
kfraser@12374 42 struct vcpu *v = d->vcpu[0];
kfraser@12374 43 return (v ? v->processor : 0);
kfraser@12374 44 }
kfraser@12374 45
kfraser@12374 46 static void increase_reservation(struct memop_args *a)
kaf24@6486 47 {
kaf24@8726 48 struct page_info *page;
kaf24@10314 49 unsigned long i;
kaf24@10314 50 xen_pfn_t mfn;
kfraser@12374 51 struct domain *d = a->domain;
kfraser@12374 52 unsigned int cpu = select_local_cpu(d);
kaf24@6486 53
kfraser@12374 54 if ( !guest_handle_is_null(a->extent_list) &&
kfraser@12374 55 !guest_handle_okay(a->extent_list, a->nr_extents) )
kfraser@12374 56 return;
kaf24@6486 57
kfraser@12374 58 if ( (a->extent_order != 0) &&
kaf24@8468 59 !multipage_allocation_permitted(current->domain) )
kfraser@12374 60 return;
kaf24@6486 61
kfraser@12374 62 for ( i = a->nr_done; i < a->nr_extents; i++ )
kaf24@6486 63 {
kaf24@6486 64 if ( hypercall_preempt_check() )
kaf24@6607 65 {
kfraser@12374 66 a->preempted = 1;
kfraser@12374 67 goto out;
kaf24@6607 68 }
kaf24@6486 69
kfraser@12374 70 page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
kfraser@12374 71 if ( unlikely(page == NULL) )
kaf24@6486 72 {
kaf24@12038 73 gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
kfraser@10418 74 "id=%d memflags=%x (%ld of %d)\n",
kfraser@12374 75 a->extent_order, d->domain_id, a->memflags,
kfraser@12374 76 i, a->nr_extents);
kfraser@12374 77 goto out;
kaf24@6486 78 }
kaf24@6486 79
kaf24@6486 80 /* Inform the domain of the new page's machine address. */
kfraser@12374 81 if ( !guest_handle_is_null(a->extent_list) )
kaf24@8859 82 {
kaf24@8859 83 mfn = page_to_mfn(page);
kfraser@12374 84 if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
kfraser@12374 85 goto out;
kaf24@8859 86 }
kaf24@6486 87 }
kaf24@6486 88
kfraser@12374 89 out:
kfraser@12374 90 a->nr_done = i;
kaf24@6486 91 }
sos22@8688 92
kfraser@12374 93 static void populate_physmap(struct memop_args *a)
kaf24@8673 94 {
kaf24@8726 95 struct page_info *page;
kaf24@10314 96 unsigned long i, j;
kfraser@12374 97 xen_pfn_t gpfn, mfn;
kfraser@12374 98 struct domain *d = a->domain;
kfraser@12374 99 unsigned int cpu = select_local_cpu(d);
kaf24@8673 100
kfraser@12374 101 if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
kfraser@12374 102 return;
kaf24@8673 103
kfraser@12374 104 if ( (a->extent_order != 0) &&
kaf24@8673 105 !multipage_allocation_permitted(current->domain) )
kfraser@12374 106 return;
kaf24@8673 107
kfraser@12374 108 for ( i = a->nr_done; i < a->nr_extents; i++ )
kaf24@8673 109 {
kaf24@8673 110 if ( hypercall_preempt_check() )
kaf24@8673 111 {
kfraser@12374 112 a->preempted = 1;
sos22@8688 113 goto out;
kaf24@8673 114 }
kaf24@8673 115
kfraser@12374 116 if ( unlikely(__copy_from_guest_offset(&gpfn, a->extent_list, i, 1)) )
kaf24@8859 117 goto out;
kaf24@8859 118
kfraser@12374 119 page = __alloc_domheap_pages(d, cpu, a->extent_order, a->memflags);
kfraser@12374 120 if ( unlikely(page == NULL) )
kaf24@8673 121 {
kaf24@12038 122 gdprintk(XENLOG_INFO, "Could not allocate order=%d extent: "
kfraser@12374 123 "id=%d memflags=%x (%ld of %d)\n",
kfraser@12374 124 a->extent_order, d->domain_id, a->memflags,
kfraser@12374 125 i, a->nr_extents);
sos22@8688 126 goto out;
kaf24@8673 127 }
kaf24@8673 128
kaf24@8726 129 mfn = page_to_mfn(page);
kaf24@8673 130
Tim@15635 131 if ( unlikely(paging_mode_translate(d)) )
kaf24@8694 132 {
kfraser@12374 133 for ( j = 0; j < (1 << a->extent_order); j++ )
keir@16291 134 if ( guest_physmap_add_page(d, gpfn + j, mfn + j) )
keir@16291 135 goto out;
sos22@8688 136 }
kaf24@8694 137 else
kaf24@8694 138 {
kfraser@12374 139 for ( j = 0; j < (1 << a->extent_order); j++ )
kaf24@8736 140 set_gpfn_from_mfn(mfn + j, gpfn + j);
kaf24@8673 141
sos22@8688 142 /* Inform the domain of the new page's machine address. */
kfraser@12374 143 if ( unlikely(__copy_to_guest_offset(a->extent_list, i, &mfn, 1)) )
sos22@8688 144 goto out;
sos22@8688 145 }
kaf24@8673 146 }
kaf24@8673 147
sos22@8688 148 out:
kfraser@12374 149 a->nr_done = i;
kaf24@8673 150 }
cl349@9211 151
kfraser@12374 152 int guest_remove_page(struct domain *d, unsigned long gmfn)
cl349@9211 153 {
cl349@9211 154 struct page_info *page;
cl349@9211 155 unsigned long mfn;
cl349@9211 156
cl349@9211 157 mfn = gmfn_to_mfn(d, gmfn);
cl349@9211 158 if ( unlikely(!mfn_valid(mfn)) )
cl349@9211 159 {
kaf24@12038 160 gdprintk(XENLOG_INFO, "Domain %u page number %lx invalid\n",
tdeegan@11172 161 d->domain_id, gmfn);
cl349@9211 162 return 0;
cl349@9211 163 }
cl349@9211 164
cl349@9211 165 page = mfn_to_page(mfn);
cl349@9211 166 if ( unlikely(!get_page(page, d)) )
cl349@9211 167 {
kaf24@12038 168 gdprintk(XENLOG_INFO, "Bad page free for domain %u\n", d->domain_id);
cl349@9211 169 return 0;
cl349@9211 170 }
cl349@9211 171
cl349@9211 172 if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) )
cl349@9211 173 put_page_and_type(page);
cl349@9211 174
cl349@9211 175 if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
cl349@9211 176 put_page(page);
cl349@9211 177
kfraser@11212 178 guest_physmap_remove_page(d, gmfn, mfn);
cl349@9211 179
cl349@9211 180 put_page(page);
cl349@9211 181
cl349@9211 182 return 1;
cl349@9211 183 }
cl349@9211 184
kfraser@12374 185 static void decrease_reservation(struct memop_args *a)
kaf24@6486 186 {
kaf24@10314 187 unsigned long i, j;
kaf24@10314 188 xen_pfn_t gmfn;
kaf24@6486 189
kfraser@12374 190 if ( !guest_handle_okay(a->extent_list, a->nr_extents) )
kfraser@12374 191 return;
kaf24@6486 192
kfraser@12374 193 for ( i = a->nr_done; i < a->nr_extents; i++ )
kaf24@6486 194 {
kaf24@6486 195 if ( hypercall_preempt_check() )
kaf24@6607 196 {
kfraser@12374 197 a->preempted = 1;
kfraser@12374 198 goto out;
kaf24@6607 199 }
kaf24@6486 200
kfraser@12374 201 if ( unlikely(__copy_from_guest_offset(&gmfn, a->extent_list, i, 1)) )
kfraser@12374 202 goto out;
kaf24@6486 203
kfraser@12374 204 for ( j = 0; j < (1 << a->extent_order); j++ )
kfraser@12374 205 if ( !guest_remove_page(a->domain, gmfn + j) )
kfraser@12374 206 goto out;
kaf24@6486 207 }
kaf24@6486 208
kfraser@12374 209 out:
kfraser@12374 210 a->nr_done = i;
kaf24@6486 211 }
kaf24@6486 212
kfraser@12374 213 static long translate_gpfn_list(
kaf24@9873 214 XEN_GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
kaf24@8871 215 {
kaf24@8871 216 struct xen_translate_gpfn_list op;
kaf24@10314 217 unsigned long i;
kaf24@10314 218 xen_pfn_t gpfn;
kaf24@10314 219 xen_pfn_t mfn;
kaf24@8871 220 struct domain *d;
kfraser@15815 221 int rc;
kaf24@6486 222
kaf24@9068 223 if ( copy_from_guest(&op, uop, 1) )
kaf24@8871 224 return -EFAULT;
kaf24@8871 225
kaf24@8871 226 /* Is size too large for us to encode a continuation? */
ack@13295 227 if ( op.nr_gpfns > (ULONG_MAX >> MEMOP_EXTENT_SHIFT) )
kaf24@8871 228 return -EINVAL;
kaf24@8871 229
kaf24@9068 230 if ( !guest_handle_okay(op.gpfn_list, op.nr_gpfns) ||
kaf24@9068 231 !guest_handle_okay(op.mfn_list, op.nr_gpfns) )
kaf24@8871 232 return -EFAULT;
kaf24@8871 233
kaf24@8871 234 if ( op.domid == DOMID_SELF )
keir@17349 235 {
keir@17349 236 d = rcu_lock_current_domain();
keir@17349 237 }
keir@17349 238 else
keir@17349 239 {
keir@17349 240 if ( (d = rcu_lock_domain_by_id(op.domid)) == NULL )
keir@16856 241 return -ESRCH;
keir@17349 242 if ( !IS_PRIV_FOR(current->domain, d) )
keir@17349 243 {
keir@16856 244 rcu_unlock_domain(d);
keir@16856 245 return -EPERM;
keir@16856 246 }
keir@16856 247 }
kaf24@8871 248
kaf24@8871 249
Tim@15635 250 if ( !paging_mode_translate(d) )
kaf24@8871 251 {
kfraser@14192 252 rcu_unlock_domain(d);
kaf24@8871 253 return -EINVAL;
kaf24@8871 254 }
kaf24@8871 255
kaf24@8871 256 for ( i = *progress; i < op.nr_gpfns; i++ )
kaf24@8871 257 {
kaf24@8871 258 if ( hypercall_preempt_check() )
kaf24@8871 259 {
kfraser@14192 260 rcu_unlock_domain(d);
kaf24@8871 261 *progress = i;
kaf24@8871 262 return -EAGAIN;
kaf24@8871 263 }
kaf24@8871 264
kaf24@9068 265 if ( unlikely(__copy_from_guest_offset(&gpfn, op.gpfn_list, i, 1)) )
kaf24@8871 266 {
kfraser@14192 267 rcu_unlock_domain(d);
kaf24@8871 268 return -EFAULT;
kaf24@8871 269 }
kaf24@8871 270
kaf24@8871 271 mfn = gmfn_to_mfn(d, gpfn);
kaf24@8871 272
kfraser@15815 273 rc = xsm_translate_gpfn_list(current->domain, mfn);
kfraser@15815 274 if ( rc )
kfraser@15815 275 {
kfraser@15815 276 rcu_unlock_domain(d);
kfraser@15815 277 return rc;
kfraser@15815 278 }
kfraser@15815 279
kaf24@9068 280 if ( unlikely(__copy_to_guest_offset(op.mfn_list, i, &mfn, 1)) )
kaf24@8871 281 {
kfraser@14192 282 rcu_unlock_domain(d);
kaf24@8871 283 return -EFAULT;
kaf24@8871 284 }
kaf24@8871 285 }
kaf24@8871 286
kfraser@14192 287 rcu_unlock_domain(d);
kaf24@8871 288 return 0;
kaf24@8871 289 }
kaf24@8871 290
kfraser@12374 291 static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
kfraser@10418 292 {
kfraser@10418 293 struct xen_memory_exchange exch;
kfraser@10418 294 LIST_HEAD(in_chunk_list);
kfraser@10418 295 LIST_HEAD(out_chunk_list);
kfraser@10418 296 unsigned long in_chunk_order, out_chunk_order;
kaf24@10459 297 xen_pfn_t gpfn, gmfn, mfn;
kfraser@10418 298 unsigned long i, j, k;
kfraser@11973 299 unsigned int memflags = 0, cpu;
kfraser@10418 300 long rc = 0;
kfraser@10418 301 struct domain *d;
kfraser@10418 302 struct page_info *page;
kfraser@10418 303
kfraser@10418 304 if ( copy_from_guest(&exch, arg, 1) )
kfraser@10418 305 return -EFAULT;
kfraser@10418 306
kfraser@10418 307 /* Various sanity checks. */
kfraser@10418 308 if ( (exch.nr_exchanged > exch.in.nr_extents) ||
kfraser@10418 309 /* Input and output domain identifiers match? */
kfraser@10418 310 (exch.in.domid != exch.out.domid) ||
kfraser@10418 311 /* Sizes of input and output lists do not overflow a long? */
kfraser@10418 312 ((~0UL >> exch.in.extent_order) < exch.in.nr_extents) ||
kfraser@10418 313 ((~0UL >> exch.out.extent_order) < exch.out.nr_extents) ||
kfraser@10418 314 /* Sizes of input and output lists match? */
kfraser@10418 315 ((exch.in.nr_extents << exch.in.extent_order) !=
kfraser@10418 316 (exch.out.nr_extents << exch.out.extent_order)) )
kfraser@10418 317 {
kfraser@10418 318 rc = -EINVAL;
kfraser@10418 319 goto fail_early;
kfraser@10418 320 }
kfraser@10418 321
kfraser@10418 322 /* Only privileged guests can allocate multi-page contiguous extents. */
kfraser@10418 323 if ( ((exch.in.extent_order != 0) || (exch.out.extent_order != 0)) &&
kfraser@10418 324 !multipage_allocation_permitted(current->domain) )
kfraser@10418 325 {
kfraser@10418 326 rc = -EPERM;
kfraser@10418 327 goto fail_early;
kfraser@10418 328 }
kfraser@10418 329
kfraser@10418 330 if ( exch.in.extent_order <= exch.out.extent_order )
kfraser@10418 331 {
kfraser@10418 332 in_chunk_order = exch.out.extent_order - exch.in.extent_order;
kfraser@10418 333 out_chunk_order = 0;
kfraser@10418 334 }
kfraser@10418 335 else
kfraser@10418 336 {
kfraser@10418 337 in_chunk_order = 0;
kfraser@10418 338 out_chunk_order = exch.in.extent_order - exch.out.extent_order;
kfraser@10418 339 }
kfraser@10418 340
kfraser@10418 341 /*
kfraser@10418 342 * Only support exchange on calling domain right now. Otherwise there are
kfraser@14642 343 * tricky corner cases to consider (e.g., dying domain).
kfraser@10418 344 */
kfraser@10418 345 if ( unlikely(exch.in.domid != DOMID_SELF) )
kfraser@10418 346 {
kfraser@10418 347 rc = IS_PRIV(current->domain) ? -EINVAL : -EPERM;
kfraser@10418 348 goto fail_early;
kfraser@10418 349 }
kfraser@10418 350 d = current->domain;
kfraser@10418 351
keir@16548 352 memflags |= MEMF_bits(domain_clamp_alloc_bitsize(
keir@16549 353 d, exch.out.address_bits ? : (BITS_PER_LONG+PAGE_SHIFT)));
keir@16548 354
kfraser@12374 355 cpu = select_local_cpu(d);
kfraser@11973 356
kfraser@12374 357 for ( i = (exch.nr_exchanged >> in_chunk_order);
kfraser@12374 358 i < (exch.in.nr_extents >> in_chunk_order);
kfraser@12374 359 i++ )
kfraser@10418 360 {
kfraser@10418 361 if ( hypercall_preempt_check() )
kfraser@10418 362 {
kfraser@12374 363 exch.nr_exchanged = i << in_chunk_order;
kfraser@10418 364 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
kfraser@10418 365 return -EFAULT;
kfraser@10418 366 return hypercall_create_continuation(
kfraser@10418 367 __HYPERVISOR_memory_op, "lh", XENMEM_exchange, arg);
kfraser@10418 368 }
kfraser@10418 369
kfraser@10418 370 /* Steal a chunk's worth of input pages from the domain. */
kfraser@10418 371 for ( j = 0; j < (1UL << in_chunk_order); j++ )
kfraser@10418 372 {
kfraser@10418 373 if ( unlikely(__copy_from_guest_offset(
kfraser@10418 374 &gmfn, exch.in.extent_start, (i<<in_chunk_order)+j, 1)) )
kfraser@10418 375 {
kfraser@10418 376 rc = -EFAULT;
kfraser@10418 377 goto fail;
kfraser@10418 378 }
kfraser@10418 379
kfraser@10418 380 for ( k = 0; k < (1UL << exch.in.extent_order); k++ )
kfraser@10418 381 {
kfraser@10418 382 mfn = gmfn_to_mfn(d, gmfn + k);
kfraser@10418 383 if ( unlikely(!mfn_valid(mfn)) )
kfraser@10418 384 {
kfraser@10418 385 rc = -EINVAL;
kfraser@10418 386 goto fail;
kfraser@10418 387 }
kfraser@10418 388
kfraser@10418 389 page = mfn_to_page(mfn);
kfraser@10418 390
kfraser@10418 391 if ( unlikely(steal_page(d, page, MEMF_no_refcount)) )
kfraser@10418 392 {
kfraser@10418 393 rc = -EINVAL;
kfraser@10418 394 goto fail;
kfraser@10418 395 }
kfraser@10418 396
kfraser@10418 397 list_add(&page->list, &in_chunk_list);
kfraser@10418 398 }
kfraser@10418 399 }
kfraser@10418 400
kfraser@10418 401 /* Allocate a chunk's worth of anonymous output pages. */
kfraser@10418 402 for ( j = 0; j < (1UL << out_chunk_order); j++ )
kfraser@10418 403 {
kfraser@12374 404 page = __alloc_domheap_pages(
kfraser@12374 405 NULL, cpu, exch.out.extent_order, memflags);
kfraser@10418 406 if ( unlikely(page == NULL) )
kfraser@10418 407 {
kfraser@10418 408 rc = -ENOMEM;
kfraser@10418 409 goto fail;
kfraser@10418 410 }
kfraser@10418 411
kfraser@10418 412 list_add(&page->list, &out_chunk_list);
kfraser@10418 413 }
kfraser@10418 414
kfraser@10418 415 /*
kfraser@10418 416 * Success! Beyond this point we cannot fail for this chunk.
kfraser@10418 417 */
kfraser@10418 418
kfraser@10418 419 /* Destroy final reference to each input page. */
kfraser@10418 420 while ( !list_empty(&in_chunk_list) )
kfraser@10418 421 {
kfraser@10418 422 page = list_entry(in_chunk_list.next, struct page_info, list);
kfraser@10418 423 list_del(&page->list);
kfraser@10418 424 if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) )
kfraser@10418 425 BUG();
kfraser@10418 426 mfn = page_to_mfn(page);
kfraser@10418 427 guest_physmap_remove_page(d, mfn_to_gmfn(d, mfn), mfn);
kfraser@10418 428 put_page(page);
kfraser@10418 429 }
kfraser@10418 430
kfraser@10418 431 /* Assign each output page to the domain. */
kfraser@10418 432 j = 0;
kfraser@10418 433 while ( !list_empty(&out_chunk_list) )
kfraser@10418 434 {
kfraser@10418 435 page = list_entry(out_chunk_list.next, struct page_info, list);
kfraser@10418 436 list_del(&page->list);
kfraser@10418 437 if ( assign_pages(d, page, exch.out.extent_order,
kfraser@10418 438 MEMF_no_refcount) )
kfraser@10418 439 BUG();
kfraser@10418 440
kfraser@10418 441 /* Note that we ignore errors accessing the output extent list. */
kfraser@10418 442 (void)__copy_from_guest_offset(
kfraser@10418 443 &gpfn, exch.out.extent_start, (i<<out_chunk_order)+j, 1);
kfraser@10418 444
kfraser@10418 445 mfn = page_to_mfn(page);
Tim@15635 446 if ( unlikely(paging_mode_translate(d)) )
kfraser@10418 447 {
keir@16291 448 /* Ignore failure here. There's nothing we can do. */
kfraser@10418 449 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
keir@16291 450 (void)guest_physmap_add_page(d, gpfn + k, mfn + k);
kfraser@10418 451 }
kfraser@10418 452 else
kfraser@10418 453 {
kfraser@10418 454 for ( k = 0; k < (1UL << exch.out.extent_order); k++ )
kfraser@10418 455 set_gpfn_from_mfn(mfn + k, gpfn + k);
kfraser@10418 456 (void)__copy_to_guest_offset(
kfraser@10418 457 exch.out.extent_start, (i<<out_chunk_order)+j, &mfn, 1);
kfraser@10418 458 }
kfraser@10418 459
kfraser@10418 460 j++;
kfraser@10418 461 }
kfraser@10418 462 BUG_ON(j != (1UL << out_chunk_order));
kfraser@10418 463 }
kfraser@10418 464
kfraser@12374 465 exch.nr_exchanged = exch.in.nr_extents;
kfraser@10418 466 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
kfraser@10418 467 rc = -EFAULT;
kfraser@10418 468 return rc;
kfraser@10418 469
kfraser@10418 470 /*
kfraser@10418 471 * Failed a chunk! Free any partial chunk work. Tell caller how many
kfraser@10418 472 * chunks succeeded.
kfraser@10418 473 */
kfraser@10418 474 fail:
kfraser@10418 475 /* Reassign any input pages we managed to steal. */
kfraser@10418 476 while ( !list_empty(&in_chunk_list) )
kfraser@10418 477 {
kfraser@10418 478 page = list_entry(in_chunk_list.next, struct page_info, list);
kfraser@10418 479 list_del(&page->list);
kfraser@10418 480 if ( assign_pages(d, page, 0, MEMF_no_refcount) )
kfraser@10418 481 BUG();
kfraser@10418 482 }
kfraser@10418 483
kfraser@10418 484 /* Free any output pages we managed to allocate. */
kfraser@10418 485 while ( !list_empty(&out_chunk_list) )
kfraser@10418 486 {
kfraser@10418 487 page = list_entry(out_chunk_list.next, struct page_info, list);
kfraser@10418 488 list_del(&page->list);
kfraser@10418 489 free_domheap_pages(page, exch.out.extent_order);
kfraser@10418 490 }
kfraser@10418 491
kfraser@12374 492 exch.nr_exchanged = i << in_chunk_order;
kfraser@10418 493
kfraser@10418 494 fail_early:
kfraser@10418 495 if ( copy_field_to_guest(arg, &exch, nr_exchanged) )
kfraser@10418 496 rc = -EFAULT;
kfraser@10418 497 return rc;
kfraser@10418 498 }
kfraser@10418 499
kaf24@9873 500 long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg)
kaf24@6486 501 {
kaf24@6486 502 struct domain *d;
kfraser@12374 503 int rc, op;
kaf24@8871 504 unsigned long start_extent, progress;
kaf24@6486 505 struct xen_memory_reservation reservation;
kfraser@12374 506 struct memop_args args;
kaf24@7959 507 domid_t domid;
kaf24@6486 508
ack@13295 509 op = cmd & MEMOP_CMD_MASK;
kaf24@6486 510
kaf24@6486 511 switch ( op )
kaf24@6486 512 {
kaf24@6486 513 case XENMEM_increase_reservation:
kaf24@6486 514 case XENMEM_decrease_reservation:
kaf24@8673 515 case XENMEM_populate_physmap:
ack@13295 516 start_extent = cmd >> MEMOP_EXTENT_SHIFT;
kfraser@10418 517
kaf24@9068 518 if ( copy_from_guest(&reservation, arg, 1) )
kfraser@10418 519 return start_extent;
kaf24@6486 520
kaf24@8871 521 /* Is size too large for us to encode a continuation? */
ack@13295 522 if ( reservation.nr_extents > (ULONG_MAX >> MEMOP_EXTENT_SHIFT) )
kfraser@10418 523 return start_extent;
kaf24@8871 524
kaf24@6486 525 if ( unlikely(start_extent > reservation.nr_extents) )
kfraser@10418 526 return start_extent;
kaf24@9068 527
kfraser@12374 528 args.extent_list = reservation.extent_start;
kfraser@12374 529 args.nr_extents = reservation.nr_extents;
kfraser@12374 530 args.extent_order = reservation.extent_order;
kfraser@12374 531 args.nr_done = start_extent;
kfraser@12374 532 args.preempted = 0;
kfraser@12374 533 args.memflags = 0;
kaf24@6486 534
kaf24@6701 535 if ( (reservation.address_bits != 0) &&
kaf24@6702 536 (reservation.address_bits <
kaf24@6702 537 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
kaf24@6486 538 {
kfraser@14103 539 if ( reservation.address_bits <= PAGE_SHIFT )
kfraser@10418 540 return start_extent;
kfraser@14103 541 args.memflags = MEMF_bits(reservation.address_bits);
kaf24@6486 542 }
kaf24@6486 543
kaf24@6486 544 if ( likely(reservation.domid == DOMID_SELF) )
keir@17349 545 {
keir@17349 546 d = rcu_lock_current_domain();
keir@17349 547 }
keir@17349 548 else
keir@17349 549 {
keir@17349 550 if ( (d = rcu_lock_domain_by_id(reservation.domid)) == NULL )
keir@16856 551 return start_extent;
keir@17349 552 if ( !IS_PRIV_FOR(current->domain, d) )
keir@17349 553 {
keir@16856 554 rcu_unlock_domain(d);
keir@16856 555 return start_extent;
keir@16856 556 }
keir@16856 557 }
kfraser@12374 558 args.domain = d;
kaf24@6486 559
kfraser@15815 560 rc = xsm_memory_adjust_reservation(current->domain, d);
kfraser@15815 561 if ( rc )
kfraser@15815 562 {
keir@17349 563 rcu_unlock_domain(d);
kfraser@15815 564 return rc;
kfraser@15815 565 }
kfraser@15815 566
kaf24@8673 567 switch ( op )
kaf24@8673 568 {
kaf24@8673 569 case XENMEM_increase_reservation:
kfraser@12374 570 increase_reservation(&args);
kaf24@8673 571 break;
kaf24@8673 572 case XENMEM_decrease_reservation:
kfraser@12374 573 decrease_reservation(&args);
kaf24@8673 574 break;
kfraser@12374 575 default: /* XENMEM_populate_physmap */
kfraser@12374 576 populate_physmap(&args);
kaf24@8673 577 break;
kaf24@8673 578 }
kaf24@6486 579
keir@17349 580 rcu_unlock_domain(d);
kaf24@6486 581
kfraser@12374 582 rc = args.nr_done;
kaf24@6486 583
kfraser@12374 584 if ( args.preempted )
kaf24@9068 585 return hypercall_create_continuation(
kaf24@9068 586 __HYPERVISOR_memory_op, "lh",
ack@13295 587 op | (rc << MEMOP_EXTENT_SHIFT), arg);
kaf24@6607 588
kaf24@6486 589 break;
kaf24@6486 590
kfraser@10418 591 case XENMEM_exchange:
kfraser@10418 592 rc = memory_exchange(guest_handle_cast(arg, xen_memory_exchange_t));
kfraser@10418 593 break;
kfraser@10418 594
kaf24@6486 595 case XENMEM_maximum_ram_page:
kaf24@7959 596 rc = max_page;
kaf24@7959 597 break;
kaf24@7959 598
kaf24@7959 599 case XENMEM_current_reservation:
kaf24@7959 600 case XENMEM_maximum_reservation:
kfraser@14471 601 case XENMEM_maximum_gpfn:
kaf24@9068 602 if ( copy_from_guest(&domid, arg, 1) )
kaf24@6486 603 return -EFAULT;
kaf24@7959 604
kaf24@9068 605 if ( likely(domid == DOMID_SELF) )
keir@17349 606 {
keir@17349 607 d = rcu_lock_current_domain();
keir@17349 608 }
keir@17349 609 else
keir@17349 610 {
keir@17349 611 if ( (d = rcu_lock_domain_by_id(domid)) == NULL )
keir@16856 612 return -ESRCH;
keir@17349 613 if ( !IS_PRIV_FOR(current->domain, d) )
keir@17349 614 {
keir@16856 615 rcu_unlock_domain(d);
keir@16856 616 return -EPERM;
keir@16856 617 }
keir@16856 618 }
kaf24@7959 619
kfraser@15815 620 rc = xsm_memory_stat_reservation(current->domain, d);
kfraser@15815 621 if ( rc )
kfraser@15815 622 {
keir@17349 623 rcu_unlock_domain(d);
kfraser@15815 624 return rc;
kfraser@15815 625 }
kfraser@15815 626
kfraser@14471 627 switch ( op )
kfraser@14471 628 {
kfraser@14471 629 case XENMEM_current_reservation:
kfraser@14471 630 rc = d->tot_pages;
kfraser@14471 631 break;
kfraser@14471 632 case XENMEM_maximum_reservation:
kfraser@14471 633 rc = d->max_pages;
kfraser@14471 634 break;
kfraser@14471 635 default:
kfraser@14471 636 ASSERT(op == XENMEM_maximum_gpfn);
kfraser@14471 637 rc = domain_get_maximum_gpfn(d);
kfraser@14471 638 break;
kfraser@14471 639 }
kaf24@7959 640
keir@17349 641 rcu_unlock_domain(d);
kaf24@7959 642
kaf24@6486 643 break;
kaf24@6486 644
kaf24@8871 645 case XENMEM_translate_gpfn_list:
ack@13295 646 progress = cmd >> MEMOP_EXTENT_SHIFT;
kaf24@9068 647 rc = translate_gpfn_list(
kaf24@9068 648 guest_handle_cast(arg, xen_translate_gpfn_list_t),
kaf24@9068 649 &progress);
kaf24@8871 650 if ( rc == -EAGAIN )
kaf24@9068 651 return hypercall_create_continuation(
kaf24@9068 652 __HYPERVISOR_memory_op, "lh",
ack@13295 653 op | (progress << MEMOP_EXTENT_SHIFT), arg);
kaf24@8871 654 break;
kaf24@8871 655
kaf24@6486 656 default:
kaf24@8059 657 rc = arch_memory_op(op, arg);
kaf24@6486 658 break;
kaf24@6486 659 }
kaf24@6486 660
kaf24@6486 661 return rc;
kaf24@6486 662 }
kaf24@6486 663
kaf24@6486 664 /*
kaf24@6486 665 * Local variables:
kaf24@6486 666 * mode: C
kaf24@6486 667 * c-set-style: "BSD"
kaf24@6486 668 * c-basic-offset: 4
kaf24@6486 669 * tab-width: 4
kaf24@6486 670 * indent-tabs-mode: nil
kaf24@6486 671 * End:
kaf24@6486 672 */