aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorStefano Stabellini <stefano.stabellini@eu.citrix.com>2013-10-17 12:22:27 -0400
committerStefano Stabellini <stefano.stabellini@eu.citrix.com>2013-10-17 12:22:27 -0400
commit4a19138c6505e224d9f4cc2fe9ada9188d7100ea (patch)
treecff1b72f69858d6bdfb0486e082de13e8b93f9dc /arch/arm
parent25b719d7b45947a79d298414cdfb5ec8fadf0ec8 (diff)
arm/xen,arm64/xen: introduce p2m
Introduce physical to machine and machine to physical tracking mechanisms based on rbtrees for arm/xen and arm64/xen. We need it because any guests on ARM are an autotranslate guests, therefore a physical address is potentially different from a machine address. When programming a device to do DMA, we need to be extra-careful to use machine addresses rather than physical addresses to program the device. Therefore we need to know the physical to machine mappings. For the moment we assume that dom0 starts with a 1:1 physical to machine mapping, in other words physical addresses correspond to machine addresses. However when mapping a foreign grant reference, obviously the 1:1 model doesn't work anymore. So at the very least we need to be able to track grant mappings. We need locking to protect accesses to the two trees. Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Changes in v8: - move pfn_to_mfn and mfn_to_pfn to page.h as static inline functions; - no need to walk the tree if phys_to_mach.rb_node is NULL; - correctly handle multipage p2m entries; - substitute the spin_lock with a rwlock.
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/include/asm/xen/page.h49
-rw-r--r--arch/arm/xen/Makefile2
-rw-r--r--arch/arm/xen/p2m.c208
3 files changed, 251 insertions, 8 deletions
diff --git a/arch/arm/include/asm/xen/page.h b/arch/arm/include/asm/xen/page.h
index 359a7b50b158..d1b5dd566472 100644
--- a/arch/arm/include/asm/xen/page.h
+++ b/arch/arm/include/asm/xen/page.h
@@ -7,11 +7,10 @@
7#include <linux/pfn.h> 7#include <linux/pfn.h>
8#include <linux/types.h> 8#include <linux/types.h>
9 9
10#include <xen/xen.h>
10#include <xen/interface/grant_table.h> 11#include <xen/interface/grant_table.h>
11 12
12#define pfn_to_mfn(pfn) (pfn)
13#define phys_to_machine_mapping_valid(pfn) (1) 13#define phys_to_machine_mapping_valid(pfn) (1)
14#define mfn_to_pfn(mfn) (mfn)
15#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT)) 14#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT))
16 15
17#define pte_mfn pte_pfn 16#define pte_mfn pte_pfn
@@ -32,6 +31,44 @@ typedef struct xpaddr {
32 31
33#define INVALID_P2M_ENTRY (~0UL) 32#define INVALID_P2M_ENTRY (~0UL)
34 33
34unsigned long __pfn_to_mfn(unsigned long pfn);
35unsigned long __mfn_to_pfn(unsigned long mfn);
36extern struct rb_root phys_to_mach;
37
38static inline unsigned long pfn_to_mfn(unsigned long pfn)
39{
40 unsigned long mfn;
41
42 if (phys_to_mach.rb_node != NULL) {
43 mfn = __pfn_to_mfn(pfn);
44 if (mfn != INVALID_P2M_ENTRY)
45 return mfn;
46 }
47
48 if (xen_initial_domain())
49 return pfn;
50 else
51 return INVALID_P2M_ENTRY;
52}
53
54static inline unsigned long mfn_to_pfn(unsigned long mfn)
55{
56 unsigned long pfn;
57
58 if (phys_to_mach.rb_node != NULL) {
59 pfn = __mfn_to_pfn(mfn);
60 if (pfn != INVALID_P2M_ENTRY)
61 return pfn;
62 }
63
64 if (xen_initial_domain())
65 return mfn;
66 else
67 return INVALID_P2M_ENTRY;
68}
69
70#define mfn_to_local_pfn(mfn) mfn_to_pfn(mfn)
71
35static inline xmaddr_t phys_to_machine(xpaddr_t phys) 72static inline xmaddr_t phys_to_machine(xpaddr_t phys)
36{ 73{
37 unsigned offset = phys.paddr & ~PAGE_MASK; 74 unsigned offset = phys.paddr & ~PAGE_MASK;
@@ -76,11 +113,9 @@ static inline int m2p_remove_override(struct page *page, bool clear_pte)
76 return 0; 113 return 0;
77} 114}
78 115
79static inline bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn) 116bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn);
80{ 117bool __set_phys_to_machine_multi(unsigned long pfn, unsigned long mfn,
81 BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); 118 unsigned long nr_pages);
82 return true;
83}
84 119
85static inline bool set_phys_to_machine(unsigned long pfn, unsigned long mfn) 120static inline bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
86{ 121{
diff --git a/arch/arm/xen/Makefile b/arch/arm/xen/Makefile
index 43841033afd3..21e6ff503178 100644
--- a/arch/arm/xen/Makefile
+++ b/arch/arm/xen/Makefile
@@ -1 +1 @@
obj-y := enlighten.o hypercall.o grant-table.o obj-y := enlighten.o hypercall.o grant-table.o p2m.o
diff --git a/arch/arm/xen/p2m.c b/arch/arm/xen/p2m.c
new file mode 100644
index 000000000000..5df4a9afb8c6
--- /dev/null
+++ b/arch/arm/xen/p2m.c
@@ -0,0 +1,208 @@
1#include <linux/bootmem.h>
2#include <linux/gfp.h>
3#include <linux/export.h>
4#include <linux/rwlock.h>
5#include <linux/slab.h>
6#include <linux/types.h>
7#include <linux/dma-mapping.h>
8#include <linux/vmalloc.h>
9#include <linux/swiotlb.h>
10
11#include <xen/xen.h>
12#include <xen/interface/memory.h>
13#include <xen/swiotlb-xen.h>
14
15#include <asm/cacheflush.h>
16#include <asm/xen/page.h>
17#include <asm/xen/hypercall.h>
18#include <asm/xen/interface.h>
19
20struct xen_p2m_entry {
21 unsigned long pfn;
22 unsigned long mfn;
23 unsigned long nr_pages;
24 struct rb_node rbnode_mach;
25 struct rb_node rbnode_phys;
26};
27
28rwlock_t p2m_lock;
29struct rb_root phys_to_mach = RB_ROOT;
30static struct rb_root mach_to_phys = RB_ROOT;
31
32static int xen_add_phys_to_mach_entry(struct xen_p2m_entry *new)
33{
34 struct rb_node **link = &phys_to_mach.rb_node;
35 struct rb_node *parent = NULL;
36 struct xen_p2m_entry *entry;
37 int rc = 0;
38
39 while (*link) {
40 parent = *link;
41 entry = rb_entry(parent, struct xen_p2m_entry, rbnode_phys);
42
43 if (new->mfn == entry->mfn)
44 goto err_out;
45 if (new->pfn == entry->pfn)
46 goto err_out;
47
48 if (new->pfn < entry->pfn)
49 link = &(*link)->rb_left;
50 else
51 link = &(*link)->rb_right;
52 }
53 rb_link_node(&new->rbnode_phys, parent, link);
54 rb_insert_color(&new->rbnode_phys, &phys_to_mach);
55 goto out;
56
57err_out:
58 rc = -EINVAL;
59 pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
60 __func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
61out:
62 return rc;
63}
64
65unsigned long __pfn_to_mfn(unsigned long pfn)
66{
67 struct rb_node *n = phys_to_mach.rb_node;
68 struct xen_p2m_entry *entry;
69 unsigned long irqflags;
70
71 read_lock_irqsave(&p2m_lock, irqflags);
72 while (n) {
73 entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
74 if (entry->pfn <= pfn &&
75 entry->pfn + entry->nr_pages > pfn) {
76 read_unlock_irqrestore(&p2m_lock, irqflags);
77 return entry->mfn + (pfn - entry->pfn);
78 }
79 if (pfn < entry->pfn)
80 n = n->rb_left;
81 else
82 n = n->rb_right;
83 }
84 read_unlock_irqrestore(&p2m_lock, irqflags);
85
86 return INVALID_P2M_ENTRY;
87}
88EXPORT_SYMBOL_GPL(__pfn_to_mfn);
89
90static int xen_add_mach_to_phys_entry(struct xen_p2m_entry *new)
91{
92 struct rb_node **link = &mach_to_phys.rb_node;
93 struct rb_node *parent = NULL;
94 struct xen_p2m_entry *entry;
95 int rc = 0;
96
97 while (*link) {
98 parent = *link;
99 entry = rb_entry(parent, struct xen_p2m_entry, rbnode_mach);
100
101 if (new->mfn == entry->mfn)
102 goto err_out;
103 if (new->pfn == entry->pfn)
104 goto err_out;
105
106 if (new->mfn < entry->mfn)
107 link = &(*link)->rb_left;
108 else
109 link = &(*link)->rb_right;
110 }
111 rb_link_node(&new->rbnode_mach, parent, link);
112 rb_insert_color(&new->rbnode_mach, &mach_to_phys);
113 goto out;
114
115err_out:
116 rc = -EINVAL;
117 pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
118 __func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
119out:
120 return rc;
121}
122
123unsigned long __mfn_to_pfn(unsigned long mfn)
124{
125 struct rb_node *n = mach_to_phys.rb_node;
126 struct xen_p2m_entry *entry;
127 unsigned long irqflags;
128
129 read_lock_irqsave(&p2m_lock, irqflags);
130 while (n) {
131 entry = rb_entry(n, struct xen_p2m_entry, rbnode_mach);
132 if (entry->mfn <= mfn &&
133 entry->mfn + entry->nr_pages > mfn) {
134 read_unlock_irqrestore(&p2m_lock, irqflags);
135 return entry->pfn + (mfn - entry->mfn);
136 }
137 if (mfn < entry->mfn)
138 n = n->rb_left;
139 else
140 n = n->rb_right;
141 }
142 read_unlock_irqrestore(&p2m_lock, irqflags);
143
144 return INVALID_P2M_ENTRY;
145}
146EXPORT_SYMBOL_GPL(__mfn_to_pfn);
147
148bool __set_phys_to_machine_multi(unsigned long pfn,
149 unsigned long mfn, unsigned long nr_pages)
150{
151 int rc;
152 unsigned long irqflags;
153 struct xen_p2m_entry *p2m_entry;
154 struct rb_node *n = phys_to_mach.rb_node;
155
156 if (mfn == INVALID_P2M_ENTRY) {
157 write_lock_irqsave(&p2m_lock, irqflags);
158 while (n) {
159 p2m_entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
160 if (p2m_entry->pfn <= pfn &&
161 p2m_entry->pfn + p2m_entry->nr_pages > pfn) {
162 rb_erase(&p2m_entry->rbnode_mach, &mach_to_phys);
163 rb_erase(&p2m_entry->rbnode_phys, &phys_to_mach);
164 write_unlock_irqrestore(&p2m_lock, irqflags);
165 kfree(p2m_entry);
166 return true;
167 }
168 if (pfn < p2m_entry->pfn)
169 n = n->rb_left;
170 else
171 n = n->rb_right;
172 }
173 write_unlock_irqrestore(&p2m_lock, irqflags);
174 return true;
175 }
176
177 p2m_entry = kzalloc(sizeof(struct xen_p2m_entry), GFP_NOWAIT);
178 if (!p2m_entry) {
179 pr_warn("cannot allocate xen_p2m_entry\n");
180 return false;
181 }
182 p2m_entry->pfn = pfn;
183 p2m_entry->nr_pages = nr_pages;
184 p2m_entry->mfn = mfn;
185
186 write_lock_irqsave(&p2m_lock, irqflags);
187 if ((rc = xen_add_phys_to_mach_entry(p2m_entry) < 0) ||
188 (rc = xen_add_mach_to_phys_entry(p2m_entry) < 0)) {
189 write_unlock_irqrestore(&p2m_lock, irqflags);
190 return false;
191 }
192 write_unlock_irqrestore(&p2m_lock, irqflags);
193 return true;
194}
195EXPORT_SYMBOL_GPL(__set_phys_to_machine_multi);
196
197bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
198{
199 return __set_phys_to_machine_multi(pfn, mfn, 1);
200}
201EXPORT_SYMBOL_GPL(__set_phys_to_machine);
202
203int p2m_init(void)
204{
205 rwlock_init(&p2m_lock);
206 return 0;
207}
208arch_initcall(p2m_init);