summaryrefslogtreecommitdiff
path: root/vm/memory_object_proxy.c
blob: 4ad30030519aef36fbb298742629b6849e2ea9bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* memory_object_proxy.c - Proxy memory objects for Mach.
   Copyright (C) 2005 Free Software Foundation, Inc.
   Written by Marcus Brinkmann.

   This file is part of GNU Mach.

   GNU Mach is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Mach is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA. */

/* A proxy memory object is a kernel port that can be used like a real
   memory object in a vm_map call, except that the current and maximum
   protection are restricted to the proxy object's maximum protection
   at the time the mapping is established.  The kernel port will hold
   a reference to the real memory object for the life time of the
   proxy object.

   Note that we don't need to do any reference counting on the proxy
   object.  Our caller will hold a reference to the proxy object when
   looking it up, and is expected to acquire its own reference to the
   real memory object if needed before releasing the reference to the
   proxy object.

   The user provided real memory object and the maximum protection are
   not checked for validity.  The maximum protection is only used as a
   mask, and the memory object is validated at the time the mapping is
   established.  */

#include <mach/port.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/vm_prot.h>
#include <kern/printf.h>
#include <kern/slab.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_space.h>

#include <vm/memory_object_proxy.h>

/* The cache which holds our proxy memory objects.  */
static struct kmem_cache memory_object_proxy_cache;

struct memory_object_proxy
{
  struct ipc_port *port;

  ipc_port_t object;
  vm_prot_t max_protection;
  vm_offset_t start;
  vm_offset_t len;
};
typedef struct memory_object_proxy *memory_object_proxy_t;


void
memory_object_proxy_init (void)
{
  kmem_cache_init (&memory_object_proxy_cache, "memory_object_proxy",
		   sizeof (struct memory_object_proxy), 0, NULL, 0);
}

/* Lookup a proxy memory object by its port.  */
static memory_object_proxy_t
memory_object_proxy_port_lookup (ipc_port_t port)
{
  memory_object_proxy_t proxy;

  if (!IP_VALID(port))
    return 0;

  ip_lock (port);
  if (ip_active (port) && (ip_kotype (port) == IKOT_PAGER_PROXY))
    proxy = (memory_object_proxy_t) port->ip_kobject;
  else
    proxy = 0;
  ip_unlock (port);
  return proxy;
}


/* Process a no-sender notification for the proxy memory object
   port.  */
boolean_t
memory_object_proxy_notify (mach_msg_header_t *msg)
{
  if (msg->msgh_id == MACH_NOTIFY_NO_SENDERS)
    {
      memory_object_proxy_t proxy;
      mach_no_senders_notification_t *ns;

      ns = (mach_no_senders_notification_t *) msg;
      proxy = memory_object_proxy_port_lookup
	((ipc_port_t) ns->not_header.msgh_remote_port);
      assert (proxy);

      ipc_port_release_send (proxy->object);

      ipc_kobject_set (proxy->port, IKO_NULL, IKOT_NONE);
      ipc_port_dealloc_kernel (proxy->port);

      kmem_cache_free (&memory_object_proxy_cache, (vm_offset_t) proxy);

      return TRUE;
    }

  printf ("memory_object_proxy_notify: strange notification %d\n",
	  msg->msgh_id);
  return FALSE;
}


/* Create a new proxy memory object from [START;START+LEN) in the
   given OBJECT at OFFSET in the new object with the maximum
   protection MAX_PROTECTION and return it in *PORT.  */
kern_return_t
memory_object_create_proxy (const ipc_space_t space, vm_prot_t max_protection,
			    ipc_port_t *object, natural_t object_count,
			    const vm_offset_t *offset, natural_t offset_count,
			    const vm_offset_t *start, natural_t start_count,
			    const vm_offset_t *len, natural_t len_count,
			    ipc_port_t *port)
{
  memory_object_proxy_t proxy;
  ipc_port_t notify;

  if (space == IS_NULL)
    return KERN_INVALID_TASK;

  if (offset_count != object_count || start_count != object_count
      || len_count != object_count)
    return KERN_INVALID_ARGUMENT;

  /* FIXME: Support more than one memory object.  */
  if (object_count != 1)
    return KERN_INVALID_ARGUMENT;

  if (!IP_VALID(object[0]))
    return KERN_INVALID_NAME;

  /* FIXME: Support a different offset from 0.  */
  if (offset[0] != 0)
    return KERN_INVALID_ARGUMENT;

  if (start[0] + len[0] < start[0])
    return KERN_INVALID_ARGUMENT;

  proxy = (memory_object_proxy_t) kmem_cache_alloc (&memory_object_proxy_cache);

  /* Allocate port, keeping a reference for it.  */
  proxy->port = ipc_port_alloc_kernel ();
  if (proxy->port == IP_NULL)
    {
      kmem_cache_free (&memory_object_proxy_cache, (vm_offset_t) proxy);
      return KERN_RESOURCE_SHORTAGE;
    }
  /* Associate the port with the proxy memory object.  */
  ipc_kobject_set (proxy->port, (ipc_kobject_t) proxy, IKOT_PAGER_PROXY);

  /* Request no-senders notifications on the port.  */
  notify = ipc_port_make_sonce (proxy->port);
  ip_lock (proxy->port);
  ipc_port_nsrequest (proxy->port, 1, notify, &notify);
  assert (notify == IP_NULL);

  /* Consumes the port right */
  proxy->object = object[0];
  proxy->max_protection = max_protection;
  proxy->start = start[0];
  proxy->len = len[0];

  *port = ipc_port_make_send (proxy->port);
  return KERN_SUCCESS;
}

/* Lookup the real memory object and maximum protection for the proxy
   memory object port PORT, for which the caller holds a reference.
   *OBJECT is only guaranteed to be valid as long as the caller holds
   the reference to PORT (unless the caller acquires its own reference
   to it).  If PORT is not a proxy memory object, return
   KERN_INVALID_ARGUMENT.  */
kern_return_t
memory_object_proxy_lookup (ipc_port_t port, ipc_port_t *object,
			    vm_prot_t *max_protection, vm_offset_t *start,
			    vm_offset_t *len)
{
  memory_object_proxy_t proxy;

  proxy = memory_object_proxy_port_lookup (port);
  if (!proxy)
    return KERN_INVALID_ARGUMENT;

  *max_protection = proxy->max_protection;
  *start = 0;
  *len = (vm_offset_t) ~0;

  do
    {
      *object = proxy->object;
      if (proxy->len <= *start)
	*len = 0;
      else
	*len = MIN(*len, proxy->len - *start);
      *start += proxy->start;
    }
  while ((proxy = memory_object_proxy_port_lookup (proxy->object)));

  return KERN_SUCCESS;
}