/* Memory management for efiemu */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2009 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
/*
To keep efiemu runtime contiguous this mm is special.
It uses deferred allocation.
In the first stage you may request memory with grub_efiemu_request_memalign
It will give you a handle with which in the second phase you can access your
memory with grub_efiemu_mm_obtain_request (handle). It's guaranteed that
subsequent calls with the same handle return the same result. You can't request any additional memory once you're in the second phase
*/
struct grub_efiemu_memrequest
{
int handle;
void *val;
};
/* Linked list of requested memory. */
/* Memory map. */
/* Pointer to allocated memory */
static void *resident_memory = 0;
/* Size of requested memory per type */
/* How many slots is allocated for memory_map and how many are already used */
/* Add a memory region to map*/
static grub_err_t
{
/* Extend map if necessary*/
if (mmap_num >= mmap_reserved_size)
{
* sizeof (grub_efi_memory_descriptor_t));
if (!efiemu_mmap)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"not enough space for memory map");
}
/* Fill slot*/
mmap_num++;
return GRUB_ERR_NONE;
}
/* Request a resident memory of type TYPE of size SIZE aligned at ALIGN
ALIGN must be a divisor of page size (if it's a divisor of 4096
it should be ok on all platforms)
*/
int
{
/* Check that the request is correct */
return -2;
/* Add new size to requested size */
if (align_overhead == align)
align_overhead = 0;
/* Remember the request */
if (!ret)
return -1;
prev = 0;
/* Add request to the end of the chain.
It should be at the end because otherwise alignment isn't guaranteed */
if (prev)
{
}
else
{
memrequests = ret;
}
}
/* Really allocate the memory */
static grub_err_t
efiemu_alloc_requests (void)
{
unsigned i;
/* Order of memory regions */
{
/* First come regions usable by OS*/
/* Then memory used by runtime */
/* This way all our regions are in a single block */
/* And then unavailable memory types. This is more for a completeness.
You should double think before allocating memory of any of these types
*/
};
/* Compute total memory needed */
{
if (align_overhead == GRUB_EFIEMU_PAGESIZE)
align_overhead = 0;
}
/* Allocate the whole memory in one block */
if (!resident_memory)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate resident memory");
/* Split the memory into blocks by type */
{
if (!requested_memory[reqorder[i]])
continue;
/* Write pointers to requests */
{
}
/* Ensure that the regions are page-aligned */
if (align_overhead == GRUB_EFIEMU_PAGESIZE)
align_overhead = 0;
/* Add the region to memory map */
}
return GRUB_ERR_NONE;
}
/* Get a pointer to requested memory from handle */
void *
{
return 0;
}
/* Get type of requested memory by handle */
{
return 0;
}
/* Free a request */
void
{
/* Remove head if necessary */
{
memrequests = cur;
}
if (!memrequests)
return;
/* Remove request from a middle of chain*/
{
}
else
{
}
}
/* Reserve space for memory map */
static grub_err_t
grub_efiemu_mmap_init (void)
{
__attribute__ ((unused)))
{
return 0;
}
// the place for memory used by efiemu itself
#ifndef GRUB_MACHINE_EMU
#endif
return GRUB_ERR_NONE;
}
/* This is a drop-in replacement of grub_efi_get_memory_map */
/* Get the memory map as defined in the EFI spec. Return 1 if successful,
return 0 if partial, or return -1 if an error occurs. */
int
{
if (!efiemu_mmap)
{
"you need to first launch efiemu_prepare");
return -1;
}
{
return 0;
}
if (descriptor_size)
*descriptor_size = sizeof (grub_efi_memory_descriptor_t);
if (descriptor_version)
*descriptor_version = 1;
if (map_key)
*map_key = 0;
return 1;
}
{
if (val == 1)
return GRUB_ERR_NONE;
if (val == -1)
return grub_errno;
}
/* Free everything */
grub_efiemu_mm_unload (void)
{
{
cur = d;
}
memrequests = 0;
resident_memory = 0;
efiemu_mmap = 0;
mmap_reserved_size = mmap_num = 0;
return GRUB_ERR_NONE;
}
/* This function should be called before doing any requests */
grub_efiemu_mm_init (void)
{
err = grub_efiemu_mm_unload ();
if (err)
return err;
return GRUB_ERR_NONE;
}
/* Copy host memory map */
static grub_err_t
grub_efiemu_mmap_fill (void)
{
{
switch (type)
{
case GRUB_MEMORY_AVAILABLE:
case GRUB_MEMORY_ACPI:
case GRUB_MEMORY_NVS:
default:
grub_dprintf ("efiemu",
"Unknown memory type %d. Assuming unusable\n", type);
case GRUB_MEMORY_RESERVED:
}
}
#ifndef GRUB_MACHINE_EMU
#endif
return GRUB_ERR_NONE;
}
{
unsigned i;
for (i = 0; i < (unsigned) mmap_num; i++)
switch (efiemu_mmap[i].type)
{
break;
case GRUB_EFI_UNUSABLE_MEMORY:
break;
case GRUB_EFI_PAL_CODE:
case GRUB_EFI_MAX_MEMORY_TYPE:
break;
case GRUB_EFI_LOADER_CODE:
case GRUB_EFI_LOADER_DATA:
break;
break;
case GRUB_EFI_ACPI_MEMORY_NVS:
break;
}
return 0;
}
/* This function resolves overlapping regions and sorts the memory map
It uses scanline (sweeping) algorithm
*/
static grub_err_t
{
/* If same page is used by multiple types it's resolved
according to priority
0 - free memory
1 - memory immediately usable after ExitBootServices
2 - memory usable after loading ACPI tables
3 - efiemu memory
4 - unusable memory
*/
{
[GRUB_EFI_LOADER_CODE] = 1,
[GRUB_EFI_LOADER_DATA] = 1,
[GRUB_EFI_BOOT_SERVICES_CODE] = 1,
[GRUB_EFI_BOOT_SERVICES_DATA] = 1,
[GRUB_EFI_CONVENTIONAL_MEMORY] = 0,
[GRUB_EFI_UNUSABLE_MEMORY] = 4,
[GRUB_EFI_ACPI_RECLAIM_MEMORY] = 2,
[GRUB_EFI_ACPI_MEMORY_NVS] = 3,
[GRUB_EFI_MEMORY_MAPPED_IO] = 4,
[GRUB_EFI_PAL_CODE] = 4
};
int i, j, k, done;
/* Scanline events */
struct grub_efiemu_mmap_scan
{
/* At which memory address*/
/* 0 = region starts, 1 = region ends */
int type;
/* Which type of memory region */
};
struct grub_efiemu_mmap_scan t;
/* Previous scanline event */
int lasttype;
/* Current scanline event */
int curtype;
/* how many regions of given type overlap at current location */
/* Here is stored the resulting memory map*/
/* Initialize variables*/
scanline_events = (struct grub_efiemu_mmap_scan *)
/* Number of chunks can't increase more than by factor of 2 */
if (!result || !scanline_events)
{
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
"couldn't allocate space for new memory map");
}
/* Register scanline events */
for (i = 0; i < mmap_num; i++)
{
}
/* Primitive bubble sort. It has complexity O(n^2) but since we're
unlikely to have more than 100 chunks it's probably one of the
fastest for one purpose */
done = 1;
while (done)
{
done = 0;
{
t = scanline_events[i + 1];
scanline_events[i] = t;
done = 1;
}
}
/* Pointer in resulting memory map */
j = 0;
for (i = 0; i < 2 * mmap_num; i++)
{
/* Process event */
if (scanline_events[i].type)
else
/* Determine current region type */
curtype = -1;
for (k = 0; k < GRUB_EFI_MAX_MEMORY_TYPE; k++)
curtype = k;
/* Add memory region to resulting map if necessary */
&& lasttype != -1)
{
/* We set runtime attribute on pages we need to be mapped */
? GRUB_EFI_MEMORY_RUNTIME : 0;
grub_dprintf ("efiemu",
"mmap entry: type %d start 0x%llx 0x%llx pages\n",
j++;
}
/* Update last values if necessary */
{
}
}
/* Shrink resulting memory map to really used size and replace efiemu_mmap
by new value */
return GRUB_ERR_NONE;
}
/* This function is called to switch from first to second phase */
grub_efiemu_mm_do_alloc (void)
{
/* Preallocate mmap */
if (!efiemu_mmap)
{
}
if ((err = efiemu_alloc_requests ()))
return err;
if ((err = grub_efiemu_mmap_fill ()))
return err;
return grub_efiemu_mmap_sort_and_uniq ();
}