#include <mach/mach_types.h>
#include <mach/message.h>
#include <kern/mach_param.h>
#include <kern/misc_protos.h>
#include <mach/port.h>
#include <kern/lock.h>
#include <kern/ipc_kobject.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <kern/host.h>
#include <kern/ledger.h>
#include <mach/ledger_server.h>
ledger_t root_wired_ledger;
ledger_t root_paged_ledger;
kern_return_t
ledger_enter(
ledger_t ledger,
ledger_item_t amount)
{
ledger_lock(ledger);
if (amount > 0) {
if (ledger->ledger_limit != LEDGER_ITEM_INFINITY &&
ledger->ledger_balance + amount > ledger->ledger_limit) {
printf("Ledger limit exceeded ! ledger=%x lim=%d balance=%d\n",
ledger, ledger->ledger_limit,
ledger->ledger_balance);
ledger_unlock(ledger);
return(KERN_RESOURCE_SHORTAGE);
}
if ((natural_t)(ledger->ledger_balance + amount)
< LEDGER_ITEM_INFINITY)
ledger->ledger_balance += amount;
else
ledger->ledger_balance = LEDGER_ITEM_INFINITY;
}
else if (amount) {
if (ledger->ledger_balance + amount > 0)
ledger->ledger_balance += amount;
else
ledger->ledger_balance = 0;
}
ledger_unlock(ledger);
return(KERN_SUCCESS);
}
static ledger_t
ledger_allocate(
ledger_item_t limit,
ledger_t ledger_ledger,
ledger_t ledger_parent)
{
ledger_t ledger;
ledger = (ledger_t)kalloc(sizeof(ledger_data_t));
if (ledger == LEDGER_NULL)
return(LEDGER_NULL);
ledger->ledger_self = ipc_port_alloc_kernel();
if (ledger->ledger_self == IP_NULL)
return(LEDGER_NULL);
ledger_lock_init(ledger);
ledger->ledger_limit = limit;
ledger->ledger_balance = 0;
ledger->ledger_service_port = MACH_PORT_NULL;
ledger->ledger_ledger = ledger_ledger;
ledger->ledger_parent = ledger_parent;
ipc_kobject_set(ledger->ledger_self, (ipc_kobject_t)ledger,
IKOT_LEDGER);
return(ledger);
}
static void
ledger_deallocate(
ledger_t ledger)
{
ipc_port_dealloc_kernel(ledger->ledger_self);
kfree((vm_offset_t)ledger, sizeof(*ledger));
}
void ledger_init(void)
{
root_wired_ledger = ledger_allocate(LEDGER_ITEM_INFINITY,
LEDGER_NULL, LEDGER_NULL);
if (root_wired_ledger == LEDGER_NULL)
panic("can't allocate root (wired) ledger");
ipc_port_make_send(root_wired_ledger->ledger_self);
root_paged_ledger = ledger_allocate(LEDGER_ITEM_INFINITY,
LEDGER_NULL, LEDGER_NULL);
if (root_paged_ledger == LEDGER_NULL)
panic("can't allocate root (paged) ledger");
ipc_port_make_send(root_paged_ledger->ledger_self);
}
kern_return_t ledger_create(
ledger_t parent_ledger,
ledger_t ledger_ledger,
ledger_t *new_ledger,
ledger_item_t transfer)
{
if (parent_ledger == LEDGER_NULL)
return(KERN_INVALID_ARGUMENT);
if (ledger_ledger == LEDGER_NULL)
return(KERN_INVALID_LEDGER);
ledger_lock(ledger_ledger);
if ((ledger_ledger->ledger_limit != LEDGER_ITEM_INFINITY) &&
(ledger_ledger->ledger_balance + sizeof(ledger_data_t) >
ledger_ledger->ledger_limit)) {
ledger_unlock(ledger_ledger);
return(KERN_RESOURCE_SHORTAGE);
}
*new_ledger = ledger_allocate(LEDGER_ITEM_INFINITY, ledger_ledger, parent_ledger);
if (*new_ledger == LEDGER_NULL) {
ledger_unlock(ledger_ledger);
return(KERN_RESOURCE_SHORTAGE);
}
ledger_lock(parent_ledger);
if (parent_ledger->ledger_limit != LEDGER_ITEM_INFINITY) {
if (parent_ledger->ledger_limit - transfer < parent_ledger->ledger_balance) {
ledger_unlock(parent_ledger);
ledger_unlock(ledger_ledger);
return(KERN_RESOURCE_SHORTAGE);
}
if (parent_ledger->ledger_limit - transfer > 0)
parent_ledger->ledger_limit -= transfer;
else
parent_ledger->ledger_limit = 0;
}
(*new_ledger)->ledger_limit = transfer;
ledger_ledger->ledger_balance += sizeof(ledger_data_t);
ledger_unlock(parent_ledger);
ledger_unlock(ledger_ledger);
return(KERN_SUCCESS);
}
kern_return_t ledger_terminate(
ledger_t ledger)
{
if (ledger == LEDGER_NULL)
return(KERN_INVALID_ARGUMENT);
if (ledger == root_wired_ledger ||
ledger == root_paged_ledger)
return(KERN_INVALID_LEDGER);
ledger_lock(ledger);
ledger_lock(ledger->ledger_parent);
if (ledger->ledger_parent->ledger_limit != LEDGER_ITEM_INFINITY) {
assert((natural_t)(ledger->ledger_parent->ledger_limit +
ledger->ledger_limit) <
LEDGER_ITEM_INFINITY);
ledger->ledger_parent->ledger_limit += ledger->ledger_limit;
}
ledger_unlock(ledger->ledger_parent);
(void) ledger_enter(ledger->ledger_parent, ledger->ledger_balance);
(void) ledger_enter(ledger->ledger_ledger, -sizeof(*ledger));
ledger_deallocate(ledger);
return(KERN_SUCCESS);
}
kern_return_t ledger_read(
ledger_t ledger,
ledger_item_t *balance,
ledger_item_t *limit)
{
if (ledger == LEDGER_NULL)
return(KERN_INVALID_ARGUMENT);
ledger_lock(ledger);
*balance = ledger->ledger_balance;
*limit = ledger->ledger_limit;
ledger_unlock(ledger);
return(KERN_SUCCESS);
}
kern_return_t ledger_transfer(
ledger_t parent_ledger,
ledger_t child_ledger,
ledger_item_t transfer)
{
#define abs(v) ((v) > 0)?(v):-(v)
ledger_t src, dest;
ledger_item_t amount = abs(transfer);
if (parent_ledger == LEDGER_NULL)
return(KERN_INVALID_ARGUMENT);
if (child_ledger == LEDGER_NULL)
return(KERN_INVALID_ARGUMENT);
if (parent_ledger == child_ledger)
return(KERN_INVALID_ARGUMENT);
if (transfer == 0)
return(KERN_SUCCESS);
ledger_lock(child_ledger);
ledger_lock(parent_ledger);
if (parent_ledger != child_ledger->ledger_parent) {
ledger_unlock(parent_ledger);
ledger_unlock(child_ledger);
return(KERN_INVALID_LEDGER);
}
if (transfer > 0) {
dest = child_ledger;
src = parent_ledger;
}
else {
src = child_ledger;
dest = parent_ledger;
}
if (src->ledger_limit != LEDGER_ITEM_INFINITY) {
if (src->ledger_limit - amount < src->ledger_balance) {
ledger_unlock(parent_ledger);
ledger_unlock(child_ledger);
return(KERN_RESOURCE_SHORTAGE);
}
if (src->ledger_limit - amount > 0)
src->ledger_limit -= amount;
else
src->ledger_limit = 0;
}
if (dest->ledger_limit != LEDGER_ITEM_INFINITY) {
if ((natural_t)(dest->ledger_limit + amount)
< LEDGER_ITEM_INFINITY)
dest->ledger_limit += amount;
else
dest->ledger_limit = (LEDGER_ITEM_INFINITY - 1);
}
ledger_unlock(parent_ledger);
ledger_unlock(child_ledger);
return(KERN_SUCCESS);
#undef abs
}
ledger_t
convert_port_to_ledger(
ipc_port_t port)
{
ledger_t ledger = LEDGER_NULL;
if (IP_VALID(port)) {
ip_lock(port);
if (ip_active(port) &&
(ip_kotype(port) == IKOT_LEDGER))
ledger = (ledger_t) port->ip_kobject;
ip_unlock(port);
}
return ledger;
}
ipc_port_t
convert_ledger_to_port(
ledger_t ledger)
{
ipc_port_t port;
port = ipc_port_make_send(ledger->ledger_self);
return port;
}
ipc_port_t
ledger_copy(
ledger_t ledger)
{
assert(ledger);
return(ipc_port_copy_send(ledger->ledger_self));
}