]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/5e/arm.c
cwfs: fix listen filedescriptor leaks
[plan9front.git] / sys / src / cmd / 5e / arm.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include <mach.h>
6 #include "dat.h"
7 #include "fns.h"
8
9 enum {
10         fI = 1<<25,
11         fP = 1<<24,
12         fLi = 1<<24,
13         fU = 1<<23,
14         fB = 1<<22,
15         fW = 1<<21,
16         fL = 1<<20,
17         fS = 1<<20,
18         fSg = 1<<6,
19         fH = 1<<5,
20 };
21
22 void
23 invalid(u32int instr)
24 {
25         suicide("undefined instruction %8ux @ %8ux", instr, P->R[15] - 4);
26 }
27
28 u32int
29 evenaddr(u32int addr, u32int mask)
30 {
31         if((addr & mask) == 0)
32                 return addr;
33         suicide("unaligned access %8ux @ %8ux", addr, P->R[15] - 4);
34         return addr & ~mask;
35 }
36
37 static u32int
38 doshift(u32int instr, u8int *carry)
39 {
40         ulong amount, val;
41
42         val = P->R[instr & 15];
43         if(instr & (1<<4)) {
44                 if(instr & (1<<7))
45                         invalid(instr);
46                 amount = P->R[(instr >> 8) & 15] & 0xFF;
47                 if(amount == 0)
48                         return val;
49         } else {
50                 amount = (instr >> 7) & 31;
51                 if(amount == 0 && (instr & (3<<5)) != 0)
52                         amount = 32;
53         }
54         switch((instr >> 5) & 3) {
55         default:
56                 if(amount == 0)
57                         return val;
58                 if(amount < 32) {
59                         *carry = (val >> (32 - amount)) & 1;
60                         return val << amount;
61                 }
62                 *carry = val & 1;
63                 return 0;
64         case 1:
65                 if(amount < 32){
66                         *carry = (val >> (amount - 1)) & 1;
67                         return val >> amount;
68                 }
69                 *carry = val >> 31;
70                 return 0;
71         case 2:
72                 if(amount < 32){
73                         *carry = (val >> (amount - 1)) & 1;
74                         return ((long) val) >> amount;
75                 }
76                 if((long)val < 0){
77                         *carry = 1;
78                         return -1;
79                 }
80                 *carry = 0;
81                 return 0;
82         case 3:
83                 amount &= 31;
84                 if(amount){
85                         *carry = (val >> (amount - 1)) & 1;
86                         return (val >> amount) | (val << (32 - amount));
87                 }
88                 amount = *carry & 1;
89                 *carry = val & 1;
90                 return (val>>1) | (amount<<31);
91         }
92 }
93
94 static void
95 single(u32int instr)
96 {
97         long offset;
98         u32int addr;
99         u32int *Rn, *Rd;
100         void *targ;
101         Segment *seg;
102         
103         if(instr & fI) {
104                 u8int carry = 0;
105                 if(instr & (1<<4))
106                         invalid(instr);
107                 offset = doshift(instr, &carry);
108         } else
109                 offset = instr & ((1<<12) - 1);
110         if(!(instr & fU))
111                 offset = - offset;
112         Rn = P->R + ((instr >> 16) & 15);
113         Rd = P->R + ((instr >> 12) & 15);
114         if((instr & (fW | fP)) == fW)
115                 invalid(instr);
116         if(Rn == P->R + 15) {
117                 if(instr & fW)
118                         invalid(instr);
119                 addr = P->R[15] + 4;
120         }
121         else
122                 addr = *Rn;
123         if(instr & fP)
124                 addr += offset;
125         if((instr & fB) == 0)
126                 addr = evenaddr(addr, 3);
127         targ = vaddr(addr, (instr & fB) == 0 ? 4 : 1, &seg);
128         switch(instr & (fB | fL)) {
129         case 0:
130                 *(u32int*) targ = *Rd;
131                 break;
132         case fB:
133                 *(u8int*) targ = *Rd;
134                 break;
135         case fL:
136                 *Rd = *(u32int*) targ;
137                 break;
138         case fB | fL:
139                 *Rd = *(u8int*) targ;
140                 break;
141         }
142         if(Rd == P->R + 15 && !(instr & fL)) {
143                 if(instr & fB)
144                         *(u8int*) targ += 8;
145                 else
146                         *(u32int*) targ += 8;
147         }
148         segunlock(seg);
149         if(!(instr & fP))
150                 addr += offset;
151         if((instr & fW) || !(instr & fP))
152                 *Rn = addr;
153 }
154
155 /* atomic compare and swap from libc */
156 extern int cas(u32int *p, u32int old, u32int new);
157
158 static void
159 swap(u32int instr)
160 {
161         u32int *Rm, *Rn, *Rd, *targ, addr, old, new;
162         Segment *seg;
163         
164         Rm = P->R + (instr & 15);
165         Rd = P->R + ((instr >> 12) & 15);
166         Rn = P->R + ((instr >> 16) & 15);
167         if(Rm == P->R + 15 || Rd == P->R + 15 || Rn == P->R + 15)
168                 invalid(instr);
169         addr = *Rn;
170         if((instr & fB) == 0)
171                 addr = evenaddr(addr, 3);
172         targ = (u32int *) vaddr(addr & ~3, 4, &seg);
173         do {
174                 old = *targ;
175                 new = *Rm;
176                 if(instr & fB){
177                         new &= 0xFF;
178                         new <<= 8*(addr&3);
179                         new |= old & ~(0xFF << 8*(addr&3));
180                 }
181         } while(!cas(targ, old, new));
182         if(instr & fB) {
183                 old >>= 8*(addr&3);
184                 old &= 0xFF;
185         }
186         *Rd = old;
187         segunlock(seg);
188 }
189
190 static u32int
191 add(u32int a, u32int b, u8int type, u8int *carry, u8int *overflow)
192 {
193         u32int res1;
194         u64int res2;
195
196         if(type) {
197                 res2 = (u64int)a - b + *carry - 1;
198                 res1 = res2;
199                 if(((a ^ b) & (1<<31)) && !((b ^ res1) & (1<<31))) *overflow = 1;
200                 else *overflow = 0;
201                 if(res2 & 0x100000000LL) *carry = 0;
202                 else *carry = 1;        
203         } else {
204                 res2 = (u64int)a + b + *carry;
205                 res1 = res2;
206                 if(!((a ^ b) & (1<<31)) && ((b ^ res1) & (1<<31))) *overflow = 1;
207                 else *overflow = 0;
208                 if(res2 & 0x100000000LL) *carry = 1;
209                 else *carry = 0;
210         }
211         return res1;
212 }
213
214 static void
215 alu(u32int instr)
216 {
217         u32int Rn, *Rd, operand, shift, result, op;
218         u8int carry, overflow;
219         
220         Rn = P->R[(instr >> 16) & 15];
221         Rd = P->R + ((instr >> 12) & 15);
222         if(((instr >> 16) & 15) == 15) {
223                 Rn += 4;
224                 if(!(instr & fI) && (instr & (1<<4)))
225                         Rn += 4;
226         }
227         if(Rd == P->R + 15 && (instr & fS))
228                 invalid(instr);
229
230         carry = (P->CPSR & flC) != 0;
231         overflow = (P->CPSR & flV) != 0;
232
233         if(instr & fI) {
234                 operand = instr & 0xFF;
235                 shift = ((instr >> 8) & 15) << 1;
236                 if(shift){
237                         operand = (operand >> shift) | (operand << (32 - shift));
238                         carry = operand >> 31;
239                 }
240         } else
241                 operand = doshift(instr, &carry);
242
243         op = (instr >> 21) & 15;
244         if(op >= 8 && op <= 11 && !(instr & fS))
245                 sysfatal("no PSR transfers plz");
246         if(op >= 5 && op < 8)
247                 carry = (P->CPSR & flC) != 0;
248         switch(op) {
249         case 0: case 8: result = Rn & operand; break;
250         case 1: case 9: result = Rn ^ operand; break;
251         case 2: case 10: carry = 1; case 6: result = add(Rn, operand, 1, &carry, &overflow); break;
252         case 3:          carry = 1; case 7: result = add(operand, Rn, 1, &carry, &overflow); break;
253         case 4: case 11: carry = 0; case 5: result = add(operand, Rn, 0, &carry, &overflow); break;
254         case 12: result = Rn | operand; break;
255         case 13: result = operand; break;
256         case 14: result = Rn & ~operand; break;
257         case 15: result = ~operand; break;
258         default: result = 0; /* never happens */
259         }
260         if(instr & fS) {
261                 P->CPSR &= ~FLAGS;
262                 if(result == 0)
263                         P->CPSR |= flZ;
264                 if(result & (1<<31))
265                         P->CPSR |= flN;
266                 if(carry)
267                         P->CPSR |= flC;
268                 if(overflow)
269                         P->CPSR |= flV;
270         }
271         if(op < 8 || op >= 12)
272                 *Rd = result;
273 }
274
275 static void
276 branch(u32int instr)
277 {
278         long offset;
279         
280         offset = instr & ((1<<24) - 1);
281         if(offset & (1<<23))
282                 offset |= ~((1 << 24) - 1);
283         offset *= 4;
284         if(instr & fLi)
285                 P->R[14] = P->R[15];
286         P->R[15] += offset + 4;
287 }
288
289 static void
290 halfword(u32int instr)
291 {
292         u32int offset, target, *Rn, *Rd;
293         Segment *seg;
294         
295         if(instr & (1<<22)) {
296                 offset = (instr & 15) | ((instr >> 4) & 0xF0);
297         } else {
298                 if((instr & 15) == 15)
299                         invalid(instr);
300                 offset = P->R[instr & 15];
301         }
302         if(!(instr & fU))
303                 offset = - offset;
304         if(!(instr & fP) && (instr & fW))
305                 invalid(instr);
306         Rn = P->R + ((instr >> 16) & 15);
307         Rd = P->R + ((instr >> 12) & 15);
308         if(Rn == P->R + 15 || Rd == P->R + 15)
309                 sysfatal("R15 in halfword");
310         target = *Rn;
311         if(instr & fP)
312                 target += offset;
313         if(instr & fH)
314                 target = evenaddr(target, 1);
315         switch(instr & (fSg | fH | fL)) {
316         case fSg: *(u8int*) vaddr(target, 1, &seg) = *Rd; break;
317         case fSg | fL: *Rd = (long) *(char*) vaddr(target, 1, &seg); break;
318         case fH: case fSg | fH: *(u16int*) vaddr(target, 2, &seg) = *Rd; break;
319         case fH | fL: *Rd = *(u16int*) vaddr(target, 2, &seg); break;
320         case fH | fL | fSg: *Rd = (long) *(short*) vaddr(target, 2, &seg); break;
321         }
322         segunlock(seg);
323         if(!(instr & fP))
324                 target += offset;
325         if(!(instr & fP) || (instr & fW))
326                 *Rn = target;
327 }
328
329 static void
330 block(u32int instr)
331 {
332         int i;
333         u32int targ, *Rn;
334         Segment *seg;
335
336         if(instr & (1<<22))
337                 invalid(instr);
338         Rn = P->R + ((instr >> 16) & 15);
339         if(Rn == P->R + 15 || instr & (1<<15))
340                 sysfatal("R15 block");
341         targ = evenaddr(*Rn, 3);
342         if(instr & fU) {
343                 for(i = 0; i < 16; i++) {
344                         if(!(instr & (1<<i)))
345                                 continue;
346                         if(instr & fP)
347                                 targ += 4;
348                         if(instr & fL)
349                                 P->R[i] = *(u32int*) vaddr(targ, 4, &seg);
350                         else
351                                 *(u32int*) vaddr(targ, 4, &seg) = P->R[i];
352                         segunlock(seg);
353                         if(!(instr & fP))
354                                 targ += 4;
355                 }
356         } else {
357                 for(i = 15; i >= 0; i--) {
358                         if(!(instr & (1<<i)))
359                                 continue;
360                         if(instr & fP)
361                                 targ -= 4;
362                         if(instr & fL)
363                                 P->R[i] = *(u32int*) vaddr(targ, 4, &seg);
364                         else
365                                 *(u32int*) vaddr(targ, 4, &seg) = P->R[i];
366                         segunlock(seg);
367                         if(!(instr & fP))
368                                 targ -= 4;
369                 }
370         }
371         if(instr & fW)
372                 *Rn = targ;
373 }
374
375 static void
376 multiply(u32int instr)
377 {
378         u32int *Rd, *Rn, *Rs, *Rm, res;
379         
380         Rm = P->R + (instr & 15);
381         Rs = P->R + ((instr >> 8) & 15);
382         Rn = P->R + ((instr >> 12) & 15);
383         Rd = P->R + ((instr >> 16) & 15);
384         if(Rd == Rm || Rm == P->R + 15 || Rs == P->R + 15 || Rn == P->R + 15 || Rd == P->R + 15)
385                 invalid(instr);
386         res = *Rm * *Rs;
387         if(instr & (1<<21))
388                 res += *Rn;
389         *Rd = res;
390         if(instr & (1<<20)) {
391                 P->CPSR &= ~(flN | flZ);
392                 if(res & (1<<31))
393                         P->CPSR |= flN;
394                 if(res == 0)
395                         P->CPSR |= flZ;
396         }
397 }
398
399 static void
400 multiplylong(u32int instr)
401 {
402         u32int *RdH, *RdL, *Rs, *Rm;
403         u64int res;
404         
405         Rm = P->R + (instr & 15);
406         Rs = P->R + ((instr >> 8) & 15);
407         RdL = P->R + ((instr >> 12) & 15);
408         RdH = P->R + ((instr >> 16) & 15);
409         if(RdL == RdH || RdH == Rm || RdL == Rm || Rm == P->R + 15 || Rs == P->R + 15 || RdL == P->R + 15 || RdH == P->R + 15)
410                 invalid(instr);
411         if(instr & (1<<22))
412                 res = ((vlong)*(int*)Rs) * *(int*)Rm;
413         else {
414                 res = *Rs;
415                 res *= *Rm;
416         }
417         if(instr & (1<<21)) {
418                 res += *RdL;
419                 res += ((uvlong)*RdH) << 32;
420         }
421         *RdL = res;
422         *RdH = res >> 32;
423         if(instr & (1<<20)) {
424                 P->CPSR &= ~FLAGS;
425                 if(res == 0)
426                         P->CPSR |= flN;
427                 if(res & (1LL<<63))
428                         P->CPSR |= flV;
429         }
430 }
431
432 static void
433 singleex(u32int instr)
434 {
435         u32int *Rn, *Rd, *Rm, *targ, addr;
436         Segment *seg;
437         
438         Rd = P->R + ((instr >> 12) & 15);
439         Rn = P->R + ((instr >> 16) & 15);
440         if(Rd == P->R + 15 || Rn == P->R + 15)
441                 invalid(instr);
442         addr = evenaddr(*Rn, 3);
443         if(instr & fS) {
444                 targ = vaddr(addr, 4, &seg);
445                 *Rd = *targ;
446                 P->lladdr = addr;
447                 P->llval = *Rd;
448                 segunlock(seg);
449         } else {
450                 Rm = P->R + (instr & 15);
451                 if(Rm == P->R + 15)
452                         invalid(instr);
453                 targ = vaddr(addr, 4, &seg);
454
455                 /*
456                  * this is not quite correct as we will succeed even
457                  * if the value was modified and then restored to its
458                  * original value but good enougth approximation for
459                  * libc's _tas(), _cas() and _xinc()/_xdec().
460                  */
461                 *Rd = addr != P->lladdr || !cas(targ, P->llval, *Rm);
462                 segunlock(seg);
463                 clrex();
464         }
465 }
466
467 void
468 clrex(void)
469 {
470         P->lladdr = 0;
471         P->llval = 0;
472 }
473
474 static void
475 barrier(void)
476 {
477         static Lock l;
478
479         lock(&l);
480         unlock(&l);
481 }
482
483 void
484 step(void)
485 {
486         u32int instr;
487         Segment *seg;
488
489         instr = *(u32int*) vaddr(P->R[15], 4, &seg);
490         segunlock(seg);
491         if(fulltrace) {
492                 print("%d ", P->pid);
493                 if(havesymbols) {
494                         Symbol s;
495                         char buf[512];
496                         
497                         if(findsym(P->R[15], CTEXT, &s) >= 0)
498                                 print("%s ", s.name);
499                         if(fileline(buf, 512, P->R[15]) >= 0)
500                                 print("%s ", buf);
501                 }
502                 print("%.8ux %.8ux %c%c%c%c\n", P->R[15], instr,
503                         (P->CPSR & flZ) ? 'Z' : ' ',
504                         (P->CPSR & flC) ? 'C' : ' ',
505                         (P->CPSR & flN) ? 'N' : ' ',
506                         (P->CPSR & flV) ? 'V' : ' '
507                         );
508         }
509         P->R[15] += 4;
510         switch(instr >> 28) {
511         case 0x0: if(!(P->CPSR & flZ)) return; break;
512         case 0x1: if(P->CPSR & flZ) return; break;
513         case 0x2: if(!(P->CPSR & flC)) return; break;
514         case 0x3: if(P->CPSR & flC) return; break;
515         case 0x4: if(!(P->CPSR & flN)) return; break;
516         case 0x5: if(P->CPSR & flN) return; break;
517         case 0x6: if(!(P->CPSR & flV)) return; break;
518         case 0x7: if(P->CPSR & flV) return; break;
519         case 0x8: if(!(P->CPSR & flC) || (P->CPSR & flZ)) return; break;
520         case 0x9: if((P->CPSR & flC) && !(P->CPSR & flZ)) return; break;
521         case 0xA: if(!(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
522         case 0xB: if(!(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
523         case 0xC: if((P->CPSR & flZ) || !(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
524         case 0xD: if(!(P->CPSR & flZ) && !(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
525         case 0xE: break;
526         case 0xF:
527                 switch(instr & 0xFFF000F0){
528                 case 0xF5700010:        /* CLREX */
529                         clrex();
530                         return;
531                 case 0xF5700040:        /* DSB */
532                 case 0xF5700050:        /* DMB */
533                 case 0xF5700060:        /* ISB */
534                         barrier();
535                         return;
536                 }
537         default: sysfatal("condition code %x not implemented (instr %ux, ps %ux)", instr >> 28, instr, P->R[15]);
538         }
539         if((instr & 0x0FB00FF0) == 0x01000090)
540                 swap(instr);
541         else if((instr & 0x0FE000F0) == 0x01800090)
542                 singleex(instr);
543         else if((instr & 0x0FC000F0) == 0x90)
544                 multiply(instr);
545         else if((instr & 0x0F8000F0) == 0x800090)
546                 multiplylong(instr);
547         else if((instr & ((1<<26) | (1<<27))) == (1 << 26))
548                 single(instr);
549         else if((instr & 0x0E000090) == 0x90 && (instr & 0x60))
550                 halfword(instr);
551         else if((instr & ((1<<26) | (1<<27))) == 0)
552                 alu(instr);
553         else if((instr & (7<<25)) == (5 << 25))
554                 branch(instr);
555         else if((instr & (15<<24)) == (15 << 24))
556                 syscall();
557         else if((instr & (7<<25)) == (4 << 25))
558                 block(instr);
559         else if((instr & 0x0E000F00) == 0x0C000100)
560                 fpatransfer(instr);
561         else if((instr & 0x0E000F10) == 0x0E000100)
562                 fpaoperation(instr);
563         else if((instr & 0x0E000F10) == 0x0E000110)
564                 fparegtransfer(instr);
565         else if(vfp && ((instr & 0x0F000A10) == 0x0E000A00))
566                 vfpoperation(instr);
567         else if(vfp && ((instr & 0x0F000F10) == 0x0E000A10))
568                 vfpregtransfer(instr);
569         else if(vfp && ((instr & 0x0F000A00) == 0x0D000A00))
570                 vfprmtransfer(instr);
571         else
572                 invalid(instr);
573 }