Skip to content

Commit 566fb90

Browse files
author
Christoph Hellwig
committed
swiotlb-xen: fix DMA_ATTR_NO_KERNEL_MAPPING on arm
swiotlb-xen uses very different ways to allocate coherent memory on x86 vs arm. On the former it allocates memory from the page allocator, while on the later it reuses the dma-direct allocator the handles the complexities of non-coherent DMA on arm platforms. Unfortunately the complexities of trying to deal with the two cases in the swiotlb-xen.c code lead to a bug in the handling of DMA_ATTR_NO_KERNEL_MAPPING on arm. With the DMA_ATTR_NO_KERNEL_MAPPING flag the coherent memory allocator does not actually allocate coherent memory, but just a DMA handle for some memory that is DMA addressable by the device, but which does not have to have a kernel mapping. Thus dereferencing the return value will lead to kernel crashed and memory corruption. Fix this by using the dma-direct allocator directly for arm, which works perfectly fine because on arm swiotlb-xen is only used when the domain is 1:1 mapped, and then simplifying the remaining code to only cater for the x86 case with DMA coherent device. Reported-by: Rahul Singh <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]> Reviewed-by: Rahul Singh <[email protected]> Reviewed-by: Stefano Stabellini <[email protected]> Tested-by: Rahul Singh <[email protected]>
1 parent 3cb4503 commit 566fb90

File tree

10 files changed

+41
-140
lines changed

10 files changed

+41
-140
lines changed

arch/arm/include/asm/xen/page-coherent.h

Lines changed: 0 additions & 2 deletions
This file was deleted.

arch/arm/xen/mm.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,6 @@ bool xen_arch_need_swiotlb(struct device *dev,
116116
!dev_is_dma_coherent(dev));
117117
}
118118

119-
int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
120-
unsigned int address_bits,
121-
dma_addr_t *dma_handle)
122-
{
123-
/* the domain is 1:1 mapped to use swiotlb-xen */
124-
*dma_handle = pstart;
125-
return 0;
126-
}
127-
128-
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
129-
{
130-
return;
131-
}
132-
133119
static int __init xen_mm_init(void)
134120
{
135121
struct gnttab_cache_flush cflush;

arch/arm64/include/asm/xen/page-coherent.h

Lines changed: 0 additions & 2 deletions
This file was deleted.

arch/x86/include/asm/xen/page-coherent.h

Lines changed: 0 additions & 24 deletions
This file was deleted.

arch/x86/include/asm/xen/swiotlb-xen.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ extern int pci_xen_swiotlb_init_late(void);
88
static inline int pci_xen_swiotlb_init_late(void) { return -ENXIO; }
99
#endif
1010

11+
int xen_swiotlb_fixup(void *buf, unsigned long nslabs);
12+
int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
13+
unsigned int address_bits,
14+
dma_addr_t *dma_handle);
15+
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order);
16+
1117
#endif /* _ASM_X86_SWIOTLB_XEN_H */

arch/x86/xen/mmu_pv.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include <xen/interface/version.h>
8181
#include <xen/interface/memory.h>
8282
#include <xen/hvc-console.h>
83+
#include <xen/swiotlb-xen.h>
8384

8485
#include "multicalls.h"
8586
#include "mmu.h"

drivers/xen/swiotlb-xen.c

Lines changed: 34 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
#include <xen/hvc-console.h>
3737

3838
#include <asm/dma-mapping.h>
39-
#include <asm/xen/page-coherent.h>
4039

4140
#include <trace/events/swiotlb.h>
4241
#define MAX_DMA_BITS 32
@@ -104,6 +103,7 @@ static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr)
104103
return 0;
105104
}
106105

106+
#ifdef CONFIG_X86
107107
int xen_swiotlb_fixup(void *buf, unsigned long nslabs)
108108
{
109109
int rc;
@@ -131,94 +131,58 @@ int xen_swiotlb_fixup(void *buf, unsigned long nslabs)
131131
}
132132

