summaryrefslogtreecommitdiff
path: root/physmem/priv.h
blob: d86357b02fc401ce2b2cde9207d12e385410cc23 (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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/* physmem.c - Generic definitions.
   Copyright (C) 2003, 2005 Free Software Foundation, Inc.
   Written by Neal H. Walfield <neal@gnu.org>.

   This file is part of the GNU Hurd.

   The GNU Hurd 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.
   
   The GNU Hurd 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 the GNU Hurd; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
   USA.  */

#include <errno.h>
#include <l4.h>
#include <hurd/btree.h>
#include <hurd/cap-server.h>

#include <compiler.h>

#include "output.h"


/* The program name.  */
extern char program_name[];

#define BUG_ADDRESS	"<bug-hurd@gnu.org>"

int main (int argc, char *argv[]);


/* Extract the L4 access rights from FLAGS.  */
static inline l4_word_t
extract_access (l4_word_t flags)
{
  return flags & L4_FPAGE_FULLY_ACCESSIBLE;
}

/* The following function must be defined by the architecture
   dependent code.  */

/* Switch execution transparently to thread TO.  The thread FROM,
   which must be the current thread, will be halted.  */
void switch_thread (l4_thread_id_t from, l4_thread_id_t to);


/* Return true if INDEX lies within (START, START+SIZE-1)
   inclusive.  */
static bool
within (l4_word_t index, l4_word_t start, l4_word_t size)
{
  return index >= start && index < start + size;
}

/* Return true if (INDEX1, INDEX1+SIZE1-1) inclusive overlaps with
   (INDEX2, INDEX2+SIZE2-1) inclusive.  */
static bool
overlap (l4_word_t index1, l4_word_t size1, l4_word_t index2, l4_word_t size2)
{
  return
    /* Is the start of the first region within the second?
          2  1  2  1
       or 2  1  1  2  */
    within (index1, index2, size2)
    /* Is the end of the first region within the second?
          1  2  1  2
       or 2  1  1  2  */
    || within (index1 + size1 - 1, index2, size2)
    /* Is start of the second region within the first?
          1  2  1  2
       or 1  2  2  1 */
    || within (index2, index1, size1);

  /* We have implicitly checked if the end of the second region is
     within the first (i.e. within (index2 + size2 - 1, index1, size1))
          2  1  2  1
       or 1  2  2  1
     in check 1 and check 3.  */
}

/* A region of memory.  */
struct region
{
  /* Start of the region.  */
  uintptr_t start;
  /* And its extent.  */
  size_t size;
};

static inline int
region_compare (const struct region *a, const struct region *b)
{
  if (overlap (a->start, a->size, b->start, b->size))
    return 0;
  else
    return a->start - b->start;
}

/* Forward.  */
struct frame_entry;

/* A frame referrs directly to physical memory.  Exactly one frame
   structure refers to each piece of allocated (to users, i.e. not
   internal) physical memory.  */
struct frame
{
  /* Lock for all members as well as all frame entries using this
     frame.  */
  pthread_mutex_t lock;

  /* One reference per frame entry plus any active users.  */
  int refs;

  /* The physical memory allocated to this frame.  This is allocated
     lazily.  If the address portion is 0, memory has not yet been
     allocated.  */
  l4_fpage_t memory;

  /* The types of mappings which have been made since the last time
     this frame was unmapped.  This does not mean that it actually is
     mapped as users can unmap it themselves.  */
  l4_word_t may_be_mapped;

  /* Number of extant copy on writes.  */
  int cow;

  /* List of frame entries referring to this frame.  */
  struct frame_entry *frame_entries;
};

/* Regions in containers refer to physical memory.  Multiple regions
   may refer to the same phsyical memory (thereby allowing sharing and
   COW).  Every region has its own frame entry which contains the
   per-region state.  FRAME refers to the physical memory.  */
struct frame_entry
{
  /* The following fields are locked by the containing container's
     lock.  */

  /* The container of which this frame entry is a part.  */
  struct container *container;
  /* The name of this region within the containing container.  */
  struct region region;
  hurd_btree_node_t node;

  /* The following fields are lock by FRAME->lock.  */

  /* The physical memory backing this region.  */
  struct frame *frame;

  /* The frame entry may not reference all of the physical memory in
     FRAME (due to partial sharing, etc).  This is the offset to the
     start of the memory which this frame entry uses.  */
  size_t frame_offset;

  /* The list entry for FRAME's list of frame entries referring to
     itself.  */
  struct frame_entry *next;
  struct frame_entry **prevp;

  /* A circular list of frame entries which share a copy of the
     frame.  */
  struct frame_entry *shared_next;
  struct frame_entry **shared_prevp;
};

BTREE_CLASS(frame_entry, struct frame_entry, struct region, region,
	    node, region_compare)

struct container
{
  pthread_mutex_t lock;
  /* List of allocate frames in this container.  */
  hurd_btree_frame_entry_t frame_entries;
};

/* Initialize the frame subsystem.  */
extern void frame_entry_init (void);

/* Allocate an uninitialized frame entry structure.  Return NULL if
   there is insufficient memory.  */
extern struct frame_entry *frame_entry_alloc (void);

/* Deallocate frame entry FRAME_ENTRY.  NB: this function does not
   deinitialize any resources FRAME_ENTRY may still reference.  It is
   the dual of frame_entry_alloc.  */
extern void frame_entry_free (struct frame_entry *frame_entry);

/* Initialize the previously uninitialized frame entry structure
   FRAME_ENTRY to cover the region starting at byte START and
   extending SIZE bytes on container CONT.  SIZE must be a power of 2.
   CONT must be locked.  Physical memory is reserved, however, it is
   not allocated until a frame is attached and that frame is bound
   using frame_memory_bind.  FRAME_ENTRY->FRAME is locked.

   If the specified region overlaps with any in the container, EEXIST
   is returned.  */
extern error_t frame_entry_create (struct container *cont,
				   struct frame_entry *frame_entry,
				   uintptr_t start, size_t size);

/* Initialize the previously uninitialized frame entry structure
   FRAME_ENTRY to cover the region starting at byte START and
   extending SIZE bytes in container CONT.  FRAME_ENTRY refers to the
   physical memory in SOURCE starting at offset FRAME_OFFSET relative
   to the base of SOURCE->FRAME.  If SHARED_MEMORY is true, the
   physical memory is shared otherwise, a copy is marked COW.  SIZE
   must be a power of 2.  FRAME_OFFSET must be a multiple of SIZE.
   CONT must be locked.  FRAME must be locked.  A reference is added
   to FRAME.

   If the specified region overlaps with any in the container, EEXIST
   is returned.  */
extern error_t frame_entry_copy (struct container *cont,
				 struct frame_entry *frame_entry,
				 uintptr_t start, size_t size,
				 struct frame_entry *source,
				 size_t frame_offset,
				 bool shared_memory);

/* Initialize the previously uninitialized frame entry structure
   FRAME_ENTRY to cover the region starting at byte START and
   extending SIZE bytes in container CONT.  FRAME_ENTRY refers to the
   physical memory in SOURCE starting at offset FRAME_OFFSET relative
   to the base of SOURCE->FRAME.  If SHARED_MEMORY is true, the
   physical memory is shared otherwise, a copy is marked COW.  SIZE
   must be a power of 2.  FRAME_OFFSET must be a multiple of SIZE.
   CONT must be locked.  FRAME must be locked.  A reference is added
   to FRAME.

   If the specified region overlaps with any in the container, EEXIST
   is returned.  */
extern error_t frame_entry_use (struct container *cont,
				struct frame_entry *frame_entry,
				uintptr_t start, size_t size,
				struct frame *frame,
				size_t frame_offset);

/* Deinitialize frame entry FRAME_ENTRY.  If CONT is NULL, FRAME_ENTRY
   has already been detached from any container.  Otherwise,
   FRAME_ENTRY is deattached from the locked container CONT.  Drops a
   reference to the underlying frame.  FRAME_ENTRY->LOCK must be held
   and is unlocked if DO_UNLOCK_FRAME is true, otherwise it remains
   locked on return (in which case the caller must still have a
   reference to FRAME_ENTRY->FRAME).  This does *not* deallocate
   FRAME_ENTRY which must still be done by calling
   frame_entry_free.  */
extern void frame_entry_destroy (struct container *cont,
				 struct frame_entry *frame_entry,
				 bool do_unlock_frame);

/* Find a frame entry in container CONT which overlaps with the region
   START+SIZE and return it.  Returns NULL if no frame entry in CONT
   overlaps with the provided region.  CONT must be locked.  */
extern struct frame_entry *frame_entry_find (struct container *cont,
					     uintptr_t start,
					     size_t size);

/* Append map items to the message MSG with access ACCESS for the LEN
   bytes of corresponding to the memory underlying FRAME_ENTRY
   starting at byte START (relative to the base of FRAME_ENTRY) to be
   mapped at virtual memory address VADDR.  If AMOUNT is not-NULL, the
   number of bytes for which map items could be created is placed in
   *AMOUNT.  If not there is not space for map items to cover all LEN
   bytes, ENOSPC is returned.  */
extern error_t frame_entry_map (struct frame_entry *frame_entry,
				size_t start, size_t len, int access,
				uintptr_t vaddr, l4_msg_t msg,
				size_t *amount);

/* Deallocate part (or all) of FE which is in container CONT.
   CONT_START is the start of the region to deallocate in terms of the
   container which FE must cover.  LENGTH is the number of bytes to
   deallocate all of which FE must cover.  CONT must be lock.
   FE->FRAME must be lock.  FE->FRAME is implicitly unlocked on
   return.  */
extern error_t frame_entry_deallocate (struct container *cont,
				       struct frame_entry *fe,
				       uintptr_t cont_start,
				       size_t length);

/* Initialize the frame subsystem.  */
extern void frame_init (void);

/* Allocate a frame structure holding SIZE bytes.  Physical memory
   must have already been reserved (by, e.g. a prior frame_entry_alloc
   call).  Allocation of the physical memory is deferred until
   frame_memory_alloc is called.  The returned frame has a single
   reference and is locked.  */
extern struct frame *frame_alloc (size_t size);

/* Bind frame FRAME to physical memory if not already done.  */
static inline void
frame_memory_bind (struct frame *frame)
{
  /* Actually allocates the physical memory.  */
  extern void frame_memory_alloc (struct frame *frame);

  assert (pthread_mutex_trylock (&frame->lock) == EBUSY);
  if (! l4_address (frame->memory))
    frame_memory_alloc (frame);
}

/* Add a reference to frame FRAME.  */
static inline void
frame_ref (struct frame *frame)
{
  frame->refs ++;
}

/* Release a reference to frame FRAME.  FRAME must be locked.  When
   the last reference is removed, any exant client mappings will be
   unmapped, any physical memory will be deallocated and FRAME will be
   freed.  */
extern void frame_deref (struct frame *frame);

/* Release a reference to frame FRAME.  FRAME must be locked.  The
   caller must hold at least one reference in addition to the one it
   wants to release.  FRAME is not unlocked.  */
static inline void
frame_release (struct frame *frame)
{
  assert (pthread_mutex_trylock (&frame->lock) == EBUSY);
  assert (frame->refs > 1);
  frame->refs --;
}

/* Add FRAME_ENTRY as a user of FRAME.  FRAME_ENTRY must hold a
   reference to FRAME as long as it uses it.  (This function does not
   allocate a reference.)  FRAME must be locked.  */
extern void frame_add_user (struct frame *frame,
			    struct frame_entry *frame_entry);

/* Remove FRAME_ENTRY as a user of FRAME.  */
extern void frame_drop_user (struct frame *frame,
			     struct frame_entry *frame_entry);

/* Attach frame entry FRAME_ENTRY to container CONT.  FRAME_ENTRY must
   not currently be part of any container.  CONT must be locked.
   Returns EEXIST if FRAME_ENTRY overlaps with a frame entry in
   CONT.  */
extern error_t container_attach (struct container *cont,
				 struct frame_entry *frame_entry);

/* Detach frame entry FRAME_ENTRY from container CONT.  CONT must be
   locked.  After returning, FRAME_ENTRY->CONTAINER must no longer be
   used.  */
extern void container_detach (struct container *cont,
			      struct frame_entry *frame_entry);

/* Allocate a new container object covering the NR_FPAGES fpages
   listed in FPAGES.  The object returned is locked and has one
   reference.  */
extern error_t container_alloc (l4_word_t nr_fpages, l4_word_t *fpages,
				struct container **r_container);