Allow allocating arbitrarily aligned physical memory regions

This will be needed for USB (XHCI controller driver).
This commit is contained in:
Josh Holtrop 2025-02-21 15:31:48 -05:00
parent b2497d1ee0
commit 7313b01732
2 changed files with 73 additions and 0 deletions

View File

@ -198,6 +198,7 @@ task "run", desc: "Run HOS in QEMU" do
sh %W[ sh %W[
qemu-system-x86_64 qemu-system-x86_64
-machine q35 -machine q35
-m 128M
-cpu max -cpu max
-smp cpus=4 -smp cpus=4
-serial file:qemu/serial.out -serial file:qemu/serial.out

View File

@ -89,6 +89,12 @@ struct Hippo
*/ */
public static void free_region(T)(T start, ulong size) public static void free_region(T)(T start, ulong size)
{ {
if (size == PAGE_SIZE)
{
free_page(start);
return;
}
if (regions is null) if (regions is null)
{ {
/* The free regions list is empty. Append this new region. */ /* The free regions list is empty. Append this new region. */
@ -124,6 +130,72 @@ struct Hippo
m_n_free_pages += size >> 12; m_n_free_pages += size >> 12;
} }
/**
* Allocate a region with the given size and alignment.
*
* @param size Region size.
* @param alignment Region alignment.
*/
public static void * allocate_aligned_region(ulong size, ulong alignment)
{
Region ** regionp = &regions;
for (;;)
{
Region * region = *regionp;
if (region is null)
{
break;
}
/* The size of the region must be large enough */
if (region.size >= size)
{
/* The alignment contstraint must be satisfied. */
if ((cast(ulong)region & (alignment - 1)) == 0)
{
/* The region start address happens to already be aligned. */
if (region.size > size)
{
/* Region found is larger that needed. */
Region * new_region = cast(Region *)(cast(ulong)region + size);
new_region.size = region.size - size;
new_region.next = region.next;
*regionp = new_region;
return region;
}
else
{
/* Region happens to be the size requested. */
*regionp = region.next;
return region;
}
}
else
{
/* This region is large enough but not aligned. See if an
* aligned subregion can be found at the end. */
ulong start = (cast(ulong)region + region.size - size) & ~(alignment - 1);
if (start > cast(ulong)region)
{
/* The aligned subregion does fit in this region. */
region.size = (start - cast(ulong)region);
ulong region_end = cast(ulong)region + region.size;
if (region_end > (start + size))
{
/* There is another region following the aligned
* subregion that we must reclaim. */
free_region(start + size, region_end - (start + size));
}
return cast(void *)start;
}
}
}
regionp = &region.next;
}
return null;
}
/** /**
* Get the number of free pages. * Get the number of free pages.
* *