VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c@ 3305

Last change on this file since 3305 was 3305, checked in by vboxsync, 17 years ago

change_page_attr was introduced somewhere in the 2.4.21..28 range. (Don't know exactly when since lxr.linux.no only has 2.4.20 and 2.4.28, and I don't have any other sources handy.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.3 KB
Line 
1/* $Id: alloc-r0drv-linux.c 3305 2007-06-26 19:39:14Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Memory Allocation, Ring-0 Driver, Linux.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include "the-linux-kernel.h"
27#include <iprt/mem.h>
28#include <iprt/assert.h>
29#include "r0drv/alloc-r0drv.h"
30
31#if defined(__AMD64__) || defined(__DOXYGEN__)
32/**
33 * We need memory in the module range (~2GB to ~0) this can only be obtained
34 * thru APIs that are not exported (see module_alloc()).
35 *
36 * So, we'll have to create a quick and dirty heap here using BSS memory.
37 * Very annoying and it's going to restrict us!
38 */
39# define RTMEMALLOC_EXEC_HEAP
40#endif
41#ifdef RTMEMALLOC_EXEC_HEAP
42# include <iprt/heap.h>
43# include <iprt/spinlock.h>
44# include <iprt/err.h>
45#endif
46
47
48/*******************************************************************************
49* Global Variables *
50*******************************************************************************/
51#ifdef RTMEMALLOC_EXEC_HEAP
52/** The heap. */
53static RTHEAPSIMPLE g_HeapExec = NIL_RTHEAPSIMPLE;
54/** Spinlock protecting the heap. */
55static RTSPINLOCK g_HeapExecSpinlock = NIL_RTSPINLOCK;
56
57
58/**
59 * API for cleaning up the heap spinlock on IPRT termination.
60 * This is as RTMemExecDonate specific to AMD64 Linux/GNU.
61 */
62void rtR0MemExecCleanup(void)
63{
64 RTSpinlockDestroy(g_HeapExecSpinlock);
65 g_HeapExecSpinlock = NIL_RTSPINLOCK;
66}
67
68
69/**
70 * Donate read+write+execute memory to the exec heap.
71 *
72 * This API is specific to AMD64 and Linux/GNU. A kernel module that desires to
73 * use RTMemExecAlloc on AMD64 Linux/GNU will have to donate some statically
74 * allocated memory in the module if it wishes for GCC generated code to work.
75 * GCC can only generate modules that work in the address range ~2GB to ~0
76 * currently.
77 *
78 * The API only accept one single donation.
79 *
80 * @returns IPRT status code.
81 * @param pvMemory Pointer to the memory block.
82 * @param cb The size of the memory block.
83 */
84RTR0DECL(int) RTR0MemExecDonate(void *pvMemory, size_t cb)
85{
86 int rc;
87 AssertReturn(g_HeapExec == NIL_RTHEAPSIMPLE, VERR_WRONG_ORDER);
88
89 rc = RTSpinlockCreate(&g_HeapExecSpinlock);
90 if (RT_SUCCESS(rc))
91 {
92 rc = RTHeapSimpleInit(&g_HeapExec, pvMemory, cb);
93 if (RT_FAILURE(rc))
94 rtR0MemExecCleanup();
95 }
96 return rc;
97}
98#endif /* RTMEMALLOC_EXEC_HEAP */
99
100
101
102/**
103 * OS specific allocation function.
104 */
105PRTMEMHDR rtMemAlloc(size_t cb, uint32_t fFlags)
106{
107 /*
108 * Allocate.
109 */
110 PRTMEMHDR pHdr;
111 Assert(cb != sizeof(void *)); /* 99% of pointer sized allocations are wrong. */
112 if (fFlags & RTMEMHDR_FLAG_EXEC)
113 {
114#if defined(__AMD64__)
115# ifdef RTMEMALLOC_EXEC_HEAP
116 if (g_HeapExec != NIL_RTHEAPSIMPLE)
117 {
118 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
119 RTSpinlockAcquireNoInts(g_HeapExecSpinlock, &SpinlockTmp);
120 pHdr = (PRTMEMHDR)RTHeapSimpleAlloc(g_HeapExec, cb + sizeof(*pHdr), 0);
121 RTSpinlockReleaseNoInts(g_HeapExecSpinlock, &SpinlockTmp);
122 fFlags |= RTMEMHDR_FLAG_EXEC_HEAP;
123 }
124 else
125# endif
126 pHdr = (PRTMEMHDR)__vmalloc(cb + sizeof(*pHdr), GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC);
127
128#elif defined(PAGE_KERNEL_EXEC) && defined(CONFIG_X86_PAE)
129 pHdr = (PRTMEMHDR)__vmalloc(cb + sizeof(*pHdr), GFP_KERNEL | __GFP_HIGHMEM,
130 __pgprot(cpu_has_pge ? _PAGE_KERNEL_EXEC | _PAGE_GLOBAL : _PAGE_KERNEL_EXEC));
131#else
132 pHdr = (PRTMEMHDR)vmalloc(cb + sizeof(*pHdr));
133#endif
134 }
135 else
136 {
137 if (cb <= PAGE_SIZE)
138 {
139 fFlags |= RTMEMHDR_FLAG_KMALLOC;
140 pHdr = kmalloc(cb + sizeof(*pHdr), GFP_KERNEL);
141 }
142 else
143 pHdr = vmalloc(cb + sizeof(*pHdr));
144 }
145
146 /*
147 * Initialize.
148 */
149 if (pHdr)
150 {
151 pHdr->u32Magic = RTMEMHDR_MAGIC;
152 pHdr->fFlags = fFlags;
153 pHdr->cb = cb;
154 pHdr->u32Padding= 0;
155 }
156 return pHdr;
157}
158
159
160/**
161 * OS specific free function.
162 */
163void rtMemFree(PRTMEMHDR pHdr)
164{
165 pHdr->u32Magic += 1;
166 if (pHdr->fFlags & RTMEMHDR_FLAG_KMALLOC)
167 kfree(pHdr);
168#ifdef RTMEMALLOC_EXEC_HEAP
169 else if (pHdr->fFlags & RTMEMHDR_FLAG_EXEC_HEAP)
170 {
171 RTSPINLOCKTMP SpinlockTmp = RTSPINLOCKTMP_INITIALIZER;
172 RTSpinlockAcquireNoInts(g_HeapExecSpinlock, &SpinlockTmp);
173 RTHeapSimpleFree(g_HeapExec, pHdr);
174 RTSpinlockReleaseNoInts(g_HeapExecSpinlock, &SpinlockTmp);
175 }
176#endif
177 else
178 vfree(pHdr);
179}
180
181
182/**
183 * Compute order. Some functions allocate 2^order pages.
184 *
185 * @returns order.
186 * @param cPages Number of pages.
187 */
188static int CalcPowerOf2Order(unsigned long cPages)
189{
190 int iOrder;
191 unsigned long cTmp;
192
193 for (iOrder = 0, cTmp = cPages; cTmp >>= 1; ++iOrder)
194 ;
195 if (cPages & ~(1 << iOrder))
196 ++iOrder;
197
198 return iOrder;
199}
200
201
202/**
203 * Allocates physical contiguous memory (below 4GB).
204 * The allocation is page aligned and the content is undefined.
205 *
206 * @returns Pointer to the memory block. This is page aligned.
207 * @param pPhys Where to store the physical address.
208 * @param cb The allocation size in bytes. This is always
209 * rounded up to PAGE_SIZE.
210 */
211RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
212{
213 int cOrder;
214 unsigned cPages;
215 struct page *paPages;
216
217 /*
218 * validate input.
219 */
220 Assert(VALID_PTR(pPhys));
221 Assert(cb > 0);
222
223 /*
224 * Allocate page pointer array.
225 */
226 cb = RT_ALIGN_Z(cb, PAGE_SIZE);
227 cPages = cb >> PAGE_SHIFT;
228 cOrder = CalcPowerOf2Order(cPages);
229#ifdef __AMD64__ /** @todo check out if there is a correct way of getting memory below 4GB (physically). */
230 paPages = alloc_pages(GFP_DMA, cOrder);
231#else
232 paPages = alloc_pages(GFP_USER, cOrder);
233#endif
234 if (paPages)
235 {
236 /*
237 * Reserve the pages and mark them executable.
238 */
239 unsigned iPage;
240 for (iPage = 0; iPage < cPages; iPage++)
241 {
242 Assert(!PageHighMem(&paPages[iPage]));
243 if (iPage + 1 < cPages)
244 {
245 AssertMsg( (uintptr_t)phys_to_virt(page_to_phys(&paPages[iPage])) + PAGE_SIZE
246 == (uintptr_t)phys_to_virt(page_to_phys(&paPages[iPage + 1]))
247 && page_to_phys(&paPages[iPage]) + PAGE_SIZE
248 == page_to_phys(&paPages[iPage + 1]),
249 ("iPage=%i cPages=%u [0]=%#llx,%p [1]=%#llx,%p\n", iPage, cPages,
250 (long long)page_to_phys(&paPages[iPage]), phys_to_virt(page_to_phys(&paPages[iPage])),
251 (long long)page_to_phys(&paPages[iPage + 1]), phys_to_virt(page_to_phys(&paPages[iPage + 1])) ));
252 }
253
254 SetPageReserved(&paPages[iPage]);
255#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 20) /** @todo find the exact kernel where change_page_attr was introduced. */
256 if (pgprot_val(MY_PAGE_KERNEL_EXEC) != pgprot_val(PAGE_KERNEL))
257 MY_CHANGE_PAGE_ATTR(&paPages[iPage], 1, MY_PAGE_KERNEL_EXEC);
258#endif
259 }
260 *pPhys = page_to_phys(paPages);
261 return phys_to_virt(page_to_phys(paPages));
262 }
263
264 return NULL;
265}
266
267
268/**
269 * Frees memory allocated ysing RTMemContAlloc().
270 *
271 * @param pv Pointer to return from RTMemContAlloc().
272 * @param cb The cb parameter passed to RTMemContAlloc().
273 */
274RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
275{
276 if (pv)
277 {
278 int cOrder;
279 unsigned cPages;
280 unsigned iPage;
281 struct page *paPages;
282
283 /* validate */
284 AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
285 Assert(cb > 0);
286
287 /* calc order and get pages */
288 cb = RT_ALIGN_Z(cb, PAGE_SIZE);
289 cPages = cb >> PAGE_SHIFT;
290 cOrder = CalcPowerOf2Order(cPages);
291 paPages = virt_to_page(pv);
292
293 /*
294 * Restore page attributes freeing the pages.
295 */
296 for (iPage = 0; iPage < cPages; iPage++)
297 {
298 ClearPageReserved(&paPages[iPage]);
299#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 20) /** @todo find the exact kernel where change_page_attr was introduced. */
300 if (pgprot_val(MY_PAGE_KERNEL_EXEC) != pgprot_val(PAGE_KERNEL))
301 MY_CHANGE_PAGE_ATTR(&paPages[iPage], 1, PAGE_KERNEL);
302#endif
303 }
304 __free_pages(paPages, cOrder);
305 }
306}
307
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette