1/* 2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * @OSF_COPYRIGHT@ 30 */ 31/* 32 * 8/13/93 33 * 34 * This is a half-hearted attempt at providing the parts of the 35 * ledger facility to satisfy the ledger interfaces. 36 * 37 * This implementation basically leaves the (dysfunctional) ledgers 38 * unfunctional and are mearly here to satisfy the Mach spec interface 39 * reqirements. 40 */ 41 42#include <mach/mach_types.h> 43#include <mach/message.h> 44#include <mach/port.h> 45#include <mach/ledger_server.h> 46 47#include <kern/mach_param.h> 48#include <kern/misc_protos.h> 49#include <kern/lock.h> 50#include <kern/ipc_kobject.h> 51#include <kern/host.h> 52#include <kern/ledger.h> 53#include <kern/kalloc.h> 54 55#include <ipc/ipc_space.h> 56#include <ipc/ipc_port.h> 57 58ledger_t root_wired_ledger; 59ledger_t root_paged_ledger; 60 61 62/* Utility routine to handle entries to a ledger */ 63kern_return_t 64ledger_enter( 65 ledger_t ledger, 66 ledger_item_t amount) 67{ 68 /* Need to lock the ledger */ 69 ledger_lock(ledger); 70 71 if (amount > 0) { 72 if (ledger->ledger_limit != LEDGER_ITEM_INFINITY && 73 ledger->ledger_balance + amount > ledger->ledger_limit) { 74 /* XXX this is where you do BAD things */ 75 printf("Ledger limit exceeded ! ledger=%p lim=%d balance=%d\n", 76 ledger, ledger->ledger_limit, 77 ledger->ledger_balance); 78 ledger_unlock(ledger); 79 return(KERN_RESOURCE_SHORTAGE); 80 } 81 if ((ledger->ledger_balance + amount) 82 < LEDGER_ITEM_INFINITY) 83 ledger->ledger_balance += amount; 84 else 85 ledger->ledger_balance = LEDGER_ITEM_INFINITY; 86 } 87 else if (amount) { 88 if (ledger->ledger_balance + amount > 0) 89 ledger->ledger_balance += amount; 90 else 91 ledger->ledger_balance = 0; 92 } 93 ledger_unlock(ledger); 94 return(KERN_SUCCESS); 95} 96 97/* Utility routine to create a new ledger */ 98static ledger_t 99ledger_allocate( 100 ledger_item_t limit, 101 ledger_t ledger_ledger, 102 ledger_t ledger_parent) 103{ 104 ledger_t ledger; 105 106 ledger = (ledger_t)kalloc(sizeof(ledger_data_t)); 107 if (ledger == LEDGER_NULL) 108 return(LEDGER_NULL); 109 110 ledger->ledger_self = ipc_port_alloc_kernel(); 111 if (ledger->ledger_self == IP_NULL) { 112 kfree(ledger, sizeof(ledger_data_t)); 113 return(LEDGER_NULL); 114 } 115 116 ledger_lock_init(ledger); 117 ledger->ledger_limit = limit; 118 ledger->ledger_balance = 0; 119 ledger->ledger_service_port = MACH_PORT_NULL; 120 ledger->ledger_ledger = ledger_ledger; 121 ledger->ledger_parent = ledger_parent; 122 ipc_kobject_set(ledger->ledger_self, (ipc_kobject_t)ledger, 123 IKOT_LEDGER); 124 125 return(ledger); 126} 127 128/* Utility routine to destroy a ledger */ 129static void 130ledger_deallocate( 131 ledger_t ledger) 132{ 133 /* XXX can be many send rights (copies) of this */ 134 ipc_port_dealloc_kernel(ledger->ledger_self); 135 136 /* XXX release send right on service port */ 137 kfree(ledger, sizeof(*ledger)); 138} 139 140 141/* 142 * Inititalize the ledger facility 143 */ 144void ledger_init(void) 145{ 146 /* 147 * Allocate the root ledgers; wired and paged. 148 */ 149 root_wired_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, 150 LEDGER_NULL, LEDGER_NULL); 151 if (root_wired_ledger == LEDGER_NULL) 152 panic("can't allocate root (wired) ledger"); 153 ipc_port_make_send(root_wired_ledger->ledger_self); 154 155 root_paged_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, 156 LEDGER_NULL, LEDGER_NULL); 157 if (root_paged_ledger == LEDGER_NULL) 158 panic("can't allocate root (paged) ledger"); 159 ipc_port_make_send(root_paged_ledger->ledger_self); 160} 161 162/* 163 * Create a subordinate ledger 164 */ 165kern_return_t ledger_create( 166 ledger_t parent_ledger, 167 ledger_t ledger_ledger, 168 ledger_t *new_ledger, 169 ledger_item_t transfer) 170{ 171 if (parent_ledger == LEDGER_NULL) 172 return(KERN_INVALID_ARGUMENT); 173 174 if (ledger_ledger == LEDGER_NULL) 175 return(KERN_INVALID_LEDGER); 176 177 /* 178 * Allocate a new ledger and change the ledger_ledger for 179 * its space. 180 */ 181 ledger_lock(ledger_ledger); 182 if ((ledger_ledger->ledger_limit != LEDGER_ITEM_INFINITY) && 183 (ledger_ledger->ledger_balance + sizeof(ledger_data_t) > 184 ledger_ledger->ledger_limit)) { 185 ledger_unlock(ledger_ledger); 186 return(KERN_RESOURCE_SHORTAGE); 187 } 188 189 *new_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, ledger_ledger, parent_ledger); 190 if (*new_ledger == LEDGER_NULL) { 191 ledger_unlock(ledger_ledger); 192 return(KERN_RESOURCE_SHORTAGE); 193 } 194 195 /* 196 * Now transfer the limit for the new ledger from the parent 197 */ 198 ledger_lock(parent_ledger); 199 if (parent_ledger->ledger_limit != LEDGER_ITEM_INFINITY) { 200 /* Would the existing balance exceed the new limit ? */ 201 if (parent_ledger->ledger_limit - transfer < parent_ledger->ledger_balance) { 202 ledger_unlock(parent_ledger); 203 ledger_unlock(ledger_ledger); 204 return(KERN_RESOURCE_SHORTAGE); 205 } 206 if (parent_ledger->ledger_limit - transfer > 0) 207 parent_ledger->ledger_limit -= transfer; 208 else 209 parent_ledger->ledger_limit = 0; 210 } 211 (*new_ledger)->ledger_limit = transfer; 212 213 /* Charge the ledger against the ledger_ledger */ 214 ledger_ledger->ledger_balance += sizeof(ledger_data_t); 215 ledger_unlock(parent_ledger); 216 217 ledger_unlock(ledger_ledger); 218 219 return(KERN_SUCCESS); 220} 221 222/* 223 * Destroy a ledger 224 */ 225kern_return_t ledger_terminate( 226 ledger_t ledger) 227{ 228 if (ledger == LEDGER_NULL) 229 return(KERN_INVALID_ARGUMENT); 230 231 /* You can't deallocate kernel ledgers */ 232 if (ledger == root_wired_ledger || 233 ledger == root_paged_ledger) 234 return(KERN_INVALID_LEDGER); 235 236 /* Lock the ledger */ 237 ledger_lock(ledger); 238 239 /* the parent ledger gets back the limit */ 240 ledger_lock(ledger->ledger_parent); 241 if (ledger->ledger_parent->ledger_limit != LEDGER_ITEM_INFINITY) { 242 assert((natural_t)(ledger->ledger_parent->ledger_limit + 243 ledger->ledger_limit) < 244 LEDGER_ITEM_INFINITY); 245 ledger->ledger_parent->ledger_limit += ledger->ledger_limit; 246 } 247 ledger_unlock(ledger->ledger_parent); 248 249 /* 250 * XXX The spec says that you have to destroy all objects that 251 * have been created with this ledger. Nice work eh? For now 252 * Transfer the balance to the parent and let it worry about 253 * it. 254 */ 255 /* XXX the parent ledger inherits the debt ?? */ 256 (void) ledger_enter(ledger->ledger_parent, ledger->ledger_balance); 257 258 /* adjust the balance of the creation ledger */ 259 (void) ledger_enter(ledger->ledger_ledger, -sizeof(*ledger)); 260 261 /* delete the ledger */ 262 ledger_deallocate(ledger); 263 264 return(KERN_SUCCESS); 265} 266 267/* 268 * Return the ledger limit and balance 269 */ 270kern_return_t ledger_read( 271 ledger_t ledger, 272 ledger_item_t *balance, 273 ledger_item_t *limit) 274{ 275 if (ledger == LEDGER_NULL) 276 return(KERN_INVALID_ARGUMENT); 277 278 ledger_lock(ledger); 279 *balance = ledger->ledger_balance; 280 *limit = ledger->ledger_limit; 281 ledger_unlock(ledger); 282 283 return(KERN_SUCCESS); 284} 285 286/* 287 * Transfer resources from a parent ledger to a child 288 */ 289kern_return_t ledger_transfer( 290 ledger_t parent_ledger, 291 ledger_t child_ledger, 292 ledger_item_t transfer) 293{ 294#define abs(v) ((v) > 0)?(v):-(v) 295 296 ledger_t src, dest; 297 ledger_item_t amount = abs(transfer); 298 299 if (parent_ledger == LEDGER_NULL) 300 return(KERN_INVALID_ARGUMENT); 301 302 if (child_ledger == LEDGER_NULL) 303 return(KERN_INVALID_ARGUMENT); 304 305 /* Must be different ledgers */ 306 if (parent_ledger == child_ledger) 307 return(KERN_INVALID_ARGUMENT); 308 309 if (transfer == 0) 310 return(KERN_SUCCESS); 311 312 ledger_lock(child_ledger); 313 ledger_lock(parent_ledger); 314 315 /* XXX Should be the parent you created it from ?? */ 316 if (parent_ledger != child_ledger->ledger_parent) { 317 ledger_unlock(parent_ledger); 318 ledger_unlock(child_ledger); 319 return(KERN_INVALID_LEDGER); 320 } 321 322 if (transfer > 0) { 323 dest = child_ledger; 324 src = parent_ledger; 325 } 326 else { 327 src = child_ledger; 328 dest = parent_ledger; 329 } 330 331 if (src->ledger_limit != LEDGER_ITEM_INFINITY) { 332 /* Would the existing balance exceed the new limit ? */ 333 if (src->ledger_limit - amount < src->ledger_balance) { 334 ledger_unlock(parent_ledger); 335 ledger_unlock(child_ledger); 336 return(KERN_RESOURCE_SHORTAGE); 337 } 338 if (src->ledger_limit - amount > 0) 339 src->ledger_limit -= amount; 340 else 341 src->ledger_limit = 0; 342 } 343 344 if (dest->ledger_limit != LEDGER_ITEM_INFINITY) { 345 if ((natural_t)(dest->ledger_limit + amount) 346 < LEDGER_ITEM_INFINITY) 347 dest->ledger_limit += amount; 348 else 349 dest->ledger_limit = (LEDGER_ITEM_INFINITY - 1); 350 } 351 352 ledger_unlock(parent_ledger); 353 ledger_unlock(child_ledger); 354 355 return(KERN_SUCCESS); 356#undef abs 357} 358 359/* 360 * Routine: convert_port_to_ledger 361 * Purpose: 362 * Convert from a port to a ledger. 363 * Doesn't consume the port ref; the ledger produced may be null. 364 * Conditions: 365 * Nothing locked. 366 */ 367 368ledger_t 369convert_port_to_ledger( 370 ipc_port_t port) 371{ 372 ledger_t ledger = LEDGER_NULL; 373 374 if (IP_VALID(port)) { 375 ip_lock(port); 376 if (ip_active(port) && 377 (ip_kotype(port) == IKOT_LEDGER)) 378 ledger = (ledger_t) port->ip_kobject; 379 ip_unlock(port); 380 } 381 382 return ledger; 383} 384 385/* 386 * Routine: convert_ledger_to_port 387 * Purpose: 388 * Convert from a ledger to a port. 389 * Produces a naked send right which may be invalid. 390 * Conditions: 391 * Nothing locked. 392 */ 393 394ipc_port_t 395convert_ledger_to_port( 396 ledger_t ledger) 397{ 398 ipc_port_t port; 399 400 port = ipc_port_make_send(ledger->ledger_self); 401 402 return port; 403} 404 405/* 406 * Copy a ledger 407 */ 408ipc_port_t 409ledger_copy( 410 ledger_t ledger) 411{ 412 /* XXX reference counting */ 413 assert(ledger); 414 return(ipc_port_copy_send(ledger->ledger_self)); 415} 416