vfio: add check for memory region overflow condition
PATCH 1/1 vfio: add check for memory region overflow condition
vfio_listener_region_add for a iommu mr results in
an overflow assert since emulated iommu memory region is initialized with UINT64_MAX. Add a check just like memory_region_size() does.
--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
hw/vfio/common.c +6 -1
@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; } - end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"
Signature · 1 line
2.7.0
Quoted 18 lines · On Mon, 21 Mar 2016 18:00:50 -0400 Bandan Das <[email protected]> wrote:
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.Signed-off-by Bandan Das <[email protected]>--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)hw/vfio/common.c +0
-0@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; }
hw/vfio/common.c static void vfio_listener_region_add(MemoryListener *listener, · line 360
Quoted 10 lines
hw/vfio/common.c +6
-1- end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"
But now all the calculations where we use end-1 are wrong. See the discussion with Pierre Morel in the January qemu-devel archives. There's a solution in there, but I never saw a follow-up from Pierre with a revised patch. Thanks,
Alex
Quoted 29 lines · Alex Williamson
Quoted 26 lines · On Mon, 21 Mar 2016 18:00:50 -0400 Bandan Das <[email protected]> wrote:
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.Signed-off-by Bandan Das <[email protected]>--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)hw/vfio/common.c +6
-1@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; } - end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }
Quoted 7 lines
hw/vfio/common.c static void vfio_listener_region_add(MemoryListener *listener, · line 360
Quoted 2 lines
hw/vfio/common.c +0
-0if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"But now all the calculations where we use end-1 are wrong. See the discussion with Pierre Morel in the January qemu-devel archives. There's a solution in there, but I never saw a follow-up from Pierre with a revised patch. Thanks,
I am missing something. When end < UIN64_MAX, end - 1 calculations are valid because the patch doesn't change that behavior. When end is UINT64_MAX, int128_get64() doesn't know how to calculate this value and we are just feeding it manually. The patch is just the opposite of what memory_region_init() did to init the mem region in the first place:
mr->size = int128_make64(size);
if (size == UINT64_MAX) {
mr->size = int128_2_64(); }
So, end - 1 is still valid for end = UINT64_MAX, no ?
Quoted 1 line
Alex
Quoted 43 lines · On Mon, 21 Mar 2016 20:06:32 -0400 Bandan Das <[email protected]> wrote:
Quoted 29 lines · Alex Williamson
Quoted 26 lines · On Mon, 21 Mar 2016 18:00:50 -0400 Bandan Das <[email protected]> wrote:
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.Signed-off-by Bandan Das <[email protected]>--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)hw/vfio/common.c +6
-1@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; } - end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }Quoted 7 lines
hw/vfio/common.c static void vfio_listener_region_add(MemoryListener *listener, · line 360
Quoted 2 lines
hw/vfio/common.c +0
-0if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"But now all the calculations where we use end-1 are wrong. See the discussion with Pierre Morel in the January qemu-devel archives. There's a solution in there, but I never saw a follow-up from Pierre with a revised patch. Thanks,
I am missing something. When end <
UIN64_MAX, end - 1 calculations are valid because the patch doesn't change that behavior. When end isUINT64_MAX,int128_get64()doesn't know how to calculate this value and we are just feeding it manually. The patch is just the opposite of whatmemory_region_init()did to init the mem region in the first place:
Quoted 5 lines
mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); }So, end - 1 is still valid for end =
UINT64_MAX, no ?
int128_2_64() is not equal to UINT64_MAX, so assigning UIN64_MAX to @end is clearing altering the value. If we had a range from zero to int128_2_64() then the size of that region is int128_2_64(). If we alter @end to be UINT64_MAX, then the size is only UINT64_MAX and @end
- 1 is off by one versus the case where we use the value directly. You're effectively changing @end to be the last address in the range, but only in some cases, and not adjusting the remaining code to match. Not only that, but the vfio map command is probably going to fail if we pass in such an unaligned size since the mapping granularity is likely the system page size. Thanks,
Alex
Quoted 52 lines · Alex Williamson
Quoted 43 lines · On Mon, 21 Mar 2016 20:06:32 -0400 Bandan Das <[email protected]> wrote:
Quoted 29 lines · Alex Williamson
Quoted 26 lines · On Mon, 21 Mar 2016 18:00:50 -0400 Bandan Das <[email protected]> wrote:
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.Signed-off-by Bandan Das <[email protected]>--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)hw/vfio/common.c +6
-1@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; } - end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }Quoted 7 lines
hw/vfio/common.c static void vfio_listener_region_add(MemoryListener *listener, · line 360
Quoted 2 lines
hw/vfio/common.c +0
-0if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"But now all the calculations where we use end-1 are wrong. See the discussion with Pierre Morel in the January qemu-devel archives. There's a solution in there, but I never saw a follow-up from Pierre with a revised patch. Thanks,
I am missing something. When end <
UIN64_MAX, end - 1 calculations are valid because the patch doesn't change that behavior. When end isUINT64_MAX,int128_get64()doesn't know how to calculate this value and we are just feeding it manually. The patch is just the opposite of whatmemory_region_init()did to init the mem region in the first place:Quoted 5 lines
mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); }So, end - 1 is still valid for end =
UINT64_MAX, no ?
Quoted 2 lines
int128_2_64()is not equal toUINT64_MAX, so assigningUIN64_MAXto @end is clearing altering the value. If we had a range from zero to
I thought in128_2_64 is the 128 bit representation of UINT64_MAX. The if condition in memory_region_init doesn't make sense otherwise.
Quoted 3 lines
int128_2_64()then the size of that region isint128_2_64(). If we alter @end to beUINT64_MAX, then the size is onlyUINT64_MAXand @end
- 1 is off by one versus the case where we use the value directly.
Ok, you mean something like:int128_get64(int128_sub(int128_2_64(), int128_make64(1))); for (end - 1) ? But we still have to deal with (end - iova) when calling vfio_dmap_map(). int128_get64() will definitely assert for iova = 0.
Quoted 1 line
You're effectively changing @end to be the last address in the range,
No, I think I am changing "end" to what we initally started with for size before converting to 128 bit.
Quoted 3 lines
but only in some cases, and not adjusting the remaining code to match. Not only that, but the vfio map command is probably going to fail if we pass in such an unaligned size since the mapping granularity is
Trying to map such a large region is wrong anyway, I am still trying to workout a solution to avoid calling memory_region_init_iommu() with UINT64_MAX which is what emulated vt-d currently does.
Quoted 3 lines
likely the system page size. Thanks,
Alex
Quoted 54 lines · On Mon, 21 Mar 2016 21:54:48 -0400 Bandan Das <[email protected]> wrote:
Quoted 52 lines · Alex Williamson
Quoted 48 lines · On Mon, 21 Mar 2016 20:06:32 -0400 Bandan Das <[email protected]> wrote:
Quoted 29 lines · Alex Williamson
Quoted 26 lines · On Mon, 21 Mar 2016 18:00:50 -0400 Bandan Das <[email protected]> wrote:
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.Signed-off-by Bandan Das <[email protected]>--- hw/vfio/common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)hw/vfio/common.c +6
-1@@ -349,7 +349,12 @@ static void vfio_listener_region_add(MemoryListener *listener,if (int128_ge (int128_make64 (iova), llend)) {return ; } - end =int128_get64 (llend); + +if (int128_eq (llend,int128_2_64 ())) { + end =UINT64_MAX ; + }else { + end =int128_get64 (llend); + }Quoted 7 lines
hw/vfio/common.c static void vfio_listener_region_add(MemoryListener *listener, · line 360
Quoted 2 lines
hw/vfio/common.c +0
-0if ((iova < container->min_iova) || ((end -1 ) > container->max_iova)) { error_report("vfio: IOMMU container %p can't map guest IOVA region"But now all the calculations where we use end-1 are wrong. See the discussion with Pierre Morel in the January qemu-devel archives. There's a solution in there, but I never saw a follow-up from Pierre with a revised patch. Thanks,
I am missing something. When end <
UIN64_MAX, end - 1 calculations are valid because the patch doesn't change that behavior. When end isUINT64_MAX,int128_get64()doesn't know how to calculate this value and we are just feeding it manually. The patch is just the opposite of whatmemory_region_init()did to init the mem region in the first place:mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); }So, end - 1 is still valid for end =
UINT64_MAX, no ?
Quoted 5 lines
Quoted 2 lines
int128_2_64()is not equal toUINT64_MAX, so assigningUIN64_MAXto @end is clearing altering the value. If we had a range from zero toI thought
in128_2_64is the 128 bit representation ofUINT64_MAX. The if condition inmemory_region_initdoesn't make sense otherwise.
2^64 cannot be represented with a uint64_t, 2^64 - 1 can:
int128_2_64 = 1_0000_0000_0000_0000hUINT64_MAX = ffff_ffff_ffff_ffffh
Quoted 8 lines
Quoted 3 lines
int128_2_64()then the size of that region isint128_2_64(). If we alter @end to beUINT64_MAX, then the size is onlyUINT64_MAXand @end
- 1 is off by one versus the case where we use the value directly.Ok, you mean something like:
int128_get64(int128_sub(int128_2_64(), int128_make64(1))); for (end - 1) ? But we still have to deal with (end - iova) when callingvfio_dmap_map().int128_get64()will definitely assert for iova = 0.
I don't know that that's the most efficient way to handle it, but @end represents a different thing by imposing that -1 and it needs to be handled in the reset of the code.
Quoted 4 lines
Quoted 1 line
You're effectively changing @end to be the last address in the range,
No, I think I am changing "end" to what we initally started with for size before converting to 128 bit.
Nope, it's the difference between the size of the region and the last address of the region.
Quoted 7 lines
Quoted 3 lines
but only in some cases, and not adjusting the remaining code to match. Not only that, but the vfio map command is probably going to fail if we pass in such an unaligned size since the mapping granularity is
Trying to map such a large region is wrong anyway, I am still trying to workout a solution to avoid calling
memory_region_init_iommu()withUINT64_MAXwhich is what emulated vt-d currently does.
Right, the address width of the IOMMU on x86 is typically nowhere near 2^64, so if you take the vfio_dma_map path, you'll surely explode. Does this fix actually fix anything or just move us to the next assert? Thanks,
Alex
Alex Williamson <[email protected]> writes:
...
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines
mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); }So, end - 1 is still valid for end =
UINT64_MAX, no ?
int128_2_64()is not equal toUINT64_MAX, so assigningUIN64_MAXto @end is clearing altering the value. If we had a range from zero to
Quoted 7 lines
Quoted 2 lines
I thought
in128_2_64is the 128 bit representation ofUINT64_MAX. The if condition inmemory_region_initdoesn't make sense otherwise.2^64 cannot be represented with a
uint64_t, 2^64 - 1 can:
int128_2_64= 1_0000_0000_0000_0000hUINT64_MAX=ffff_ffff_ffff_ffffh
Thanks, understood this part. I still don't understand the if condition in memory_region_init however. Unless, that function actually takes the last address for the size parameter and in that case, it should be UINT64_MAX-1 for a size of UINT64_MAX.
Quoted 13 lines
Quoted 8 lines
Quoted 3 lines
int128_2_64()then the size of that region isint128_2_64(). If we alter @end to beUINT64_MAX, then the size is onlyUINT64_MAXand @end
- 1 is off by one versus the case where we use the value directly.Ok, you mean something like:
int128_get64(int128_sub(int128_2_64(), int128_make64(1))); for (end - 1) ? But we still have to deal with (end - iova) when callingvfio_dmap_map().int128_get64()will definitely assert for iova = 0.I don't know that that's the most efficient way to handle it, but @end represents a different thing by imposing that -1 and it needs to be handled in the reset of the code.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
You're effectively changing @end to be the last address in the range,
No, I think I am changing "end" to what we initally started with for size before converting to 128 bit.
Nope, it's the difference between the size of the region and the last address of the region.
Ok, but note that it's the "size" that actually asserts here since the offset is 0. So, we started with a size UINT64_MAX but end with mr->size = 128_2_64().
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
but only in some cases, and not adjusting the remaining code to match. Not only that, but the vfio map command is probably going to fail if we pass in such an unaligned size since the mapping granularity is
Trying to map such a large region is wrong anyway, I am still trying to workout a solution to avoid calling
memory_region_init_iommu()withUINT64_MAXwhich is what emulated vt-d currently does.Right, the address width of the IOMMU on x86 is typically nowhere near 2^64, so if you take the
vfio_dma_mappath, you'll surely explode.
And it does. If we fix this assert, then vfio_dma_map() attempts mapping this direct mapped address range starting from 0 and prints a warning message; happens for the whole range and goes on for ever. The overflow check seemed to me like something we should fix, but now I am more confused then ever!
Quoted 4 lines
Does this fix actually fix anything or just move us to the next assert? Thanks,
Alex
Quoted 16 lines · On Tue, 22 Mar 2016 14:55:14 -0400 Bandan Das <[email protected]> wrote:
Alex Williamson <[email protected]> writes:
...Quoted 14 lines
Quoted 11 lines
Quoted 8 lines
Quoted 5 lines
mr->size = int128_make64(size); if (size == UINT64_MAX) { mr->size = int128_2_64(); }So, end - 1 is still valid for end =
UINT64_MAX, no ?
int128_2_64()is not equal toUINT64_MAX, so assigningUIN64_MAXto @end is clearing altering the value. If we had a range from zero toI thought
in128_2_64is the 128 bit representation ofUINT64_MAX. The if condition inmemory_region_initdoesn't make sense otherwise.2^64 cannot be represented with a
uint64_t, 2^64 - 1 can:
Quoted 7 lines
Quoted 2 lines
int128_2_64= 1_0000_0000_0000_0000hUINT64_MAX=ffff_ffff_ffff_ffffhThanks, understood this part. I still don't understand the if condition in
memory_region_inithowever. Unless, that function actually takes the last address for the size parameter and in that case, it should be UINT64_MAX-1 for a size ofUINT64_MAX.
Seems like some sort of compatibility convention since memory_region_init() only takes a uint64_t as the size. memory.c interprets that as "oh, I know you really mean 2^64".
Quoted 18 lines
Quoted 18 lines
Quoted 8 lines
Quoted 3 lines
int128_2_64()then the size of that region isint128_2_64(). If we alter @end to beUINT64_MAX, then the size is onlyUINT64_MAXand @end
- 1 is off by one versus the case where we use the value directly.Ok, you mean something like:
int128_get64(int128_sub(int128_2_64(), int128_make64(1))); for (end - 1) ? But we still have to deal with (end - iova) when callingvfio_dmap_map().int128_get64()will definitely assert for iova = 0.I don't know that that's the most efficient way to handle it, but @end represents a different thing by imposing that -1 and it needs to be handled in the reset of the code.
Quoted 4 lines
Quoted 1 line
You're effectively changing @end to be the last address in the range,
No, I think I am changing "end" to what we initally started with for size before converting to 128 bit.
Quoted 6 lines
Quoted 2 lines
Nope, it's the difference between the size of the region and the last address of the region.
Ok, but note that it's the "size" that actually asserts here since the offset is 0. So, we started with a size
UINT64_MAXbut end withmr->size= 128_2_64().
A size of UINT64_MAX doesn't make much sense to me, that would mean the last address is UINT64_MAX - 1. A size of 2^64 means the last address is UINT64_MAX, which seems reasonable.
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
but only in some cases, and not adjusting the remaining code to match. Not only that, but the vfio map command is probably going to fail if we pass in such an unaligned size since the mapping granularity is
Trying to map such a large region is wrong anyway, I am still trying to workout a solution to avoid calling
memory_region_init_iommu()withUINT64_MAXwhich is what emulated vt-d currently does.
Quoted 8 lines
Quoted 2 lines
Right, the address width of the IOMMU on x86 is typically nowhere near 2^64, so if you take the
vfio_dma_mappath, you'll surely explode.And it does. If we fix this assert, then
vfio_dma_map()attempts mapping this direct mapped address range starting from 0 and prints a warning message; happens for the whole range and goes on for ever. The overflow check seemed to me like something we should fix, but now I am more confused then ever!
Is the MemoryRegion memory_region_is_iommu() such that you're calling vfio_dma_map() from vfio_iommu_map_notify()? If so then we should probably be using 128bit helpers for doing sanity checking and go ahead and let something assert if we get to the vfio_dma_map() in vfio_listener_region_add() with a 2^64 size. Then if you're taking the memory_region_is_iommu() path, vfio_dma_map() is going to be called with translations within that 2^64 bit address space, not mapping the entire space, right? Thanks,
Alex
Alex Williamson <[email protected]> writes:
...
Quoted 9 lines
Quoted 6 lines
And it does. If we fix this assert, then
vfio_dma_map()attempts mapping this direct mapped address range starting from 0 and prints a warning message; happens for the whole range and goes on for ever. The overflow check seemed to me like something we should fix, but now I am more confused then ever!Is the MemoryRegion
memory_region_is_iommu()such that you're callingvfio_dma_map()fromvfio_iommu_map_notify()? If so then we should
Yes, that is correct. This all started after we added the iommu mapping replay changes but I was wrong about the vfio_dma_map part. Please see below.
Quoted 6 lines
probably be using 128bit helpers for doing sanity checking and go ahead and let something assert if we get to the
vfio_dma_map()invfio_listener_region_add()with a 2^64 size. Then if you're taking thememory_region_is_iommu()path,vfio_dma_map()is going to be called with translations within that 2^64 bit address space, not mapping the entire space, right? Thanks,
The 128 bit operations make sense...
The error message comes from:
if (!memory_region_is_ram(mr)) {
error_report("iommu map to non memory area %"HWADDR_PRIx"",
xlat);
goto out;
}in vfio_iommu_map_notify() before we even get to vfio_dma_map().
This gets attempted for the entire range because dmar isn't enabled yet and vtd_iommu_translate() does this direct mapping in 4k increments in the translate path :
...
if (!s->dmar_enabled) {
/* DMAR disabled, passthrough, use 4k-page*/
ret.iova = addr & VTD_PAGE_MASK_4K;
ret.translated_addr = addr & VTD_PAGE_MASK_4K;
ret.addr_mask = ~VTD_PAGE_MASK_4K;
ret.perm = IOMMU_RW;
return ret;
}I am not sure yet who actually uses it though. memory_region_iommu_replay() does the whole iteration if perm != IOMMU_NONE:
void memory_region_iommu_replay(MemoryRegion *mr, Notifier *n,
hwaddr granularity, bool is_write)
{
hwaddr addr;
IOMMUTLBEntry iotlb;
for (addr = 0; addr < memory_region_size(mr); addr += granularity) {
iotlb = mr->iommu_ops->translate(mr, addr, is_write);
if (iotlb.perm != IOMMU_NONE) {
n->notify(n, &iotlb);
}...
Quoted 1 line
Alex
Quoted 5 lines · Bandan Das
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.
Hi, Bandan,
In case you missed this:
https://lists.gnu.org/archive/html/qemu-devel/2016-03/msg02865.html
-- peterx
Quoted 7 lines · Peter Xu
Quoted 5 lines · Bandan Das
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.
Quoted 5 lines
Hi, Bandan,
In case you missed this:
https://lists.gnu.org/archive/html/qemu-devel/2016-03/msg02865.html
Thank you Peter, I wasn't aware. But unfortunately, I don't think he's doing the right thing either when handing @end ? Oh well, it's a RFC :)
Quoted 1 line
-- peterx
Quoted 9 lines · On Tue, 22 Mar 2016 15:07:00 -0400 Bandan Das <[email protected]> wrote:
Quoted 7 lines · Peter Xu
Quoted 5 lines · Bandan Das
vfio_listener_region_addfor a iommu mr results in
an overflow assert since emulated iommu memory region is initialized withUINT64_MAX. Add a check just likememory_region_size()does.
Quoted 8 lines
Quoted 5 lines
Hi, Bandan,
In case you missed this:
https://lists.gnu.org/archive/html/qemu-devel/2016-03/msg02865.html
Thank you Peter, I wasn't aware. But unfortunately, I don't think he's doing the right thing either when handing @end ? Oh well, it's a RFC :)
Agree, the fix there is bogus too.
Quoted 2 lines · Bandan Das
Thank you Peter, I wasn't aware. But unfortunately, I don't think he's doing the right thing either when handing @end ? Oh well, it's a RFC :)
Possibly. Just to make sure you know about the whole thing (rather than only the @end fix), since I see that fixing the core dump should possibly be the start to move on to VFIO+IOMMU. As you mentioned, guessing that it's never a bad thing to know more about ideas. :)
Thanks.
-- peterx