Initial original import from: fuse-2.4.2-2.fc4
[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 <sys/mman.h>   /* for PROT_NONE etc. */
27 #include <glib/ghash.h>
28
29
30 /**
31  * captive_flProtect_to_mmap_prot:
32  * @flProtect: reactos compatible constant such as %PAGE_READWRITE.
33  *
34  * Map reactos flProtect to mprotect(2)-compatible "prot" argument.
35  *
36  * Returns: mmap(2) compatible @prot argument.
37  */
38 gint captive_flProtect_to_mmap_prot(ULONG flProtect)
39 {
40         flProtect&=~PAGE_NOCACHE;       /* too low level for libcaptive */
41         flProtect&=~PAGE_SYSTEM;        /* too low level for libcaptive */
42         /* FIXME: what does mean PAGE_WRITETHROUGH ? */
43         switch (flProtect) {
44                 case PAGE_GUARD:             return PROT_NONE;
45                 case PAGE_NOACCESS:          return PROT_NONE;
46                 case PAGE_READONLY:          return PROT_READ;
47                 case PAGE_READWRITE:         return PROT_READ|PROT_WRITE;
48                 case PAGE_WRITECOPY:         g_return_val_if_reached(PROT_NONE);        /* FIXME: no multithreading supported */
49                 case PAGE_EXECUTE_READ:      return PROT_EXEC|PROT_READ;
50                 case PAGE_EXECUTE_READWRITE: return PROT_EXEC|PROT_READ|PROT_WRITE;
51                 case PAGE_EXECUTE_WRITECOPY: g_return_val_if_reached(PROT_NONE);        /* FIXME: no multithreading supported */
52                 }
53         g_return_val_if_reached(PROT_NONE);     /* =unsupported flags */
54 }
55
56
57 static GHashTable *captive_mmap_map_hash;
58
59 static void captive_mmap_map_hash_init(void)
60 {
61         if (captive_mmap_map_hash)
62                 return;
63         captive_mmap_map_hash=g_hash_table_new(g_direct_hash,g_direct_equal);
64 }
65
66 /**
67  * captive_mmap_map_new:
68  * @addr: %PAGE_SIZE aligned address of memory block.
69  * %NULL value is forbidden.
70  * @len: %PAGE_SIZE aligned  length of memory block.
71  * Value %0 is permitted. Value %-1 is forbidden.
72  * @mmap_prot: Protections for the memory block as specified by @prot of mprotect(2).
73  *
74  * Initialize the protection map for the specified memory block.
75  * Any existing protections in the specified block are forbidden.
76  *
77  * This function does not do any mprotect(2) style, it just stores
78  * the settings for the later %OR operations by MmSetPageProtect().
79  * Caller is responsibel to set the same protections as the given @mmap_prot.
80  *
81  * Returns: %TRUE if the protection storage was successful.
82  */
83 gboolean captive_mmap_map_new(gconstpointer addr,size_t len,int mmap_prot)
84 {
85         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);
86         g_return_val_if_fail(addr!=NULL,FALSE);
87         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,FALSE);
88         g_return_val_if_fail((len&(PAGE_SIZE-1))==0,FALSE);
89         g_return_val_if_fail(mmap_prot!=-1,FALSE);
90
91         captive_mmap_map_hash_init();
92
93         while (len) {
94                 g_return_val_if_fail(FALSE==g_hash_table_lookup_extended(captive_mmap_map_hash,
95                                                 addr,   /* lookup_key */
96                                                 NULL,   /* orig_key */
97                                                 NULL),  /* value */
98                                 FALSE);
99                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p,PAGE_SIZE=0x%X",G_STRLOC,addr,(guint)(PAGE_SIZE));
100                 g_hash_table_insert(captive_mmap_map_hash,
101                                 (gpointer)addr, /* key */
102                                 GINT_TO_POINTER(mmap_prot));    /* value */
103
104                 addr=(gconstpointer)(((const char *)addr)+PAGE_SIZE);
105                 len-=PAGE_SIZE;
106                 }
107
108         return TRUE;
109 }
110
111
112 /**
113  * captive_mmap_map_get:
114  * @addr: %PAGE_SIZE aligned address of memory block.
115  * %NULL value is forbidden.
116  *
117  * Query the protection settings at @addr address.
118  * The given @addr block of %PAGE_SIZE must be already initialized
119  * by captive_mmap_map_new().
120  *
121  * Returns: Protections of the page as specified by @prot of mprotect(2)
122  * if successful. Value %-1 if failed.
123  */
124 gint captive_mmap_map_get(gconstpointer addr)
125 {
126 gpointer r_gpointer;
127 gint r;
128
129         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p",G_STRLOC,addr);
130         g_return_val_if_fail(addr!=NULL,-1);
131         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,-1);
132
133         captive_mmap_map_hash_init();
134
135         g_return_val_if_fail(TRUE==g_hash_table_lookup_extended(captive_mmap_map_hash,
136                                         addr,   /* lookup_key */
137                                         NULL,   /* orig_key */
138                                         &r_gpointer),   /* value */
139                         -1);
140         r=GPOINTER_TO_INT(r_gpointer);
141
142         g_assert(r!=-1);
143         return r;
144 }
145
146
147 /**
148  * captive_mmap_map_set:
149  * @addr: %PAGE_SIZE aligned address of memory block.
150  * %NULL value is forbidden.
151  * @mmap_prot: Protections for the memory block as specified by @prot of mprotect(2).
152  *
153  * Set the protection settings at @addr address.
154  * The given @addr block of %PAGE_SIZE must be already initialized
155  * by captive_mmap_map_new().
156  *
157  * Returns: %TRUE if the protections were successfuly set.
158  */
159 gboolean captive_mmap_map_set(gconstpointer addr,int mmap_prot)
160 {
161         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: addr=%p,mmap_prot=0x%X",G_STRLOC,addr,mmap_prot);
162         g_return_val_if_fail(addr!=NULL,FALSE);
163         g_return_val_if_fail((GPOINTER_TO_UINT(addr)&(PAGE_SIZE-1))==0,FALSE);
164         g_return_val_if_fail(mmap_prot!=-1,FALSE);
165
166         captive_mmap_map_hash_init();
167
168         g_return_val_if_fail(TRUE==g_hash_table_lookup_extended(captive_mmap_map_hash,
169                                         addr,   /* lookup_key */
170                                         NULL,   /* orig_key */
171                                         NULL),  /* value */
172                         FALSE);
173
174         g_hash_table_replace(captive_mmap_map_hash,
175                         (gpointer)addr, /* key */
176                         GINT_TO_POINTER(mmap_prot));    /* value */
177
178         return TRUE;
179 }
180
181 /**
182  * MmSetPageProtect:
183  * @Process: Attached during the execution if not %NULL. Ignored by #libcaptive.
184  * @Address: One page to modify the protecton bits of.
185  * @flProtect: Required protection flags to logically %OR to the previos ones.
186  *
187  * FIXME: Undocumented by reactos. %ORes the protection of one page located at @Address
188  * with protection flags @flProtect. Implemented by mprotect(2) by #libcaptive.
189  */
190 VOID MmSetPageProtect(PEPROCESS Process,PVOID Address,ULONG flProtect)
191 {
192 gint mmap_prot;
193 int err;
194
195         g_return_if_fail(Address!=NULL);
196
197         captive_mmap_map_hash_init();
198
199         /* 'Address' is not required to be PAGE_SIZE-aligned:
200          * reactos MmSetPageProtect() calls MmGetPageEntry(Address) with no such requirement.
201          * glib NOTE: YOU MAY NOT STORE POINTERS IN INTEGERS.
202          */
203         Address=(VOID *)(((char *)Address)-(GPOINTER_TO_UINT(Address)&(PAGE_SIZE-1)));
204         /* This may be also invalid input 'Address' */
205         g_assert(Address!=NULL);
206
207         /* Prepare and updated ORed 'mmap_prot' in captive_mmap_map storage */
208         mmap_prot=captive_flProtect_to_mmap_prot(flProtect);
209         mmap_prot|=captive_mmap_map_get(Address);
210         captive_mmap_map_set(Address,mmap_prot);
211
212         /* protect one page; reactos call cannot return error codes */
213         err=mprotect(Address,PAGE_SIZE,mmap_prot);
214         g_return_if_fail(err==0);
215
216         /* success */
217 }
218
219
220 /**
221  * MmGetPhysicalAddress:
222  * @vaddr: Virtual address to convert.
223  *
224  * Function converts address from the virtual address space to the physical one.
225  * As libcaptive unifies all the address spaces this function will return @vaddr directly there.
226  *
227  * Returns: Pure @vaddr extended to 64-bits of #PHYSICAL_ADDRESS (libcaptive-specific).
228  */
229 PHYSICAL_ADDRESS MmGetPhysicalAddress(PVOID vaddr)
230 {
231 PHYSICAL_ADDRESS r;
232
233         r.QuadPart=0;
234         g_return_val_if_fail(vaddr!=NULL,r);
235
236         g_assert(sizeof(vaddr)==sizeof(guint32));
237         r.QuadPart=(guint32)vaddr;
238
239         return r;
240 }