2 * COPYRIGHT: See COPYING in the top directory
3 * PROJECT: ReactOS kernel v0.0.2
5 * PURPOSE: implementing the Virtualxxx section of the win32 api
6 * PROGRAMMER: David Welch
9 * 10/6/98: Corrections from Fatahi (i_fatahi@hotmail.com)
12 /* INCLUDE *****************************************************************/
16 #include <internal/hal/segment.h>
17 #include <internal/mm.h>
18 #include <internal/hal/page.h>
21 #include <internal/debug.h>
23 /* TYPES *******************************************************************/
26 * These two are statically declared because mm is initalized before the
29 static memory_area kernel_area_desc
;
30 static memory_area kernel_pool_desc
;
33 * Head of the list of system memory areas
35 memory_area
* system_memory_area_list_head
=&kernel_area_desc
;
38 * Head of the list of user memory areas (this should be per process)
40 memory_area
* memory_area_list_head
=NULL
;
43 * The base address of the kmalloc region
45 unsigned int kernel_pool_base
= 0;
48 /* FUNCTIONS ****************************************************************/
50 void VirtualInit(boot_param
* bp
)
52 * FUNCTION: Intialize the memory areas list
54 * bp = Pointer to the boot parameters
55 * kernel_len = Length of the kernel
58 unsigned int kernel_len
= bp
->end_mem
- bp
->start_mem
;
60 DPRINT("VirtualInit() %x\n",bp
);
63 * Setup the system area descriptor list
65 kernel_area_desc
.base
= KERNEL_BASE
;
66 kernel_area_desc
.length
= kernel_len
;
67 kernel_area_desc
.next
= &kernel_pool_desc
;
68 kernel_area_desc
.load_page
=NULL
;
71 * The kmalloc area starts one page after the kernel area
73 kernel_pool_desc
.base
= KERNEL_BASE
+ PAGE_ROUND_UP(kernel_len
) + PAGESIZE
;
74 kernel_pool_desc
.length
= NONPAGED_POOL_SIZE
;
75 kernel_pool_desc
.next
= NULL
;
76 kernel_pool_desc
.load_page
=NULL
;
78 kernel_pool_base
= kernel_pool_desc
.base
;
79 DPRINT("kmalloc_region_base %x\n",kernel_pool_base
);
83 memory_area
* find_first_marea(memory_area
* list_head
, unsigned int base
,
86 * FUNCTION: Returns the first memory area starting in the region or the last
87 * one before the start of the region
89 * list_head = Head of the list of memory areas to search
90 * base = base address of the region
91 * length = length of the region
92 * RETURNS: A pointer to the area found or
93 * NULL if the region was before the first region on the list
96 memory_area
* current
=list_head
;
103 if (current
->base
== base
&& length
==0)
108 if (current
->base
>= base
)
110 if (current
->base
< (base
+length
))
116 return(current
->previous
);
119 if (current
->next
==NULL
)
123 current
=current
->next
;
128 asmlinkage
int page_fault_handler(unsigned int edi
,
129 unsigned int esi
, unsigned int ebp
,
130 unsigned int esp
, unsigned int ebx
,
131 unsigned int edx
, unsigned int ecx
,
135 unsigned short int error_code
,
137 unsigned int cs
, unsigned int eflags
,
138 unsigned int esp0
, unsigned int ss0
)
140 * FUNCTION: Handle a page fault
143 memory_area
* marea
=NULL
;
146 * Get the address for the page fault
149 __asm__("movl %%cr2,%0\n\t" : "=d" (cr2
));
150 DPRINT("Page fault at address %x with eip %x\n",cr2
,eip
);
152 cr2
= PAGE_ROUND_DOWN(cr2
);
155 * Find the memory area for the faulting address
157 if (cr2
>=KERNEL_BASE
)
164 printk("%s:%d\n",__FILE__
,__LINE__
);
167 marea
=find_first_marea(system_memory_area_list_head
,cr2
,
172 marea
=find_first_marea(memory_area_list_head
,cr2
,
177 * If the access was to an invalid area of memory raise an exception
179 if (marea
==NULL
|| marea
->load_page
==NULL
)
181 printk("%s:%d\n",__FILE__
,__LINE__
);
186 * Check the access was within the area
188 if (cr2
> (marea
->base
+ marea
->length
) || cr2
< marea
->base
)
190 DPRINT("base was %x length %x\n",marea
->base
,marea
->length
);
191 DPRINT("%s:%d\n",__FILE__
,__LINE__
);
196 * Call the region specific page fault handler
198 return(marea
->load_page(marea
,cr2
- marea
->base
));
203 static LPVOID
allocate_marea(DWORD dwSize
, DWORD flAllocationType
,
204 DWORD flProtect
, memory_area
** list_head
,
205 unsigned int last_addr
,
206 BOOL (*fn
)(memory_area
* marea
,
207 unsigned int address
))
212 memory_area
* current
=*list_head
;
213 memory_area
* ndesc
=NULL
;
215 while (current
!=NULL
)
217 last_addr
= PAGE_ROUND_UP(current
->base
+ current
->length
);
218 current
=current
->next
;
220 ndesc
= ExAllocatePool(NonPagedPool
,sizeof(memory_area
));
221 ndesc
->access
=flProtect
;
222 ndesc
->state
=flAllocationType
;
224 ndesc
->base
=last_addr
+PAGESIZE
;
225 ndesc
->length
=dwSize
;
226 ndesc
->previous
=current
;
229 ndesc
->next
=current
->next
;
231 if (current
->next
!=NULL
)
233 current
->next
->previous
=ndesc
;
240 ndesc
->previous
=NULL
;
243 DPRINT("VirtualAlloc() returning %x\n",ndesc
->base
);
244 return((LPVOID
)ndesc
->base
);
247 void commit_region_free(memory_area
* marea
)
249 * FUNCTION: Decommits the region
253 for (i
=0;i
<marea
->length
;i
=i
+PAGESIZE
)
255 if (is_page_present(marea
->base
+marea
->length
))
257 free_page(MmGetPhysicalAddress(marea
->base
+marea
->length
),1);
262 BOOL
commit_region_load_page(memory_area
* marea
, unsigned int address
)
264 * FUNCTION: Handles a page fault on a committed region of memory
266 * marea = memory area
267 * address = address of faulting access
268 * RETURNS: TRUE if the page was loaded
269 * FALSE if an exception should be generated
272 set_page(marea
->base
+address
,0x7,get_free_page());
276 asmlinkage LPVOID STDCALL
VirtualAlloc(LPVOID lpAddress
, DWORD dwSize
,
277 DWORD flAllocationType
,
281 * FUNCTION: Create a memory area
283 * lpAddress = the base address of the area or NULL if the system
285 * dwSize = the size of the area
286 * flAllocationType = the type of allocation
287 * MEM_COMMIT = accessible
288 * MEM_RESERVE = not accessible but can't be allocated
289 * flProtect = what protection to give the area
290 * RETURNS: The base address of the block
292 DPRINT("VirtualAlloc() lpAddress %x dwSize %x flAllocationType %x ",
293 lpAddress
,dwSize
,flAllocationType
);
294 DPRINT("flProtect %x\n",flProtect
);
299 * We decide the address
301 if (flProtect
& PAGE_SYSTEM
)
303 return(allocate_marea(dwSize
,flAllocationType
,
304 flProtect
,&system_memory_area_list_head
,
305 KERNEL_BASE
,commit_region_load_page
));
309 return(allocate_marea(dwSize
,flAllocationType
,
310 flProtect
,&memory_area_list_head
,PAGESIZE
,
311 commit_region_load_page
));
316 memory_area
* marea
=NULL
;
317 if (lpAddress
< ((PVOID
)KERNEL_BASE
))
319 marea
=find_first_marea(memory_area_list_head
,
320 (unsigned int)lpAddress
,dwSize
);
324 marea
=find_first_marea(system_memory_area_list_head
,
325 (unsigned int)lpAddress
,dwSize
);
329 * Check someone hasn't already allocated that area
331 if (marea
!=NULL
&& marea
->base
<= (unsigned int)lpAddress
&&
332 (marea
->base
+ marea
->length
) > (unsigned int)lpAddress
)
334 SetLastError(ERROR_INVALID_DATA
);
344 BOOL WINAPI
VirtualFreeEx(HANDLE hProcess
, LPVOID lpAddress
, DWORD dwSize
,
347 * FUNCTION: Releases, decommits or both a region of memory within the
348 * virtual address of a specified process
350 * hProcess = Process to act on
351 * lpAddress = starting virtual address to free
352 * dwSize = Size in bytes of the memory region to free
353 * dwFreeType = Type of free operation
354 * RETURNS: On success non-zero
356 * NOTE: This tries to optimize for the most common case which is for
357 * regions to be decommitted in large chunks. If a process alternatedly
358 * decommitted every other page in a region this function would be extremely
362 memory_area
* marea
= NULL
;
365 * Check our permissions for hProcess here
369 * Get the list of memory areas corresponding to hProcess here
375 if (dwFreeType
==MEM_RELEASE
)
379 SetLastError(ERROR_INVALID_BLOCK_LENGTH
);
383 marea
= find_first_marea(memory_area_list_head
,(unsigned int)lpAddress
,
386 if (marea
==NULL
|| marea
->base
!=((int)lpAddress
))
388 SetLastError(ERROR_INVALID_BLOCK
);
393 * Ask the memory area to destroy itself
398 * Change the area attributes
400 marea
->access
=PAGE_NOACCESS
;
401 marea
->state
=MEM_FREE
;
402 marea
->type
=MEM_PRIVATE
;
409 BOOL WINAPI
VirtualProtectEx(HANDLE hProcess
, LPVOID lpAddress
,
410 DWORD dwSize
, DWORD flNewProtect
, PDWORD lpflOldProtect
)
417 DWORD WINAPI
VirtualQueryEx(HANDLE hProcess
, LPCVOID lpAddress
,
418 PMEMORY_BASIC_INFORMATION lpBuffer
, DWORD dwLength
)
420 * FUNCTION: Provides information about a range of pages within the virtual
421 * address space of a specified process.
423 * hProcess = Handle of process
424 * lpAddress = Address of region
425 * lpBuffer = Buffer to store information
426 * dwLength = length of region
427 * RETURNS: The number of bytes transferred into the buffer
431 * Find the memory area
433 memory_area
* marea
= find_first_marea(memory_area_list_head
,
434 (unsigned int) lpAddress
,
440 if (marea
==NULL
|| marea
->base
!=((int)lpAddress
))
446 lpBuffer
->BaseAddress
= (void *)lpAddress
;
447 lpBuffer
->AllocationBase
= (void *)marea
->base
;
448 lpBuffer
->AllocationProtect
= marea
->initial_access
;
449 lpBuffer
->RegionSize
= marea
->length
;
450 lpBuffer
->State
= marea
->state
;
451 lpBuffer
->Protect
= marea
->access
;
452 lpBuffer
->Type
= marea
->type
;
454 return(sizeof(MEMORY_BASIC_INFORMATION
));
457 BOOLEAN
MmIsNonPagedSystemAddressValid(PVOID VirtualAddress
)
462 BOOLEAN
MmIsAddressValid(PVOID VirtualAddress
)
464 * FUNCTION: Checks whether the given address is valid for a read or write
466 * VirtualAddress = address to check
467 * RETURNS: True if the access would be valid
468 * False if the access would cause a page fault
469 * NOTES: This function checks whether a byte access to the page would
470 * succeed. Is this realistic for RISC processors which don't
471 * allow byte granular access?
476 if (VirtualAddress
>= (PVOID
)KERNEL_BASE
)
478 marea
= find_first_marea(system_memory_area_list_head
,
479 (unsigned int)VirtualAddress
,1);
483 marea
= find_first_marea(memory_area_list_head
,
484 (unsigned int)VirtualAddress
,1);
492 if (marea
->access
== PAGE_NOACCESS
|| marea
->access
& PAGE_GUARD
||
493 marea
->state
== MEM_FREE
|| marea
->state
== MEM_RESERVE
)