VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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