133133
static void *
134-
xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
135-
dma_addr_t *dma_handle, gfp_t flags,
136-
unsigned long attrs)
134+
xen_swiotlb_alloc_coherent(struct device *dev, size_t size,
135+
dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs)
137136
{
138-
void *ret;
137+
u64 dma_mask = dev->coherent_dma_mask;
139138
int order = get_order(size);
140-
u64 dma_mask = DMA_BIT_MASK(32);
141139
phys_addr_t phys;
142-
dma_addr_t dev_addr;
143-
144-
/*
145-
* Ignore region specifiers - the kernel's ideas of
146-
* pseudo-phys memory layout has nothing to do with the
147-
* machine physical layout. We can't allocate highmem
148-
* because we can't return a pointer to it.
149-
*/
150-
flags &= ~(__GFP_DMA | __GFP_HIGHMEM);
140+
void *ret;
151141

152-
/* Convert the size to actually allocated. */
142+
/* Align the allocation to the Xen page size */
153143
size = 1UL << (order + XEN_PAGE_SHIFT);
154144

155-
/* On ARM this function returns an ioremap'ped virtual address for
156-
* which virt_to_phys doesn't return the corresponding physical
157-
* address. In fact on ARM virt_to_phys only works for kernel direct
158-
* mapped RAM memory. Also see comment below.
159-
*/
160-
ret = xen_alloc_coherent_pages(hwdev, size, dma_handle, flags, attrs);
161-
145+
ret = (void *)__get_free_pages(flags, get_order(size));
162146
if (!ret)
163147
return ret;
164-
165-
if (hwdev && hwdev->coherent_dma_mask)
166-
dma_mask = hwdev->coherent_dma_mask;
167-
168-
/* At this point dma_handle is the dma address, next we are
169-
* going to set it to the machine address.
170-
* Do not use virt_to_phys(ret) because on ARM it doesn't correspond
171-
* to *dma_handle. */
172-
phys = dma_to_phys(hwdev, *dma_handle);
173-
dev_addr = xen_phys_to_dma(hwdev, phys);
174-
if (((dev_addr + size - 1 <= dma_mask)) &&
175-
!range_straddles_page_boundary(phys, size))
176-
*dma_handle = dev_addr;
177-
else {
178-
if (xen_create_contiguous_region(phys, order,
179-
fls64(dma_mask), dma_handle) != 0) {
180-
xen_free_coherent_pages(hwdev, size, ret, (dma_addr_t)phys, attrs);
181-
return NULL;
182-
}
183-
*dma_handle = phys_to_dma(hwdev, *dma_handle);
148+
phys = virt_to_phys(ret);
149+
150+
*dma_handle = xen_phys_to_dma(dev, phys);
151+
if (*dma_handle + size - 1 > dma_mask ||
152+
range_straddles_page_boundary(phys, size)) {
153+
if (xen_create_contiguous_region(phys, order, fls64(dma_mask),
154+
dma_handle) != 0)
155+
goto out_free_pages;
184156
SetPageXenRemapped(virt_to_page(ret));
185157
}
158+
186159
memset(ret, 0, size);
187160
return ret;
161+
162+
out_free_pages:
163+
free_pages((unsigned long)ret, get_order(size));
164+
return NULL;
188165
}
189166

190167
static void
191-
xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
192-
dma_addr_t dev_addr, unsigned long attrs)
168+
xen_swiotlb_free_coherent(struct device *dev, size_t size, void *vaddr,
169+
dma_addr_t dma_handle, unsigned long attrs)
193170
{
171+
phys_addr_t phys = virt_to_phys(vaddr);
194172
int order = get_order(size);
195-
phys_addr_t phys;
196-
u64 dma_mask = DMA_BIT_MASK(32);
197-
struct page *page;
198-
199-
if (hwdev && hwdev->coherent_dma_mask)
200-
dma_mask = hwdev->coherent_dma_mask;
201-
202-
/* do not use virt_to_phys because on ARM it doesn't return you the
203-
* physical address */
204-
phys = xen_dma_to_phys(hwdev, dev_addr);
205173

206174
/* Convert the size to actually allocated. */
207175
size = 1UL << (order + XEN_PAGE_SHIFT);
208176

209-
if (is_vmalloc_addr(vaddr))
210-
page = vmalloc_to_page(vaddr);
211-
else
212-
page = virt_to_page(vaddr);
177+
if (WARN_ON_ONCE(dma_handle + size - 1 > dev->coherent_dma_mask) ||
178+
WARN_ON_ONCE(range_straddles_page_boundary(phys, size)))
179+
return;
213180

214-
if (!WARN_ON((dev_addr + size - 1 > dma_mask) ||
215-
range_straddles_page_boundary(phys, size)) &&
216-
TestClearPageXenRemapped(page))
181+
if (TestClearPageXenRemapped(virt_to_page(vaddr)))
217182
xen_destroy_contiguous_region(phys, order);
218-
219-
xen_free_coherent_pages(hwdev, size, vaddr, phys_to_dma(hwdev, phys),
220-
attrs);
183+
free_pages((unsigned long)vaddr, get_order(size));
221184
}
185+
#endif /* CONFIG_X86 */
222186

223187
/*
224188
* Map a single buffer of the indicated size for DMA in streaming mode. The
@@ -421,8 +385,13 @@ xen_swiotlb_dma_supported(struct device *hwdev, u64 mask)
421385
}
422386

423387
const struct dma_map_ops xen_swiotlb_dma_ops = {
388+
#ifdef CONFIG_X86
424389
.alloc = xen_swiotlb_alloc_coherent,
425390
.free = xen_swiotlb_free_coherent,
391+
#else
392+
.alloc = dma_direct_alloc,
393+
.free = dma_direct_free,
394+
#endif
426395
.sync_single_for_cpu = xen_swiotlb_sync_single_for_cpu,
427396
.sync_single_for_device = xen_swiotlb_sync_single_for_device,
428397
.sync_sg_for_cpu = xen_swiotlb_sync_sg_for_cpu,

include/xen/arm/page-coherent.h

Lines changed: 0 additions & 20 deletions
This file was deleted.

include/xen/swiotlb-xen.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ void xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle,
1010
void xen_dma_sync_for_device(struct device *dev, dma_addr_t handle,
1111
size_t size, enum dma_data_direction dir);
1212

13-
#ifdef CONFIG_SWIOTLB_XEN
14-
int xen_swiotlb_fixup(void *buf, unsigned long nslabs);
15-
#else
16-
#define xen_swiotlb_fixup NULL
17-
#endif
18-
1913
extern const struct dma_map_ops xen_swiotlb_dma_ops;
2014

2115
#endif /* __LINUX_SWIOTLB_XEN_H */

include/xen/xen-ops.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ int xen_setup_shutdown_event(void);
4242

4343
extern unsigned long *xen_contiguous_bitmap;
4444

45-
#if defined(CONFIG_XEN_PV) || defined(CONFIG_ARM) || defined(CONFIG_ARM64)
46-
int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
47-
unsigned int address_bits,
48-
dma_addr_t *dma_handle);
49-
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order);
50-
#endif
51-
5245
#if defined(CONFIG_XEN_PV)
5346
int xen_remap_pfn(struct vm_area_struct *vma, unsigned long addr,
5447
xen_pfn_t *pfn, int nr, int *err_ptr, pgprot_t prot,

0 commit comments

Comments
 (0)