]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/arm/mem_op.cpp
update
[l4.git] / kernel / fiasco / src / kern / arm / mem_op.cpp
1 INTERFACE [arm]:
2
3 #include "types.h"
4
5 class Mem_op
6 {
7 public:
8   enum Op_cache
9   {
10     Op_cache_clean_data        = 0x00,
11     Op_cache_flush_data        = 0x01,
12     Op_cache_inv_data          = 0x02,
13     Op_cache_coherent          = 0x03,
14     Op_cache_dma_coherent      = 0x04,
15     Op_cache_dma_coherent_full = 0x05,
16     Op_cache_l2_clean          = 0x06,
17     Op_cache_l2_flush          = 0x07,
18     Op_cache_l2_inv            = 0x08,
19   };
20
21   enum Op_mem
22   {
23     Op_mem_read_data     = 0x10,
24     Op_mem_write_data    = 0x11,
25   };
26 };
27
28 // ------------------------------------------------------------------------
29 IMPLEMENTATION [arm]:
30
31 #include "context.h"
32 #include "entry_frame.h"
33 #include "globals.h"
34 #include "mem.h"
35 #include "mem_space.h"
36 #include "mem_unit.h"
37 #include "outer_cache.h"
38 #include "space.h"
39 #include "warn.h"
40
41 PRIVATE static void
42 Mem_op::l1_inv_dcache(Address start, Address end)
43 {
44   if (start & Mem_unit::Cache_line_mask)
45     {
46       Mem_unit::flush_dcache((void *)start, (void *)start);
47       start += Mem_unit::Cache_line_size;
48       start &= ~Mem_unit::Cache_line_mask;
49     }
50   if (end & Mem_unit::Cache_line_mask)
51     {
52       Mem_unit::flush_dcache((void *)end, (void *)end);
53       end &= ~Mem_unit::Cache_line_mask;
54     }
55
56   Mem_unit::inv_dcache((void *)start, (void *)end);
57 }
58
59 PRIVATE static void
60 Mem_op::inv_icache(Address start, Address end)
61 {
62   if (Address(end) - Address(start) > 0x2000)
63     asm volatile("mcr p15, 0, r0, c7, c5, 0");
64   else
65     {
66       for (start &= ~Mem_unit::Icache_line_mask;
67            start < end; start += Mem_unit::Icache_line_size)
68         asm volatile("mcr p15, 0, %0, c7, c5, 1" : : "r" (start));
69     }
70 }
71
72 PUBLIC static void
73 Mem_op::arm_mem_cache_maint(int op, void const *start, void const *end)
74 {
75   Context *c = current();
76
77   if (EXPECT_FALSE(start > end))
78     return;
79
80   c->set_ignore_mem_op_in_progress(true);
81
82   switch (op)
83     {
84     case Op_cache_clean_data:
85       Mem_unit::clean_dcache(start, end);
86       break;
87
88     case Op_cache_flush_data:
89       Mem_unit::flush_dcache(start, end);
90       break;
91
92     case Op_cache_inv_data:
93       l1_inv_dcache((Address)start, (Address)end);
94       break;
95
96     case Op_cache_coherent:
97       Mem_unit::clean_dcache(start, end);
98       Mem::dsb();
99       Mem_unit::btc_inv();
100       inv_icache(Address(start), Address(end));
101       Mem::dsb();
102       break;
103
104     case Op_cache_l2_clean:
105     case Op_cache_l2_flush:
106     case Op_cache_l2_inv:
107       outer_cache_op(op, Address(start), Address(end));
108       break;
109
110     case Op_cache_dma_coherent:
111         {
112           Mem_unit::flush_dcache(Virt_addr(Address(start)), Virt_addr(Address(end)));
113           outer_cache_op(Op_cache_l2_flush, Address(start), Address(end));
114         }
115       break;
116
117     // We might not want to implement this one but single address outer
118     // cache flushing can be really slow
119     case Op_cache_dma_coherent_full:
120       Mem_unit::flush_dcache();
121       Outer_cache::flush();
122       break;
123
124     default:
125       break;
126     };
127
128   c->set_ignore_mem_op_in_progress(false);
129 }
130
131 PUBLIC static void
132 Mem_op::arm_mem_access(Mword *r)
133 {
134   Address  a = r[1];
135   unsigned w = r[2];
136
137   if (w > 2)
138     return;
139
140   if (!current()->space()->is_user_memory(a, 1 << w))
141     return;
142
143   jmp_buf pf_recovery;
144   int e;
145
146   if ((e = setjmp(pf_recovery)) == 0)
147     {
148       current()->recover_jmp_buf(&pf_recovery);
149
150       switch (r[0])
151         {
152         case Op_mem_read_data:
153           switch (w)
154             {
155             case 0:
156               r[3] = *(unsigned char *)a;
157               break;
158             case 1:
159               r[3] = *(unsigned short *)a;
160               break;
161             case 2:
162               r[3] = *(unsigned int *)a;
163               break;
164             default:
165               break;
166             };
167           break;
168
169         case Op_mem_write_data:
170           switch (w)
171             {
172             case 0:
173               *(unsigned char *)a = r[3];
174               break;
175             case 1:
176               *(unsigned short *)a = r[3];
177               break;
178             case 2:
179               *(unsigned int *)a = r[3];
180               break;
181             default:
182               break;
183             };
184           break;
185
186         default:
187           break;
188         };
189     }
190   else
191     WARN("Unresolved memory access, skipping\n");
192
193   current()->recover_jmp_buf(0);
194 }
195
196 extern "C" void sys_arm_mem_op()
197 {
198   Entry_frame *e = current()->regs();
199   if (EXPECT_FALSE(e->r[0] & 0x10))
200     Mem_op::arm_mem_access(e->r);
201   else
202     Mem_op::arm_mem_cache_maint(e->r[0], (void *)e->r[1], (void *)e->r[2]);
203 }
204
205 // ------------------------------------------------------------------------
206 IMPLEMENTATION [arm && !outer_cache]:
207
208 PRIVATE static inline
209 void
210 Mem_op::outer_cache_op(int, Address, Address)
211 {}
212
213 // ------------------------------------------------------------------------
214 IMPLEMENTATION [arm && outer_cache]:
215
216 PRIVATE static
217 void
218 Mem_op::outer_cache_op(int op, Address start, Address end)
219 {
220   Mem_space::Vaddr v = Virt_addr(Address(start));
221   Mem_space::Vaddr e = Virt_addr(Address(end));
222
223   Context *c = current();
224
225   while (v < e)
226     {
227       Mem_space::Size phys_size;
228       Mem_space::Phys_addr phys_addr;
229       unsigned attrs;
230
231       if (   c->mem_space()->v_lookup(v, &phys_addr, &phys_size, &attrs)
232           && (attrs & Mem_space::Page_user_accessible))
233         {
234           unsigned long sz = Virt_size(phys_size).value();
235           if (Address(end) - Address(start) < sz)
236             sz = Address(end) - Address(start);
237           switch (op)
238             {
239             case Op_cache_l2_clean:
240               Outer_cache::clean(Virt_addr(phys_addr).value(),
241                                  Virt_addr(phys_addr).value() + sz, false);
242               break;
243             case Op_cache_l2_flush:
244               Outer_cache::flush(Virt_addr(phys_addr).value(),
245                                  Virt_addr(phys_addr).value() + sz, false);
246               break;
247             case Op_cache_l2_inv:
248               Outer_cache::invalidate(Virt_addr(phys_addr).value(),
249                                       Virt_addr(phys_addr).value() + sz, false);
250               break;
251             }
252         }
253       v += phys_size;
254     }
255   Outer_cache::sync();
256 }