dma/rc4030: do multiple calls to address_space_rw when doing DMA transfers
PATCH 1/1 dma/rc4030: do multiple calls to address_space_rw when doing DMA transfers
This workarounds a bug in memory management.
To reproduce the problem, try to start the Windows NT 4.0/MIPS installer. After loading some files, you should see a screen saying
"To set up Windows NT now, press ENTER."
However, you're welcomed with an IRQL_NOT_LESS_OR_EQUAL bugcheck or an Unknown Hard Error c0000221.
--- hw/dma/rc4030.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
hw/dma/rc4030.c +15 -0
@@ -681,6 +681,7 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wrirc4030State *s = opaque;hwaddr dma_addr;int dev_to_mem; +int i; s->dma_regs[n][DMA_REG_ENABLE ] &= ~(DMA_FLAG_TC_INTR |DMA_FLAG_MEM_INTR |DMA_FLAG_ADDR_INTR );
@@ -699,8 +700,22 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri dma_addr = s->dma_regs[n][DMA_REG_ADDRESS ];/* Read/write data at right place */ +#if 1 /* workaround for a bug in memory management */ +for (i =0 ; i < len; ) { +int ncpy =DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE -1 )); +if (ncpy > len - i) { + ncpy = len - i; + } +address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , + buf + i, ncpy, is_write); + + dma_addr += ncpy; + i += ncpy; + } +#else address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , buf, len, is_write); +#endif s->dma_regs[n][DMA_REG_ENABLE ] |=DMA_FLAG_TC_INTR ; s->dma_regs[n][DMA_REG_COUNT ] -= len;
Signature · 1 line
2.1.4
Quoted 35 lines · Hervé Poussineau
This workarounds a bug in memory management.
To reproduce the problem, try to start the Windows NT 4.0/MIPS installer. After loading some files, you should see a screen saying
"To set up Windows NT now, press ENTER."
However, you're welcomed with anIRQL_NOT_LESS_OR_EQUALbugcheck or an Unknown Hard Errorc0000221.Signed-off-by Hervé Poussineau <[email protected]>--- hw/dma/rc4030.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)hw/dma/rc4030.c +7
-0@@ -681,6 +681,7 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wrirc4030State *s = opaque;hwaddr dma_addr;int dev_to_mem; +int i; s->dma_regs[n][DMA_REG_ENABLE ] &= ~(DMA_FLAG_TC_INTR |DMA_FLAG_MEM_INTR |DMA_FLAG_ADDR_INTR );@@ -699,8 +700,22 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri dma_addr = s->dma_regs[n][DMA_REG_ADDRESS ];/* Read/write data at right place */ +#if 1 /* workaround for a bug in memory management */ +for (i =0 ; i < len; ) { +int ncpy =DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE -1 )); +if (ncpy > len - i) { + ncpy = len - i; + }
hw/dma/rc4030.c static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri · line 718
Quoted 10 lines
hw/dma/rc4030.c +8
-0+address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , + buf + i, ncpy, is_write); + + dma_addr += ncpy; + i += ncpy; + } +#else address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , buf, len, is_write); +#endif
Hmm, basically your code splits the transfers so that they don't cross DMA page boundaries. It seems that your DMA memory region is actually made of small subregions of size DMA_PAGESIZE aliased to the RAM.
Now looking at the address_space_rw function, it seems it optimizes the write to RAM case by calling address_space_translate() and then doing a memcpy() of the whole region. It doesn't work given the memory region is not linear.
That said address_space_translate is supposed to adjust the length if needed, but does so only if iommu_ops is defined. I therefore wonder if you therefore shouldn't model this DMA translation tables by using IOMMU ops instead of subregions.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Hi Aurelien,
Quoted 42 lines · Aurelien Jarno
Quoted 41 lines · Hervé Poussineau
This workarounds a bug in memory management.
To reproduce the problem, try to start the Windows NT 4.0/MIPS installer. After loading some files, you should see a screen saying
"To set up Windows NT now, press ENTER."
However, you're welcomed with anIRQL_NOT_LESS_OR_EQUALbugcheck or an Unknown Hard Errorc0000221.Signed-off-by Hervé Poussineau <[email protected]>--- hw/dma/rc4030.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)hw/dma/rc4030.c +13
-0@@ -681,6 +681,7 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wrirc4030State *s = opaque;hwaddr dma_addr;int dev_to_mem; +int i; s->dma_regs[n][DMA_REG_ENABLE ] &= ~(DMA_FLAG_TC_INTR |DMA_FLAG_MEM_INTR |DMA_FLAG_ADDR_INTR );@@ -699,8 +700,22 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri dma_addr = s->dma_regs[n][DMA_REG_ADDRESS ];/* Read/write data at right place */ +#if 1 /* workaround for a bug in memory management */ +for (i =0 ; i < len; ) { +int ncpy =DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE -1 )); +if (ncpy > len - i) { + ncpy = len - i; + } +address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , + buf + i, ncpy, is_write); + + dma_addr += ncpy; + i += ncpy; + }
Quoted 8 lines
hw/dma/rc4030.c static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri · line 718
Quoted 4 lines
hw/dma/rc4030.c +2
-0+#else address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , buf, len, is_write); +#endif Hmm, basically your code splits the transfers so that they don't cross DMA page boundaries. It seems that your DMA memory region is actually made of small subregions of size
DMA_PAGESIZEaliased to the RAM.
Yes, that's the case. I have lots of DMA_PAGESIZE memory region aliases in the DMA memory region.
Quoted 7 lines
Now looking at the
address_space_rwfunction, it seems it optimizes the write to RAM case by callingaddress_space_translate()and then doing amemcpy()of the whole region. It doesn't work given the memory region is not linear.That said
address_space_translateis supposed to adjust the length if needed, but does so only ifiommu_opsis defined.
Then, the problem lies here.
If you can use address_space_rw only on an address range which is linear in underlying memory region, or if underlying memory region is a iommu, then you have a big problem. As you can't query if that's the case, your only bet is to use address_space_rw with only 1 byte quantities... Adding Paolo, as he may have an idea.
Quoted 4 lines
I therefore wonder if
you therefore shouldn't model this DMA translation tables by using IOMMU ops instead of subregions.
No, in my opinion, that's an implementation detail. Paolo said that it was OK:
"Both are okay. The IOMMU makes address space changes faster; your scheme is basically a form of caching, it trades update performance for improved translation performance."
http://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05486.html
Regards,
Hervé
Quoted 64 lines · Hervé Poussineau
Hi Aurelien,
Quoted 42 lines · Aurelien Jarno
Quoted 41 lines · Hervé Poussineau
This workarounds a bug in memory management.
To reproduce the problem, try to start the Windows NT 4.0/MIPS installer. After loading some files, you should see a screen saying
"To set up Windows NT now, press ENTER."
However, you're welcomed with anIRQL_NOT_LESS_OR_EQUALbugcheck or an Unknown Hard Errorc0000221.Signed-off-by Hervé Poussineau <[email protected]>--- hw/dma/rc4030.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)hw/dma/rc4030.c +13
-0@@ -681,6 +681,7 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wrirc4030State *s = opaque;hwaddr dma_addr;int dev_to_mem; +int i; s->dma_regs[n][DMA_REG_ENABLE ] &= ~(DMA_FLAG_TC_INTR |DMA_FLAG_MEM_INTR |DMA_FLAG_ADDR_INTR );@@ -699,8 +700,22 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri dma_addr = s->dma_regs[n][DMA_REG_ADDRESS ];/* Read/write data at right place */ +#if 1 /* workaround for a bug in memory management */ +for (i =0 ; i < len; ) { +int ncpy =DMA_PAGESIZE - (dma_addr & (DMA_PAGESIZE -1 )); +if (ncpy > len - i) { + ncpy = len - i; + } +address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , + buf + i, ncpy, is_write); + + dma_addr += ncpy; + i += ncpy; + }Quoted 8 lines
hw/dma/rc4030.c static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri · line 718
Quoted 4 lines
hw/dma/rc4030.c +2
-0+#else address_space_rw (&s->dma_as, dma_addr,MEMTXATTRS_UNSPECIFIED , buf, len, is_write); +#endif Hmm, basically your code splits the transfers so that they don't cross DMA page boundaries. It seems that your DMA memory region is actually made of small subregions of size
DMA_PAGESIZEaliased to the RAM.Yes, that's the case. I have lots of
DMA_PAGESIZEmemory region aliases in the DMA memory region.Quoted 7 lines
Now looking at the
address_space_rwfunction, it seems it optimizes the write to RAM case by callingaddress_space_translate()and then doing amemcpy()of the whole region. It doesn't work given the memory region is not linear.That said
address_space_translateis supposed to adjust the length if needed, but does so only ifiommu_opsis defined.
Quoted 7 lines
Then, the problem lies here.
If you can useaddress_space_rwonly on an address range which is linear in underlying memory region, or if underlying memory region is a iommu, then you have a big problem. As you can't query if that's the case, your only bet is to useaddress_space_rwwith only 1 byte quantities...
Adding Paolo, as he may have an idea.
The code assumes that if you don't have an IOMMU, the address range in the underlying memory region is linear. One fix would be to adjust the length even without IOMMU. That would have some performance impact though, so maybe we want to make this assumption clear and always use an IOMMU in that case.
Quoted 9 lines
Quoted 4 lines
I therefore wonder if
you therefore shouldn't model this DMA translation tables by using IOMMU ops instead of subregions.No, in my opinion, that's an implementation detail. Paolo said that it was OK:
"Both are okay. The IOMMU makes address space changes faster; your scheme is basically a form of caching, it trades update performance for improved translation performance."
http://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05486.html
It seems wrong with the current code. And if we fix the bug by adjusting the length, the above sentence about the performances might becomes wrong
Aurelien.
Signature · 2 lines
Aurelien Jarno GPG: 4096R/1DDD8C9B [email protected] http://www.aurel32.net
Quoted 2 lines · Aurelien Jarno
The code assumes that if you don't have an IOMMU, the address range in the underlying memory region is linear.
I think this is exactly what Peter Crosthwaite's infamous :) "exec:
Respect as_translate_internal length clamp" patch was trying to fix. However, address_space_translate_internal uses section->mr->size instead of section->size. I'll post a patch once I'm through the email deluge from 1 week of absence.
If I read correctly the patch that introduced address_space_translate, the bug has always been there.
Quoted 4 lines
One fix would be to adjust the
length even without IOMMU. That would have some performance impact though, so maybe we want to make this assumption clear and always use an IOMMU in that case.
I don't think there would be a performance impact, except in buggy cases such as the one Hervé is fixing.
Paolo
Quoted 16 lines
Quoted 9 lines
Quoted 4 lines
I therefore wonder if
you therefore shouldn't model this DMA translation tables by using IOMMU ops instead of subregions.No, in my opinion, that's an implementation detail. Paolo said that it was OK:
"Both are okay. The IOMMU makes address space changes faster; your scheme is basically a form of caching, it trades update performance for improved translation performance."
http://lists.gnu.org/archive/html/qemu-devel/2015-03/msg05486.htmlIt seems wrong with the current code. And if we fix the bug by adjusting the length, the above sentence about the performances might becomes wrong
Aurelien.
Quoted 9 lines · Paolo Bonzini
Quoted 2 lines · Aurelien Jarno
The code assumes that if you don't have an IOMMU, the address range in the underlying memory region is linear.
I think this is exactly what Peter Crosthwaite's infamous :) "exec:
Respectas_translate_internallength clamp" patch was trying to fix. However,address_space_translate_internalusessection->mr->sizeinstead ofsection->size. I'll post a patch once I'm through the email deluge from 1 week of absence.
Can you test this?
exec.c +0 -0
@@ -350,7 +350,7 @@
address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
/* Compute offset within MemoryRegion */
*xlat = addr + section->offset_within_region;- diff = int128_sub(section->mr->size, int128_make64(addr));
+ diff = int128_sub(section->size, int128_make64(addr));
*plen = int128_get64(int128_min(diff, int128_make64(*plen)));
return section;}
Paolo
Quoted 7 lines · Paolo Bonzini
Quoted 4 lines · Paolo Bonzini
Quoted 2 lines · Aurelien Jarno
The code assumes that if you don't have an IOMMU, the address range in the underlying memory region is linear.
Quoted 7 lines
Quoted 5 lines
I think this is exactly what Peter Crosthwaite's infamous :) "exec:
Respectas_translate_internallength clamp" patch was trying to fix. However,address_space_translate_internalusessection->mr->sizeinstead ofsection->size. I'll post a patch once I'm through the email deluge from 1 week of absence.Can you test this?
Sure. It works well for my test case. Thanks Paolo!
However, it breaks PC machines.
mtree gives:
0000000000000cf8-0000000000000cfb (prio 0, RW): pci-conf-idx
0000000000000cf9-0000000000000cf9 (prio 1, RW): piix3-reset-control"make check" wants to write 4 bytes to 0xcf8. Your patch makes it write only 1 byte, and bad things happen.
Quoted 19 lines
exec.c +0
-0@@ -350,7 +350,7 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr,hwaddr *x
/* Compute offset within MemoryRegion */ *xlat = addr + section->offset_within_region;exec.c +1
-1- diff =int128_sub (section->mr->size,int128_make64 (addr)); + diff =int128_sub (section->size,int128_make64 (addr)); *plen =int128_get64 (int128_min (diff,int128_make64 (*plen)));return section; }Paolo
Quoted 10 lines · Hervé Poussineau
Quoted 1 line
Sure. It works well for my test case. Thanks Paolo!
However, it breaks PC machines.
mtree gives:0000000000000cf8-0000000000000cfb (prio 0, RW): pci-conf-idx 0000000000000cf9-0000000000000cf9 (prio 1, RW): piix3-reset-control"make check" wants to write 4 bytes to
0xcf8. Your patch makes it write only 1 byte, and bad things happen.
Yup, that's the same as the Windows XP breakage. Thanks for the test!
Paolo