This document explains how to reserve a segment of the physical memory (RAM)
for exclusive use, and then access it using WinDriver, on Windows or Linux.
NOTE
- In most cases, there is no need to resort to this method in order to
reserve segments of memory for exclusive use. Normally, you can lock a
safe Direct Memory Access (DMA) buffer (e.g., using WinDriver's DMA
APIs) and then access the buffer from your driver. The method described
in this document should be used only in rare cases of
"memory-intensive" driver projects and only when the required memory
block cannot be locked using standard methods, such as allocation of a
contiguous DMA buffer.
- When using this method, take special care not to write to the wrong
memory addresses, so as to avoid system crashes, etc.
- Reserve the desired amount of RAM on
Windows or
Linux
- Windows: Calculate the base address of the reserved memory
- Access the memory using WinDriver
- Reserve the desired amount of RAM
by following the
instructions for your OS:
- On Windows —
On Windows Vista and newer, set the
removememory boot entry option to the desired memory
amount, using the BCDEdit utility:
BCDEdit /set removememory <size_in_MB>
For example:
BCDEdit /set removememory 100
On Windows Server 2003 and earlier, modify the
boot.ini file on your PC to add a burnmemory
parameter, to the relevant Windows configuration:
/burnmemory=<size_in_MB> .
For example, on Windows XP:
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows XP, Enterprise"
/fastdetect /NoExecute=OptIn /burnmemory=100
This instructs Windows to refrain from using the specified
amount of memory in the highest area of the PC's physical
memory (RAM).
NOTE: When selecting the amount of memory to reserve,
take care not to exceed or to take up too much of the memory
allocated by default to the operating system's highest address
space (see Step 2). If the
size of the reserved memory segment is too big, this might
result in degraded OS performance.
- On Linux —
- Run the following command to view a list of the
physical memory ranges on your machine:
dmesg | grep BIOS
This produces entries as in the following sample output;
"usable" identifies memory sections that are
used by Linux:
[ 0.000000] BIOS-e820: 0000000000000000 - 000000000009f800 (usable)
[ 0.000000] BIOS-e820: 000000000009f800 - 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 - 000000007dce0000 (usable)
[ 0.000000] BIOS-e820: 000000007dce0000 - 000000007dce3000 (ACPI NVS)
[ 0.000000] BIOS-e820: 000000007dce3000 - 000000007dcf0000 (ACPI data)
[ 0.000000] BIOS-e820: 000000007dcf0000 - 000000007dd00000 (reserved)
[ 0.000000] BIOS-e820: 00000000c0000000 - 00000000d0000000 (reserved)
[ 0.000000] BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
- Edit the boot-loader command line to instruct the Linux
kernel to boot with less memory in the desired address
range.
For example, the following line from the sample
dmesg output shown above
[ 0.000000] BIOS-e820: 0000000000100000 - 000000007dce0000 (usable)
indicates that there is a ~1.95GB address range,
starting at address 0x100000, that is used by Linux. To
reserve ~150MB of memory at the end of this range for
DMA, on a machine with a GRUB boot loader, add the
following to the grub file:
GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX}
mem=1800M [email protected]"
This instructs Linux to boot with ~1,800MB
("mem=1800M") starting at address
0x100000 ("@1M"). Reconfigure GRUB to apply the
changes — e.g.:
sudo grub-mkconfig -o /boot/grub/grub.cfg
- Reboot Linux.
-
Run `
dmesg | grep BIOS\|user `
to see the available physical memory ranges. The output
should include a BIOS-provided physical RAM mappings
section with the "BIOS... " lines from the
original dmesg output, and a new user-defined
RAM mappings section with "user... " lines
indicating the actual available physical memory ranges
(based on user definitions).
The user entry for the memory range you modified in the
previous steps should be missing the portion you
reserved. For example, for the original BIOS mappings
and boot-loader changes examples provided in the
previous steps, the new output should look like similar
to this:
[ 0.000000] BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: 0000000000000000 - 000000000009f800 (usable)
[ 0.000000] BIOS-e820: 000000000009f800 - 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 - 000000007dce0000 (usable)
[ 0.000000] BIOS-e820: 000000007dce0000 - 000000007dce3000 (ACPI NVS)
[ 0.000000] BIOS-e820: 000000007dce3000 - 000000007dcf0000 (ACPI data)
[ 0.000000] BIOS-e820: 000000007dcf0000 - 000000007dd00000 (reserved)
[ 0.000000] BIOS-e820: 00000000c0000000 - 00000000d0000000 (reserved)
[ 0.000000] BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
[ 0.000000] user-defined physical RAM map:
[ 0.000000] user: 0000000000000000 - 000000000009f800 (usable)
[ 0.000000] user: 000000000009f800 - 00000000000a0000 (reserved)
[ 0.000000] user: 00000000000f0000 - 0000000000100000 (reserved)
[ 0.000000] user: 0000000000100000 - 0000000070900000 (usable)
[ 0.000000] user: 000000007dce0000 - 000000007dce3000 (ACPI NVS)
[ 0.000000] user: 000000007dce3000 - 000000007dcf0000 (ACPI data)
[ 0.000000] user: 000000007dcf0000 - 000000007dd00000 (reserved)
[ 0.000000] user: 00000000c0000000 - 00000000d0000000 (reserved)
Comparing the following line from the BIOS-provided
mapping section:
[ 0.000000] BIOS-e820: 0000000000100000 - 000000007dce0000 (usable)
with the following line from the user-defined mapping
section
[ 0.000000] user: 0000000000100000 - 0000000070900000 (usable)
shows that the original ~1.95GB memory range —
0x100000–0x7dce0000 — was reduced to a
~1.75GB range — 0x100000–0x70900000. The
memory in address range 0x70900000–0x7dce0000 is
no longer available because it has been reserved in the
previous steps, allowing you to use this range for DMA,
as demonstrated in Step 3
of this document.
- Windows: Calculate the base address of the reserved memory
To acquire the base address of the reserved memory segment on Windows,
you must first determine the physical memory mapping on your PC and
retrieve the base address and length (in bytes) of the highest address
space used by the operating system. Then add the length of this address
space to its base address to receive the base address of your reserved
memory segment:
<reserved memory base address> =
<highest OS physical memory base address> +
<length of the highest OS memory address space>
NOTE: To verify the size of your reserved memory block, compare the
length of the highest OS address space, before and after modifying
boot configuration to reserve the memory (as outlined in
Step 1.
You can use the following method to determine the highest physical
memory base address: Open the registry
(Start --> Run --> regedit.exe) and navigate to the
HKEY_LOCAL_MACHINE\HARDWARE\RESOURCEMAP\System Resources\Physical Memory\.Translated
registry key. This key is of type REG_RESOURCE_LIST and
holds information regarding the physical memory mapping on your PC. To
view a parsed version of the mapped addresses, double-click on the
.Translated key, select the relevant resource from
the Resource Lists dialog, and double-click on the resource (or
select Display... ) in order to display the
Resources dialog, which contains a list of all memory address
ranges for the selected resource. The base address for your reserved
physical memory block is calculated by locating the highest base
address in the list and adding to it the length of the relevant address
space.
For example, for the following Resources dialog, the highest
base address is 0x1000000 and the length of the address space that
begins at this address is 0x1eff0000, so the base address of the
reserved memory is 0x1fff0000:
<reserved memory base address> =
0x1000000 + 0x1eff0000 = 0x1fff0000
- Access the memory using WinDriver
Once you acquire the physical base address of the memory segment that
you reserved, you can easily access it using WinDriver, as you would
any memory on an ISA card.
You can use WinDriver's DriverWizard to test the access to the memory
you reserved: Use the wizard to create a new ISA project, define the
new memory item according to the information you acquired in the
previous step(s), then proceed to access the memory with the wizard.
You can also use DriverWizard to generate a sample diagnostics
application that demonstrates how to lock and access the reserved
memory region using WinDriver's API.
The following code segment demonstrates how you can define and lock the
physical memory using WinDriver's WDC API. The handle returned by the
sample LockReservedMemory() function can be used to access
the memory using WinDriver's WDC memory access APIs, defined in the
WinDriver/include/wdc_lib.h header file:
NOTE
There may be some differences between the API in your version of
WinDriver and that used in the following example (such as differences
in field names and/or types).
/* LockReservedMemory: Returns a WDC handle for accessing
the reserved memory block.
Parameters:
pReservedRegionBase: The physical base address of
the reserved memory region (as calculated in the
previous step)
qwReservedRegionLength: The length (in bytes) of the
reserved memory region, i.e.,:
<size_in_MB> (as configured in
Step 1) * 0x100000
Note:
The function uses the high-level WDC APIs.
You can implement similar code using the low-level
WD_CardRegister() API — see the WinDriver User's
Manual for details.
*/
WDC_DEVICE_HANDLE LockReservedMemory(PHYS_ADDR pReservedRegionBase,
UINT64 qwReservedRegionLength)
{
DWORD dwStatus;
WDC_DEVICE_HANDLE hDev = NULL;
WD_CARD deviceInfo;
/* Set the reserved memory's resources information */
BZERO(deviceInfo);
SetMemoryItem(&deviceInfo, pReservedRegionBase,
qwReservedRegionLength);
/* Get a handle to the reserved memory block */
dwStatus = WDC_IsaDeviceOpen(&hDev, &deviceInfo, NULL,
NULL, NULL, NULL);
if (WD_STATUS_SUCCESS != dwStatus)
{
printf(
"Failed opening a WDC device handle.
Error 0x%lx — %s\n", dwStatus,
Stat2Str(dwStatus));
return NULL;
}
/* Return the handle to the reserved memory */
return hDev;
}
/* SetMemoryItem: Initializes a WDC device information
structure for a specified memory region.
Parameters:
pDeviceInfo: Pointer to a resources information structure
pPhysAddr: The memory region's physical
base address
qwBytes: The memory region's length, in bytes
*/
void
SetMemoryItem(WD_CARD *pDeviceInfo, PHYS_ADDR pPhysAddr, UINT64 qwBytes)
{
WD_ITEMS *pItem = pDeviceInfo->Item;
pDeviceInfo->dwItems = 2;
/* 1st item: Bus */
pItem[0].item = ITEM_BUS;
pItem[0].I.Bus.dwBusType = WD_BUS_ISA;
/* 2nd item: Memory */
pItem[1].item = ITEM_MEMORY;
/* Lock the memory for exclusive use */
pItem[1].fNotSharable = 1;
/* Set the reserved memory's base address */
pItem[1].I.Mem.pPhysicalAddr = pPhysAddr;
/* Set the reserved memory's size */
pItem[1].I.Mem.qwBytes = qwBytes;
/* Set the reserved memory's address space */
pItem[1].I.Mem.dwBar = 0;
/* Map physical memory as cached (applicable only to RAM).
(This option is supported on Windows NT+ and CE
beginning with version 7.0.2 of WinDriver.) */
pItem[1].I.Mem.dwOptions = WD_ITEM_MEM_ALLOW_CACHE;
}
|