ia64/xen-unstable

annotate xen/common/memory.c @ 9068:c369d960f96b

Sketch a new interface for transferring hypercall arguments in memory.
Instead of manipulating guest virtual addresses, 'guest handles' are
passed across the hypercall interface, which may only be manipulated
via the interface exported by guest_access.h.

So far this has only been applied to the memory_op hypercall. The
interfaces are still subject to change. Other hypercalls can be updated
after the interface is agreed upon.

Also cleaned up the hypercall_create_continuation() interface to take
a format string and different-typed varargs (they do not all need to
be castable to longs).

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Feb 28 17:45:20 2006 +0100 (2006-02-28)
parents 0d10fac28427
children 4dd325c1d87d
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>
kaf24@6486 17 #include <xen/shadow.h>
kaf24@8468 18 #include <xen/iocap.h>
kaf24@9068 19 #include <xen/guest_access.h>
kaf24@6486 20 #include <asm/current.h>
kaf24@6486 21 #include <asm/hardirq.h>
kaf24@6486 22 #include <public/memory.h>
kaf24@6486 23
kaf24@8871 24 /*
kaf24@8871 25 * To allow safe resume of do_memory_op() after preemption, we need to know
kaf24@8871 26 * at what point in the page list to resume. For this purpose I steal the
kaf24@8871 27 * high-order bits of the @cmd parameter, which are otherwise unused and zero.
kaf24@8871 28 */
kaf24@8871 29 #define START_EXTENT_SHIFT 4 /* cmd[:4] == start_extent */
kaf24@8871 30
kaf24@6486 31 static long
kaf24@6486 32 increase_reservation(
kaf24@6486 33 struct domain *d,
kaf24@9068 34 GUEST_HANDLE(xen_ulong) extent_list,
kaf24@6486 35 unsigned int nr_extents,
kaf24@6486 36 unsigned int extent_order,
kaf24@6607 37 unsigned int flags,
kaf24@6607 38 int *preempted)
kaf24@6486 39 {
kaf24@8726 40 struct page_info *page;
kaf24@8859 41 unsigned long i, mfn;
kaf24@6486 42
kaf24@9068 43 if ( !guest_handle_is_null(extent_list) &&
kaf24@9068 44 !guest_handle_okay(extent_list, nr_extents) )
kaf24@6486 45 return 0;
kaf24@6486 46
kaf24@8468 47 if ( (extent_order != 0) &&
kaf24@8468 48 !multipage_allocation_permitted(current->domain) )
kaf24@6486 49 return 0;
kaf24@6486 50
kaf24@6486 51 for ( i = 0; i < nr_extents; i++ )
kaf24@6486 52 {
kaf24@6486 53 if ( hypercall_preempt_check() )
kaf24@6607 54 {
kaf24@6607 55 *preempted = 1;
kaf24@6486 56 return i;
kaf24@6607 57 }
kaf24@6486 58
kaf24@6486 59 if ( unlikely((page = alloc_domheap_pages(
kaf24@6486 60 d, extent_order, flags)) == NULL) )
kaf24@6486 61 {
kaf24@6752 62 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 63 "id=%d flags=%x (%ld of %d)\n",
kaf24@6752 64 extent_order, d->domain_id, flags, i, nr_extents);
kaf24@6486 65 return i;
kaf24@6486 66 }
kaf24@6486 67
kaf24@6486 68 /* Inform the domain of the new page's machine address. */
kaf24@9068 69 if ( !guest_handle_is_null(extent_list) )
kaf24@8859 70 {
kaf24@8859 71 mfn = page_to_mfn(page);
kaf24@9068 72 if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
kaf24@8859 73 return i;
kaf24@8859 74 }
kaf24@6486 75 }
kaf24@6486 76
kaf24@6486 77 return nr_extents;
kaf24@6486 78 }
sos22@8688 79
kaf24@6486 80 static long
kaf24@8673 81 populate_physmap(
kaf24@8673 82 struct domain *d,
kaf24@9068 83 GUEST_HANDLE(xen_ulong) extent_list,
kaf24@9068 84 unsigned int nr_extents,
kaf24@9068 85 unsigned int extent_order,
kaf24@9068 86 unsigned int flags,
kaf24@9068 87 int *preempted)
kaf24@8673 88 {
kaf24@8726 89 struct page_info *page;
kaf24@8736 90 unsigned long i, j, gpfn, mfn;
kaf24@8673 91
kaf24@9068 92 if ( !guest_handle_okay(extent_list, nr_extents) )
kaf24@8673 93 return 0;
kaf24@8673 94
kaf24@8673 95 if ( (extent_order != 0) &&
kaf24@8673 96 !multipage_allocation_permitted(current->domain) )
kaf24@8673 97 return 0;
kaf24@8673 98
kaf24@8673 99 for ( i = 0; i < nr_extents; i++ )
kaf24@8673 100 {
kaf24@8673 101 if ( hypercall_preempt_check() )
kaf24@8673 102 {
kaf24@8673 103 *preempted = 1;
sos22@8688 104 goto out;
kaf24@8673 105 }
kaf24@8673 106
kaf24@9068 107 if ( unlikely(__copy_from_guest_offset(&gpfn, extent_list, i, 1)) )
kaf24@8859 108 goto out;
kaf24@8859 109
kaf24@8673 110 if ( unlikely((page = alloc_domheap_pages(
kaf24@8673 111 d, extent_order, flags)) == NULL) )
kaf24@8673 112 {
kaf24@8673 113 DPRINTK("Could not allocate order=%d extent: "
kaf24@8673 114 "id=%d flags=%x (%ld of %d)\n",
kaf24@8673 115 extent_order, d->domain_id, flags, i, nr_extents);
sos22@8688 116 goto out;
kaf24@8673 117 }
kaf24@8673 118
kaf24@8726 119 mfn = page_to_mfn(page);
kaf24@8673 120
kaf24@8694 121 if ( unlikely(shadow_mode_translate(d)) )
kaf24@8694 122 {
kaf24@8694 123 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@8736 124 guest_physmap_add_page(d, gpfn + j, mfn + j);
sos22@8688 125 }
kaf24@8694 126 else
kaf24@8694 127 {
kaf24@8694 128 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@8736 129 set_gpfn_from_mfn(mfn + j, gpfn + j);
kaf24@8673 130
sos22@8688 131 /* Inform the domain of the new page's machine address. */
kaf24@9068 132 if ( unlikely(__copy_to_guest_offset(extent_list, i, &mfn, 1)) )
sos22@8688 133 goto out;
sos22@8688 134 }
kaf24@8673 135 }
kaf24@8673 136
sos22@8688 137 out:
sos22@8688 138 return i;
kaf24@8673 139 }
kaf24@8673 140
kaf24@8673 141 static long
kaf24@6486 142 decrease_reservation(
kaf24@9068 143 struct domain *d,
kaf24@9068 144 GUEST_HANDLE(xen_ulong) extent_list,
kaf24@6486 145 unsigned int nr_extents,
kaf24@6486 146 unsigned int extent_order,
kaf24@6607 147 unsigned int flags,
kaf24@6607 148 int *preempted)
kaf24@6486 149 {
kaf24@8726 150 struct page_info *page;
kaf24@8726 151 unsigned long i, j, gmfn, mfn;
kaf24@6486 152
kaf24@9068 153 if ( !guest_handle_okay(extent_list, nr_extents) )
kaf24@6486 154 return 0;
kaf24@6486 155
kaf24@6486 156 for ( i = 0; i < nr_extents; i++ )
kaf24@6486 157 {
kaf24@6486 158 if ( hypercall_preempt_check() )
kaf24@6607 159 {
kaf24@6607 160 *preempted = 1;
kaf24@6486 161 return i;
kaf24@6607 162 }
kaf24@6486 163
kaf24@9068 164 if ( unlikely(__copy_from_guest_offset(&gmfn, extent_list, i, 1)) )
kaf24@6486 165 return i;
kaf24@6486 166
kaf24@6486 167 for ( j = 0; j < (1 << extent_order); j++ )
kaf24@6486 168 {
kaf24@8726 169 mfn = gmfn_to_mfn(d, gmfn + j);
kaf24@8736 170 if ( unlikely(!mfn_valid(mfn)) )
kaf24@6486 171 {
kaf24@8736 172 DPRINTK("Domain %u page number %lx invalid\n",
kaf24@8736 173 d->domain_id, mfn);
kaf24@6486 174 return i;
kaf24@6486 175 }
kaf24@6486 176
kaf24@8726 177 page = mfn_to_page(mfn);
kaf24@6486 178 if ( unlikely(!get_page(page, d)) )
kaf24@6486 179 {
kaf24@6486 180 DPRINTK("Bad page free for domain %u\n", d->domain_id);
kaf24@6486 181 return i;
kaf24@6486 182 }
kaf24@6486 183
kaf24@6486 184 if ( test_and_clear_bit(_PGT_pinned, &page->u.inuse.type_info) )
kaf24@6486 185 put_page_and_type(page);
kaf24@6486 186
kaf24@6486 187 if ( test_and_clear_bit(_PGC_allocated, &page->count_info) )
kaf24@6486 188 put_page(page);
kaf24@6486 189
kaf24@8726 190 guest_physmap_remove_page(d, gmfn + j, mfn);
kaf24@8694 191
kaf24@6486 192 put_page(page);
kaf24@6486 193 }
kaf24@6486 194 }
kaf24@6486 195
kaf24@6486 196 return nr_extents;
kaf24@6486 197 }
kaf24@6486 198
kaf24@8871 199 static long
kaf24@8871 200 translate_gpfn_list(
kaf24@9068 201 GUEST_HANDLE(xen_translate_gpfn_list_t) uop, unsigned long *progress)
kaf24@8871 202 {
kaf24@8871 203 struct xen_translate_gpfn_list op;
kaf24@8871 204 unsigned long i, gpfn, mfn;
kaf24@8871 205 struct domain *d;
kaf24@6486 206
kaf24@9068 207 if ( copy_from_guest(&op, uop, 1) )
kaf24@8871 208 return -EFAULT;
kaf24@8871 209
kaf24@8871 210 /* Is size too large for us to encode a continuation? */
kaf24@8871 211 if ( op.nr_gpfns > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 212 return -EINVAL;
kaf24@8871 213
kaf24@9068 214 if ( !guest_handle_okay(op.gpfn_list, op.nr_gpfns) ||
kaf24@9068 215 !guest_handle_okay(op.mfn_list, op.nr_gpfns) )
kaf24@8871 216 return -EFAULT;
kaf24@8871 217
kaf24@8871 218 if ( op.domid == DOMID_SELF )
kaf24@8871 219 op.domid = current->domain->domain_id;
kaf24@8871 220 else if ( !IS_PRIV(current->domain) )
kaf24@8871 221 return -EPERM;
kaf24@8871 222
kaf24@8871 223 if ( (d = find_domain_by_id(op.domid)) == NULL )
kaf24@8871 224 return -ESRCH;
kaf24@8871 225
kaf24@8871 226 if ( !shadow_mode_translate(d) )
kaf24@8871 227 {
kaf24@8871 228 put_domain(d);
kaf24@8871 229 return -EINVAL;
kaf24@8871 230 }
kaf24@8871 231
kaf24@8871 232 for ( i = *progress; i < op.nr_gpfns; i++ )
kaf24@8871 233 {
kaf24@8871 234 if ( hypercall_preempt_check() )
kaf24@8871 235 {
kaf24@8871 236 put_domain(d);
kaf24@8871 237 *progress = i;
kaf24@8871 238 return -EAGAIN;
kaf24@8871 239 }
kaf24@8871 240
kaf24@9068 241 if ( unlikely(__copy_from_guest_offset(&gpfn, op.gpfn_list, i, 1)) )
kaf24@8871 242 {
kaf24@8871 243 put_domain(d);
kaf24@8871 244 return -EFAULT;
kaf24@8871 245 }
kaf24@8871 246
kaf24@8871 247 mfn = gmfn_to_mfn(d, gpfn);
kaf24@8871 248
kaf24@9068 249 if ( unlikely(__copy_to_guest_offset(op.mfn_list, i, &mfn, 1)) )
kaf24@8871 250 {
kaf24@8871 251 put_domain(d);
kaf24@8871 252 return -EFAULT;
kaf24@8871 253 }
kaf24@8871 254 }
kaf24@8871 255
kaf24@8871 256 put_domain(d);
kaf24@8871 257 return 0;
kaf24@8871 258 }
kaf24@8871 259
kaf24@9068 260 long do_memory_op(unsigned long cmd, GUEST_HANDLE(void) arg)
kaf24@6486 261 {
kaf24@6486 262 struct domain *d;
kaf24@8871 263 int rc, op, flags = 0, preempted = 0;
kaf24@8871 264 unsigned long start_extent, progress;
kaf24@6486 265 struct xen_memory_reservation reservation;
kaf24@7959 266 domid_t domid;
kaf24@6486 267
kaf24@6486 268 op = cmd & ((1 << START_EXTENT_SHIFT) - 1);
kaf24@6486 269
kaf24@6486 270 switch ( op )
kaf24@6486 271 {
kaf24@6486 272 case XENMEM_increase_reservation:
kaf24@6486 273 case XENMEM_decrease_reservation:
kaf24@8673 274 case XENMEM_populate_physmap:
kaf24@9068 275 if ( copy_from_guest(&reservation, arg, 1) )
kaf24@6486 276 return -EFAULT;
kaf24@6486 277
kaf24@8871 278 /* Is size too large for us to encode a continuation? */
kaf24@8871 279 if ( reservation.nr_extents > (ULONG_MAX >> START_EXTENT_SHIFT) )
kaf24@8871 280 return -EINVAL;
kaf24@8871 281
kaf24@6486 282 start_extent = cmd >> START_EXTENT_SHIFT;
kaf24@6486 283 if ( unlikely(start_extent > reservation.nr_extents) )
kaf24@6486 284 return -EINVAL;
kaf24@9068 285
kaf24@9068 286 if ( !guest_handle_is_null(reservation.extent_start) )
kaf24@9068 287 guest_handle_add_offset(reservation.extent_start, start_extent);
kaf24@6486 288 reservation.nr_extents -= start_extent;
kaf24@6486 289
kaf24@6701 290 if ( (reservation.address_bits != 0) &&
kaf24@6702 291 (reservation.address_bits <
kaf24@6702 292 (get_order_from_pages(max_page) + PAGE_SHIFT)) )
kaf24@6486 293 {
kaf24@6486 294 if ( reservation.address_bits < 31 )
kaf24@6486 295 return -ENOMEM;
kaf24@6486 296 flags = ALLOC_DOM_DMA;
kaf24@6486 297 }
kaf24@6486 298
kaf24@6486 299 if ( likely(reservation.domid == DOMID_SELF) )
kaf24@6486 300 d = current->domain;
kaf24@6486 301 else if ( !IS_PRIV(current->domain) )
kaf24@6486 302 return -EPERM;
kaf24@6486 303 else if ( (d = find_domain_by_id(reservation.domid)) == NULL )
kaf24@6486 304 return -ESRCH;
kaf24@6486 305
kaf24@8673 306 switch ( op )
kaf24@8673 307 {
kaf24@8673 308 case XENMEM_increase_reservation:
kaf24@8673 309 rc = increase_reservation(
kaf24@8673 310 d,
kaf24@8673 311 reservation.extent_start,
kaf24@8673 312 reservation.nr_extents,
kaf24@8673 313 reservation.extent_order,
kaf24@8673 314 flags,
kaf24@8673 315 &preempted);
kaf24@8673 316 break;
kaf24@8673 317 case XENMEM_decrease_reservation:
kaf24@8673 318 rc = decrease_reservation(
kaf24@8673 319 d,
kaf24@8673 320 reservation.extent_start,
kaf24@8673 321 reservation.nr_extents,
kaf24@8673 322 reservation.extent_order,
kaf24@8673 323 flags,
kaf24@8673 324 &preempted);
kaf24@8673 325 break;
kaf24@8673 326 case XENMEM_populate_physmap:
kaf24@8673 327 default:
kaf24@8673 328 rc = populate_physmap(
kaf24@8673 329 d,
kaf24@8673 330 reservation.extent_start,
kaf24@8673 331 reservation.nr_extents,
kaf24@8673 332 reservation.extent_order,
kaf24@8673 333 flags,
kaf24@8673 334 &preempted);
kaf24@8673 335 break;
kaf24@8673 336 }
kaf24@6486 337
kaf24@6486 338 if ( unlikely(reservation.domid != DOMID_SELF) )
kaf24@6486 339 put_domain(d);
kaf24@6486 340
kaf24@6486 341 rc += start_extent;
kaf24@6486 342
kaf24@6607 343 if ( preempted )
kaf24@9068 344 return hypercall_create_continuation(
kaf24@9068 345 __HYPERVISOR_memory_op, "lh",
kaf24@9068 346 op | (rc << START_EXTENT_SHIFT), arg);
kaf24@6607 347
kaf24@6486 348 break;
kaf24@6486 349
kaf24@6486 350 case XENMEM_maximum_ram_page:
kaf24@7959 351 rc = max_page;
kaf24@7959 352 break;
kaf24@7959 353
kaf24@7959 354 case XENMEM_current_reservation:
kaf24@7959 355 case XENMEM_maximum_reservation:
kaf24@9068 356 if ( copy_from_guest(&domid, arg, 1) )
kaf24@6486 357 return -EFAULT;
kaf24@7959 358
kaf24@9068 359 if ( likely(domid == DOMID_SELF) )
kaf24@7959 360 d = current->domain;
kaf24@7959 361 else if ( !IS_PRIV(current->domain) )
kaf24@7959 362 return -EPERM;
kaf24@7959 363 else if ( (d = find_domain_by_id(domid)) == NULL )
kaf24@7959 364 return -ESRCH;
kaf24@7959 365
kaf24@7959 366 rc = (op == XENMEM_current_reservation) ? d->tot_pages : d->max_pages;
kaf24@7959 367
kaf24@7959 368 if ( unlikely(domid != DOMID_SELF) )
kaf24@7959 369 put_domain(d);
kaf24@7959 370
kaf24@6486 371 break;
kaf24@6486 372
kaf24@8871 373 case XENMEM_translate_gpfn_list:
kaf24@8871 374 progress = cmd >> START_EXTENT_SHIFT;
kaf24@9068 375 rc = translate_gpfn_list(
kaf24@9068 376 guest_handle_cast(arg, xen_translate_gpfn_list_t),
kaf24@9068 377 &progress);
kaf24@8871 378 if ( rc == -EAGAIN )
kaf24@9068 379 return hypercall_create_continuation(
kaf24@9068 380 __HYPERVISOR_memory_op, "lh",
kaf24@9068 381 op | (progress << START_EXTENT_SHIFT), arg);
kaf24@8871 382 break;
kaf24@8871 383
kaf24@6486 384 default:
kaf24@8059 385 rc = arch_memory_op(op, arg);
kaf24@6486 386 break;
kaf24@6486 387 }
kaf24@6486 388
kaf24@6486 389 return rc;
kaf24@6486 390 }
kaf24@6486 391
kaf24@6486 392 /*
kaf24@6486 393 * Local variables:
kaf24@6486 394 * mode: C
kaf24@6486 395 * c-set-style: "BSD"
kaf24@6486 396 * c-basic-offset: 4
kaf24@6486 397 * tab-width: 4
kaf24@6486 398 * indent-tabs-mode: nil
kaf24@6486 399 * End:
kaf24@6486 400 */