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