bootstrap
[captive.git] / src / libcaptive / mm / page.c
1 /* $Id$
2  * reactos spinlock emulation of libcaptive
3  * Copyright (C) 2002 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captive/mm.h" /* self */
23 #include "reactos/internal/mm.h"        /* self */
24 #include "reactos/ntos/types.h" /* for PVOID etc. */
25 #include <glib/gmessages.h>
26 #include <limits.h> /* for PAGE_size */
27 #include <sys/mman.h>   /* for PROT_NONE etc. */
28 #include <glib/ghash.h>
29
30
31 /**
32  * captive_flProtect_to_mmap_prot:
33  * @flProtect: reactos compatible constant such as %PAGE_READWRITE.
34  *
35  * Map reactos flProtect to mprotect(2)-compatible "prot" argument.
36  *
37  * Returns: mmap(2) compatible @prot argument.
38  */
39 gint captive_flProtect_to_mmap_prot(ULONG flProtect)
40 {
41         flProtect&=~PAGE_NOCACHE;       /* too low level for libcaptive */
42         flProtect&=~PAGE_SYSTEM;        /* too low level for libcaptive */
43         /* FIXME: what does mean PAGE_WRITETHROUGH ? */
44         switch (flProtect) {
45                 case PAGE_GUARD:             return PROT_NONE;
46                 case PAGE_NOACCESS:          return PROT_NONE;
47                 case PAGE_READONLY:          return PROT_READ;
48                 case PAGE_READWRITE:         return PROT_READ|PROT_WRITE;
49                 case PAGE_WRITECOPY:         g_return_val_if_reached(PROT_NONE);        /* FIXME: no multithreading supported */
50                 case PAGE_EXECUTE_READ:      return PROT_EXEC|PROT_READ;
51                 case PAGE_EXECUTE_READWRITE: return PROT_EXEC|PROT_READ|PROT_WRITE;
52                 case PAGE_EXECUTE_WRITECOPY: g_return_val_if_reached(PROT_NONE);        /* FIXME: no multithreading supported */
53                 }
54         g_return_val_if_reached(PROT_NONE);     /* =unsupported flags */
55 }
56
57
58 static GHashTable *captive_mmap_map_hash;
59
60 static void captive_mmap_map_hash_init(void)
61 {
62         if (captive_mmap_map_hash)
63                 return;
64         captive_mmap_map_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
65 }
66
67 /**
68  * captive_mmap_map_new:
69  * @addr: %PAGE_SIZE aligned address of memory block.
70  * %NULL value is forbidden.
71  * @len: %PAGE_SIZE aligned  length of memory block.
72  * Value %0 is permitted. Value %-1 is forbidden.
73  * @mmap_prot: Protections for the memory block as specified by @prot of mprotect(2).
74  *
75  * Initialize the protection map for the specified memory block.
76  * Any existing protections in the specified block are forbidden.
77  *
78  * This function does not do any mprotect(2) style, it just stores
79  * the settings for the later %OR operations by MmSetPageProtect().
80  * Caller is responsibel to set the same protections as the given @mmap_prot.
81  *
82  * Returns: %TRUE if the protection storage was successful.
83  */
84 gboolean captive_mmap_map_new(gconstpointer addr,size_t len,int mmap_prot)
85 {
86         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p,len=%lu,mmap_prot=0x%X",G_STRLOC,addr,(gulong)len,mmap_prot);
87         g_return_val_if_fail(addr!=NULL,FALSE);
88         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,FALSE);
89         g_return_val_if_fail((len&(PAGE_SIZE-1))==0,FALSE);
90         g_return_val_if_fail(mmap_prot!=-1,FALSE);
91
92         captive_mmap_map_hash_init();
93
94         while (len) {
95                 g_return_val_if_fail(FALSE==g_hash_table_lookup_extended(captive_mmap_map_hash,
96                                                 addr,   /* lookup_key */
97                                                 NULL,   /* orig_key */
98                                                 NULL),  /* value */
99                                 FALSE);
100                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p,PAGE_SIZE=0x%X",G_STRLOC,addr,(guint)(PAGE_SIZE));
101                 g_hash_table_insert(captive_mmap_map_hash,
102                                 (gpointer)addr, /* key */
103                                 GINT_TO_POINTER(mmap_prot));    /* value */
104
105                 addr=(gconstpointer)(((const char *)addr)+PAGE_SIZE);
106                 len-=PAGE_SIZE;
107                 }
108
109         return TRUE;
110 }
111
112
113 /**
114  * captive_mmap_map_get:
115  * @addr: %PAGE_SIZE aligned address of memory block.
116  * %NULL value is forbidden.
117  *
118  * Query the protection settings at @addr address.
119  * The given @addr block of %PAGE_SIZE must be already initialized
120  * by captive_mmap_map_new().
121  *
122  * Returns: Protections of the page as specified by @prot of mprotect(2)
123  * if successful. Value %-1 if failed.
124  */
125 gint captive_mmap_map_get(gconstpointer addr)
126 {
127 gpointer r_gpointer;
128 gint r;
129
130         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p",G_STRLOC,addr);
131         g_return_val_if_fail(addr!=NULL,-1);
132         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,-1);
133
134         captive_mmap_map_hash_init();
135
136         g_return_val_if_fail(TRUE==g_hash_table_lookup_extended(captive_mmap_map_hash,
137                                         addr,   /* lookup_key */
138                                         NULL,   /* orig_key */
139                                         &r_gpointer),   /* value */
140                         -1);
141         r=GPOINTER_TO_INT(r_gpointer);
142
143         g_assert(r!=-1);
144         return r;
145 }
146
147
148 /**
149  * captive_mmap_map_set:
150  * @addr: %PAGE_SIZE aligned address of memory block.
151  * %NULL value is forbidden.
152  * @mmap_prot: Protections for the memory block as specified by @prot of mprotect(2).
153  *
154  * Set the protection settings at @addr address.
155  * The given @addr block of %PAGE_SIZE must be already initialized
156  * by captive_mmap_map_new().
157  *
158  * Returns: %TRUE if the protections were successfuly set.
159  */
160 gboolean captive_mmap_map_set(gconstpointer addr,int mmap_prot)
161 {
162         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p,mmap_prot=0x%X",G_STRLOC,addr,mmap_prot);
163         g_return_val_if_fail(addr!=NULL,FALSE);
164         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,FALSE);
165         g_return_val_if_fail(mmap_prot!=-1,FALSE);
166
167         captive_mmap_map_hash_init();
168
169         g_return_val_if_fail(TRUE==g_hash_table_lookup_extended(captive_mmap_map_hash,
170                                         addr,   /* lookup_key */
171                                         NULL,   /* orig_key */
172                                         NULL),  /* value */
173                         FALSE);
174
175         g_hash_table_replace(captive_mmap_map_hash,
176                         (gpointer)addr, /* key */
177                         GINT_TO_POINTER(mmap_prot));    /* value */
178
179         return TRUE;
180 }
181
182 /**
183  * MmSetPageProtect:
184  * @Process: Attached during the execution if not %NULL. Ignored by #libcaptive.
185  * @Address: One page to modify the protecton bits of.
186  * @flProtect: Required protection flags to logically %OR to the previos ones.
187  *
188  * FIXME: Undocumented by reactos. %ORes the protection of one page located at @Address
189  * with protection flags @flProtect. Implemented by mprotect(2) by #libcaptive.
190  */
191 VOID MmSetPageProtect(PEPROCESS Process,PVOID Address,ULONG flProtect)
192 {
193 gint mmap_prot;
194 int err;
195
196         g_return_if_fail(Address!=NULL);
197
198         captive_mmap_map_hash_init();
199
200         /* 'Address' is not required to be PAGE_SIZE-aligned:
201          * reactos MmSetPageProtect() calls MmGetPageEntry(Address) with no such requirement.
202          * glib NOTE: YOU MAY NOT STORE POINTERS IN INTEGERS.
203          */
204         Address=(VOID *)(((char *)Address)-(GPOINTER_TO_UINT(Address)&(PAGE_SIZE-1)));
205         /* This may be also invalid input 'Address' */
206         g_assert(Address!=NULL);
207
208         /* Prepare and updated ORed 'mmap_prot' in captive_mmap_map storage */
209         mmap_prot=captive_flProtect_to_mmap_prot(flProtect);
210         mmap_prot|=captive_mmap_map_get(Address);
211         captive_mmap_map_set(Address,mmap_prot);
212
213         /* protect one page; reactos call cannot return error codes */
214         err=mprotect(Address,PAGE_SIZE,mmap_prot);
215         g_return_if_fail(err==0);
216
217         /* success */
218 }