Some rumpkernel APIs require memory allocated from 32 bit space if the
underlying device requires it for DMA. It is not clear which devices
require this so use it for all disk devices for the time being.
---
rumpdisk/block-rump.c | 101 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/rumpdisk/block-rump.c b/rumpdisk/block-rump.c
index aa4a387ff..aa6310b56 100644
--- a/rumpdisk/block-rump.c
+++ b/rumpdisk/block-rump.c
@@ -64,6 +64,28 @@
#endif
#endif
+/*
+ Certain device memory allocations within rumpkernel require 32 bit
+ physical addresses, for DMA purposes, for example. It is not
+ currently clear how to distinguish between memory allocations that
+ require this and those which do not. A temporary workaround is to
+ allocate all such pages within the 32 bit space by overriding the
+ rump calls that makes those allocations: 'rumpuser_malloc' and
+ 'rumpuser_free'.
+
+ Additionally, some devices (eg. i440fx) expect that the physical
+ address of the memory location passed as the IO buffer to
+ rump_sys_pread/rump_sys_pwrite are within the 32 bit address space
+ whereas others (eg. q35) do not. There is no simple method of
+ determining the requirements of the device so an interim solution is
+ to ensure all such buffers are allocated within the 32 bit physical
+ memory space.
+
+ Both of these features are enabled by defining:
+ RUMPDISK_USE_32BIT_PHYSADDRS
+*/
+#define RUMPDISK_USE_32BIT_PHYSADDRS 1
+
static bool disabled;
static mach_port_t master_host;
@@ -400,7 +422,11 @@ rumpdisk_device_write (void *d, mach_port_t reply_port,
return D_INVALID_OPERATION;
}
+#ifdef RUMPDISK_USE_32BIT_PHYSADDRS
+ if (1)
+#else
if ((vm_offset_t) data % pagesize)
+#endif
{
/* Not aligned, have to copy to aligned buffer. */
vm_address_t buf;
@@ -501,7 +527,12 @@ rumpdisk_device_read (void *d, mach_port_t reply_port,
/* TODO: directly write at *data when it is aligned */
*data = 0;
+#ifdef RUMPDISK_USE_32BIT_PHYSADDRS
+ rpc_phys_addr_t pap;
+ ret = vm_allocate_contiguous (master_host, mach_task_self (), &buf, &pap,
npages * pagesize, 0, 0x100000000ULL, 0);
+#else
ret = vm_allocate (mach_task_self (), &buf, npages * pagesize, TRUE);
+#endif
if (ret != KERN_SUCCESS)
{
pthread_rwlock_unlock (&rumpdisk_rwlock);
@@ -635,3 +666,73 @@ rump_register_block (void)
{
machdev_register (&rump_block_emulation_ops);
}
+
+#ifdef RUMPDISK_USE_32BIT_PHYSADDRS
+
+#include <mach/vm_param.h>
+
+int rumpuser_malloc (size_t howmuch, int alignment, void **memp);
+void rumpuser_free (void *ptr, size_t size);
+int rumpuser__errtrans (int err);
+
+#define ET(_v_) return (_v_) ? rumpuser__errtrans(_v_) : 0;
+
+int
+rumpuser_malloc (size_t howmuch, int align, void **memp)
+{
+ vm_address_t vma;
+ rpc_phys_addr_t phys;
+
+ /* rumpuser_malloc can in principle be called with any amount
+ required but that is not the case within rumpdisk. All calls
+ require single or multiple pages with N*PAGE_SIZE alignment. Any
+ unexpected calls should be rejected since this implementation
+ cannot cope otherwise.
+ */
+ if (align == 0 || (align % PAGE_SIZE) || (howmuch % PAGE_SIZE))
+ ET(EINVAL);
+
+ if (align > PAGE_SIZE)
+ {
+ /* vm_allocate_contiguous does not currently support alignments
+ other than PAGE_SIZE. The interim solution is to allocate
+ 'align' additional bytes and use the aligned portion within
+ the allocated range. The pages either side of the aligned
+ portion are simply unused and would be 'leaked' if
+ rumpuser_free was called (but it isn't for these
+ allocations).
+ */
+ howmuch += align;
+ }
+
+ int ret = vm_allocate_contiguous (master_host, mach_task_self(), &vma, &phys,
+ howmuch, 0, 0x100000000, PAGE_SIZE);
+
+ if (ret == KERN_SUCCESS)
+ {
+ if (align > PAGE_SIZE)
+ {
+ vma = (vma + align - 1) & ~(align - 1);
+ }
+
+ *memp = (void*)vma;
+ }
+
+ ET(ret);
+}
+
+void
+rumpuser_free (void *ptr, size_t size)
+{
+ /* This might leak pages that were allocated with an alignment
+ larger than PAGE_SIZE although seemingly rumpuser_free() isn't
+ ever called within rumpdisk. */
+ int ret = vm_deallocate(mach_task_self(), (vm_offset_t)ptr, size);
+ if (ret)
+ {
+ fprintf (stderr, "warning: rumpuser_free failed: %s\n", strerror(ret));
+ fflush (stderr);
+ }
+}
+
+#endif
--
2.47.3