1 |
/* physmem.c - Generic definitions. |
/* physmem.h - Interfaces exported by physmem. |
2 |
Copyright (C) 2003, 2005 Free Software Foundation, Inc. |
Copyright (C) 2004, 2005 Free Software Foundation, Inc. |
3 |
Written by Neal H. Walfield <neal@gnu.org>. |
Written by Neal H. Walfield <neal@gnu.org>. |
4 |
|
|
5 |
This file is part of the GNU Hurd. |
This file is part of the GNU Hurd. |
19 |
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, |
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, |
20 |
USA. */ |
USA. */ |
21 |
|
|
22 |
#include <errno.h> |
#ifndef HURD_PHYSMEM_H |
23 |
#include <l4.h> |
#define HURD_PHYSMEM_H |
|
#include <hurd/btree.h> |
|
|
#include <hurd/cap-server.h> |
|
24 |
|
|
25 |
#include <compiler.h> |
/* Execute permission. */ |
26 |
|
#define HURD_PM_CONT_EXECUTE (1 << 0) |
27 |
|
/* Write permission. */ |
28 |
|
#define HURD_PM_CONT_WRITE (1 << 1) |
29 |
|
/* Read permission. */ |
30 |
|
#define HURD_PM_CONT_READ (1 << 2) |
31 |
|
/* Read and write permission. */ |
32 |
|
#define HURD_PM_CONT_RW (HURD_PM_CONT_READ|HURD_PM_CONT_WRITE) |
33 |
|
/* Read, write and execute. */ |
34 |
|
#define HURD_PM_CONT_RWX (HURD_PM_CONT_RW|HURD_PM_CONT_EXECUTE) |
35 |
|
|
36 |
|
/* Don't copy on write (COW), simply share (a la SYSV SHM). */ |
37 |
|
#define HURD_PM_CONT_COPY_SHARED (1 << 3) |
38 |
|
/* Don't copy the region, move it. */ |
39 |
|
#define HURD_PM_CONT_COPY_MOVE (1 << 4) |
40 |
|
|
41 |
|
/* Either completely fail or completely succeed: don't partially |
42 |
|
succeed. */ |
43 |
|
#define HURD_PM_CONT_ALL_OR_NONE (1 << 8) |
44 |
|
|
45 |
|
/* Do not fail if the specified identifier is already in use. |
46 |
|
Instead, deallocate the current frame and allocate a new one in its |
47 |
|
place. This is useful only to shortcut explicit deallocation |
48 |
|
requests; using it to avoid EEXIST error messages will lead to |
49 |
|
problems as it suggests that the client is not keep track of |
50 |
|
frames. */ |
51 |
|
#define HURD_PM_CONT_ALLOC_SQUASH (1 << 9) |
52 |
|
/* Allocate extra frames if needed. */ |
53 |
|
#define HURD_PM_CONT_ALLOC_EXTRA (1 << 10) |
54 |
|
/* Only allocate frames suitable for DMA. */ |
55 |
|
#define HURD_PM_CONT_ALLOC_DMA (1 << 11) |
56 |
|
|
57 |
|
/* RPC Identifiers. */ |
58 |
|
enum |
59 |
|
{ |
60 |
|
hurd_pm_container_create_id = 130, |
61 |
|
hurd_pm_container_share_id, |
62 |
|
hurd_pm_container_allocate_id, |
63 |
|
hurd_pm_container_deallocate_id, |
64 |
|
hurd_pm_container_map_id, |
65 |
|
hurd_pm_container_copy_id |
66 |
|
}; |
67 |
|
|
68 |
|
#include <hurd/types.h> |
69 |
|
|
70 |
#include "output.h" |
/* Memory control object. */ |
71 |
|
typedef hurd_cap_handle_t hurd_pm_control_t; |
72 |
|
|
73 |
|
/* Container. */ |
74 |
/* The program name. */ |
typedef hurd_cap_handle_t hurd_pm_container_t; |
|
extern char program_name[]; |
|
|
|
|
|
#define BUG_ADDRESS "<bug-hurd@gnu.org>" |
|
|
|
|
|
int main (int argc, char *argv[]); |
|
|
|
|
|
|
|
|
/* 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 |
|
|
{ |
|
|
/* 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; |
|
|
|
|
|
/* If a mapping has 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. */ |
|
|
bool 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 name of this region within the containing container. */ |
|
|
struct region region; |
|
|
/* 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; |
|
|
|
|
|
hurd_btree_node_t node; |
|
|
|
|
|
/* The list entry for FRAME's list of frame entries referring to |
|
|
itself. */ |
|
|
struct frame_entry *next; |
|
|
struct frame_entry **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; |
|
|
}; |
|
|
|
|
|
/* 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_dealloc (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. |
|
|
|
|
|
If the specified region overlaps with any in the container, EEXIST |
|
|
is returned. */ |
|
|
extern error_t frame_entry_new (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 FRAME starting at offset OFFSET (hence when |
|
|
OFFSET is non-zero, FRAME_ENTRY only refers to a portion of FRAME). |
|
|
SIZE must be a power of 2. CONT 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_frame (struct container *cont, |
|
|
struct frame_entry *frame_entry, |
|
|
uintptr_t start, size_t size, |
|
|
struct frame *frame, |
|
|
size_t offset); |
|
|
|
|
|
/* Deinitialize frame entry FRAME_ENTRY which is in the locked |
|
|
container CONT dereferencing any frame it may be using in the |
|
|
process. This does *not* deallocate the FRAME_ENTRY structure. |
|
|
This must still be done using frame_entry_dealloc. */ |
|
|
extern void frame_entry_drop (struct container *cont, |
|
|
struct frame_entry *frame_entry); |
|
|
|
|
|
/* Find a frame entry which overlaps with the region START+SIZE and |
|
|
return it. Returns NULL if no frame entry in container 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); |
|
|
|
|
|
/* Attach frame entry FRAME_ENTRY into container CONT. FRAME_ENTRY |
|
|
must not currently be part of any container. Returns EEXIST if |
|
|
FRAME_ENTRY overlaps with a frame entry in CONT. */ |
|
|
extern error_t frame_entry_attach (struct container *cont, |
|
|
struct frame_entry *frame_entry); |
|
|
|
|
|
/* Detach frame entry FRAME_ENTRY from container CONT. */ |
|
|
extern void frame_entry_detach (struct container *cont, |
|
|
struct frame_entry *frame_entry); |
|
|
|
|
|
/* 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); |
|
|
|
|
|
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); |
|
|
|
|
|
/* Add FRAME_ENTRY as a user of FRAME. A reference for FRAME_ENTRY |
|
|
must already be held. */ |
|
|
extern void frame_add_user (struct frame *frame, |
|
|
struct frame_entry *frame_entry); |
|
|
|
|
|
/* Remove FRAME_ENTRY as a user of FRAME. A reference to frame is |
|
|
released in the process. */ |
|
|
extern void frame_drop_user (struct frame *frame, |
|
|
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); |
|
75 |
|
|
76 |
|
#endif /* HURD_PHYSMEM_H */ |