Add an arch device
[akaros.git] / kern / arch / x86 / devarch.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <time.h>
24
25 typedef struct IOMap IOMap;
26 struct IOMap
27 {
28         IOMap   *next;
29         int     reserved;
30         char    tag[13];
31         uint32_t        start;
32         uint32_t        end;
33 };
34
35 static struct
36 {
37         spinlock_t lock;
38         IOMap   *map;
39         IOMap   *free;
40         IOMap   maps[32];               // some initial free maps
41
42         qlock_t ql;                     // lock for reading map
43 } iomap;
44
45 enum {
46         Qdir = 0,
47         Qioalloc = 1,
48         Qiob,
49         Qiow,
50         Qiol,
51         Qgdb,
52         Qbase,
53         Qmapram,
54         Qrealmem,
55
56         Qmax = 16,
57 };
58
59 typedef long Rdwrfn(struct chan*, void*, long, int64_t);
60
61 static Rdwrfn *readfn[Qmax];
62 static Rdwrfn *writefn[Qmax];
63
64 static struct dirtab archdir[Qmax] = {
65         {".",           { Qdir, 0, QTDIR },     0,      0555},
66         {"ioalloc",     { Qioalloc, 0 },        0,      0444},
67         {"iob",         { Qiob, 0 },            0,      0660},
68         {"iow",         { Qiow, 0 },            0,      0660},
69         {"iol",         { Qiol, 0 },            0,      0660},
70         {"gdb",         { Qgdb, 0},             0,      0660},
71         {"mapram",      { Qmapram, 0 }, 0,      0444},
72         {"realmodemem", { Qrealmem, 0 },        0,      0660},
73 };
74 spinlock_t archwlock;   /* the lock is only for changing archdir */
75 int narchdir = Qbase;
76 int gdbactive = 0;
77
78 /*
79  * Add a file to the #P listing.  Once added, you can't delete it.
80  * You can't add a file with the same name as one already there,
81  * and you get a pointer to the Dirtab entry so you can do things
82  * like change the Qid version.  Changing the Qid path is disallowed.
83  */
84 struct dirtab*
85 addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
86 {
87         int i;
88         struct dirtab d;
89         struct dirtab *dp;
90
91         memset(&d, 0, sizeof d);
92         strncpy(d.name,  name, sizeof(d.name));
93         d.perm = perm;
94
95         spin_lock(&archwlock);
96         if(narchdir >= Qmax){
97                 spin_unlock(&archwlock);
98                 return NULL;
99         }
100
101         for(i=0; i<narchdir; i++)
102                 if(strcmp(archdir[i].name, name) == 0){
103                         spin_unlock(&archwlock);
104                         return NULL;
105                 }
106
107         d.qid.path = narchdir;
108         archdir[narchdir] = d;
109         readfn[narchdir] = rdfn;
110         writefn[narchdir] = wrfn;
111         dp = &archdir[narchdir++];
112         spin_unlock(&archwlock);
113
114         return dp;
115 }
116
117 void
118 ioinit(void)
119 {
120         int i;
121         char *excluded = "";
122
123         for(i = 0; i < ARRAY_SIZE(iomap.maps)-1; i++)
124                 iomap.maps[i].next = &iomap.maps[i+1];
125         iomap.maps[i].next = NULL;
126         iomap.free = iomap.maps;
127         char *s;
128         
129         s = excluded;
130         while (s && *s != '\0' && *s != '\n') {
131                 char *ends;
132                 int io_s, io_e;
133                 
134                 io_s = (int)strtol(s, &ends, 0);
135                 if (ends == NULL || ends == s || *ends != '-') {
136                         printd("ioinit: cannot parse option string\n");
137                         break;
138                 }
139                 s = ++ends;
140                 
141                 io_e = (int)strtol(s, &ends, 0);
142                 if (ends && *ends == ',')
143                         *ends++ = '\0';
144                 s = ends;
145                 
146 #warning "how do we do io allocate"
147                 //ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
148         }
149 }
150
151 // Reserve a range to be ioalloced later.
152 // This is in particular useful for exchangable cards, such
153 // as pcmcia and cardbus cards.
154 int
155 ioreserve( int unused_int, int size, int align, char *tag)
156 {
157         IOMap *map, **l;
158         int i, port;
159
160         spin_lock(&(&iomap)->lock);
161         // find a free port above 0x400 and below 0x1000
162         port = 0x400;
163         for(l = &iomap.map; *l; l = &(*l)->next){
164                 map = *l;
165                 if (map->start < 0x400)
166                         continue;
167                 i = map->start - port;
168                 if(i > size)
169                         break;
170                 if(align > 0)
171                         port = ((port+align-1)/align)*align;
172                 else
173                         port = map->end;
174         }
175         if(*l == NULL){
176                 spin_unlock(&(&iomap)->lock);
177                 return -1;
178         }
179         map = iomap.free;
180         if(map == NULL){
181                 printd("ioalloc: out of maps");
182                 spin_unlock(&(&iomap)->lock);
183                 return port;
184         }
185         iomap.free = map->next;
186         map->next = *l;
187         map->start = port;
188         map->end = port + size;
189         map->reserved = 1;
190         strncpy(map->tag, tag, sizeof(map->tag));
191         map->tag[sizeof(map->tag)-1] = 0;
192         *l = map;
193
194         archdir[0].qid.vers++;
195
196         spin_unlock(&(&iomap)->lock);
197         return map->start;
198 }
199
200 //
201 //      alloc some io port space and remember who it was
202 //      alloced to.  if port < 0, find a free region.
203 //
204 int
205 ioalloc(int port, int size, int align, char *tag)
206 {
207         IOMap *map, **l;
208         int i;
209
210         spin_lock(&(&iomap)->lock);
211         if(port < 0){
212                 // find a free port above 0x400 and below 0x1000
213                 port = 0x400;
214                 for(l = &iomap.map; *l; l = &(*l)->next){
215                         map = *l;
216                         if (map->start < 0x400)
217                                 continue;
218                         i = map->start - port;
219                         if(i > size)
220                                 break;
221                         if(align > 0)
222                                 port = ((port+align-1)/align)*align;
223                         else
224                                 port = map->end;
225                 }
226                 if(*l == NULL){
227                         spin_unlock(&(&iomap)->lock);
228                         return -1;
229                 }
230         } else {
231                 // Only 64KB I/O space on the x86.
232                 if((port+size) > 0x10000){
233                         spin_unlock(&(&iomap)->lock);
234                         return -1;
235                 }
236                 // see if the space clashes with previously allocated ports
237                 for(l = &iomap.map; *l; l = &(*l)->next){
238                         map = *l;
239                         if(map->end <= port)
240                                 continue;
241                         if(map->reserved && map->start == port && map->end == port + size) {
242                                 map->reserved = 0;
243                                 spin_unlock(&(&iomap)->lock);
244                                 return map->start;
245                         }
246                         if(map->start >= port+size)
247                                 break;
248                         spin_unlock(&(&iomap)->lock);
249                         return -1;
250                 }
251         }
252         map = iomap.free;
253         if(map == NULL){
254                 printd("ioalloc: out of maps");
255                 spin_unlock(&(&iomap)->lock);
256                 return port;
257         }
258         iomap.free = map->next;
259         map->next = *l;
260         map->start = port;
261         map->end = port + size;
262         strncpy(map->tag, tag, sizeof(map->tag));
263         map->tag[sizeof(map->tag)-1] = 0;
264         *l = map;
265
266         archdir[0].qid.vers++;
267
268         spin_unlock(&(&iomap)->lock);
269         return map->start;
270 }
271
272 void
273 iofree(int port)
274 {
275         IOMap *map, **l;
276
277         spin_lock(&(&iomap)->lock);
278         for(l = &iomap.map; *l; l = &(*l)->next){
279                 if((*l)->start == port){
280                         map = *l;
281                         *l = map->next;
282                         map->next = iomap.free;
283                         iomap.free = map;
284                         break;
285                 }
286                 if((*l)->start > port)
287                         break;
288         }
289         archdir[0].qid.vers++;
290         spin_unlock(&(&iomap)->lock);
291 }
292
293 int
294 iounused(int start, int end)
295 {
296         IOMap *map;
297
298         for(map = iomap.map; map; map = map->next){
299                 if(((start >= map->start) && (start < map->end))
300                    || ((start <= map->start) && (end > map->start)))
301                         return 0;
302         }
303         return 1;
304 }
305
306 static void
307 checkport(int start, int end)
308 {
309         /* standard vga regs are OK */
310         if(start >= 0x2b0 && end <= 0x2df+1)
311                 return;
312         if(start >= 0x3c0 && end <= 0x3da+1)
313                 return;
314
315         if(iounused(start, end))
316                 return;
317         error(Eperm);
318 }
319
320 static struct chan*
321 archattach(char* spec)
322 {
323         return devattach('P', spec);
324 }
325
326 struct walkqid*
327 archwalk(struct chan* c, struct chan *nc, char** name, int nname)
328 {
329         return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
330 }
331
332 static int
333 archstat(struct chan* c, uint8_t* dp, int n)
334 {
335         return devstat(c, dp, n, archdir, narchdir, devgen);
336 }
337
338 static struct chan*
339 archopen(struct chan* c, int omode)
340 {
341         return devopen(c, omode, archdir, narchdir, devgen);
342 }
343
344 static void
345 archclose(struct chan*unused)
346 {
347 }
348
349 enum
350 {
351         Linelen= 31,
352 };
353
354 static long
355 archread(struct chan *c, void *a, long n, int64_t offset)
356 {
357         char *buf, *p;
358         int port;
359         uint16_t *sp;
360         uint32_t *lp;
361         IOMap *map;
362         Rdwrfn *fn;
363
364         switch((uint32_t)c->qid.path){
365
366         case Qdir:
367                 return devdirread(c, a, n, archdir, narchdir, devgen);
368
369         case Qgdb:
370                 p = gdbactive ? "1" : "0";
371                 return readstr(offset, a, n, p);
372         case Qiob:
373                 port = offset;
374                 checkport(offset, offset+n);
375                 for(p = a; port < offset+n; port++)
376                         *p++ = inb(port);
377                 return n;
378
379         case Qiow:
380                 if(n & 1)
381                         error(Ebadarg);
382                 checkport(offset, offset+n);
383                 sp = a;
384                 for(port = offset; port < offset+n; port += 2)
385                         *sp++ = inw(port);
386                 return n;
387
388         case Qiol:
389                 if(n & 3)
390                         error(Ebadarg);
391                 checkport(offset, offset+n);
392                 lp = a;
393                 for(port = offset; port < offset+n; port += 4)
394                         *lp++ = inl(port);
395                 return n;
396
397         case Qioalloc:
398                 break;
399
400         default:
401                 if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
402                         return fn(c, a, n, offset);
403                 error(Eperm);
404                 break;
405         }
406
407         if((buf = kzmalloc(n, 0)) == NULL)
408                 error(Enomem);
409         p = buf;
410         n = n/Linelen;
411         offset = offset/Linelen;
412
413         switch((uint32_t)c->qid.path){
414         case Qioalloc:
415                 spin_lock(&(&iomap)->lock);
416                 for(map = iomap.map; n > 0 && map != NULL; map = map->next){
417                         if(offset-- > 0)
418                                 continue;
419                         snprintf(p, n*Linelen, "%#8p %#8p %-12.12s\n", map->start, map->end-1, map->tag);
420                         p += Linelen;
421                         n--;
422                 }
423                 spin_unlock(&(&iomap)->lock);
424                 break;
425         case Qmapram:
426                 error("Not yet");
427                 break;
428         }
429
430         n = p - buf;
431         memmove(a, buf, n);
432         kfree(buf);
433
434         return n;
435 }
436
437 static long
438 archwrite(struct chan *c, void *a, long n, int64_t offset)
439 {
440         char *p;
441         int port;
442         uint16_t *sp;
443         uint32_t *lp;
444         Rdwrfn *fn;
445
446         switch((uint32_t)c->qid.path){
447
448         case Qgdb:
449                 p = a;
450                 if (n != 1)
451                         error("Gdb: Write one byte, '1' or '0'");
452                 if (*p == '1')
453                         gdbactive = 1;
454                 else if (*p == '0')
455                         gdbactive = 0;
456                 else
457                         error("Gdb: must be 1 or 0");
458                 return 1;
459
460         case Qiob:
461                 p = a;
462                 checkport(offset, offset+n);
463                 for(port = offset; port < offset+n; port++)
464                         outb(port, *p++);
465                 return n;
466
467         case Qiow:
468                 if(n & 1)
469                         error(Ebadarg);
470                 checkport(offset, offset+n);
471                 sp = a;
472                 for(port = offset; port < offset+n; port += 2)
473                         outw(port, *sp++);
474                 return n;
475
476         case Qiol:
477                 if(n & 3)
478                         error(Ebadarg);
479                 checkport(offset, offset+n);
480                 lp = a;
481                 for(port = offset; port < offset+n; port += 4)
482                         outl(port, *lp++);
483                 return n;
484
485         default:
486                 if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
487                         return fn(c, a, n, offset);
488                 error(Eperm);
489                 break;
490         }
491         return 0;
492 }
493
494 struct dev archdevtab = {
495         'P',
496         "arch",
497
498         devreset,
499         devinit,
500         devshutdown,
501         archattach,
502         archwalk,
503         archstat,
504         archopen,
505         devcreate,
506         archclose,
507         archread,
508         devbread,
509         archwrite,
510         devbwrite,
511         devremove,
512         devwstat,
513 };
514
515 /*
516  */
517 void
518 nop(void)
519 {
520 }
521
522 //void (*coherence)(void) = mfence;
523 #warning "need memory fence"
524 #define coherence()
525
526
527 static long
528 cputyperead(struct chan*unused, void *a, long n, int64_t off)
529 {
530         char buf[512], *s, *e;
531         int i, k;
532         error("unimplemented");
533 #if 0
534         e = buf+sizeof buf;
535         s = seprintf(buf, e, "%s %d\n", "AMD64", 0);
536         k = m->ncpuinfoe - m->ncpuinfos;
537         if(k > 4)
538                 k = 4;
539         for(i = 0; i < k; i++)
540                 s = seprintf(s, e, "%#8.8ux %#8.8ux %#8.8ux %#8.8ux\n",
541                         m->cpuinfo[i][0], m->cpuinfo[i][1],
542                         m->cpuinfo[i][2], m->cpuinfo[i][3]);
543         return readstr(off, a, n, buf);
544 #endif
545 }
546
547
548 static long
549 rmemrw(int isr, void *a, long n, int64_t off)
550 {
551         if(off < 0)
552                 error("offset must be >= 0");
553         if(n < 0)
554                 error("count must be >= 0");
555         if(isr){
556                 if(off >= MB)
557                         error("offset must be < 1MB");
558                 if(off+n >= MB)
559                         n = MB - off;
560                 memmove(a, KADDR((uint32_t)off), n);
561         } else {
562                 /* realmode buf page ok, allow vga framebuf's access */
563                 if(off >= MB)
564                         error("offset must be < 1MB");
565                 if(off+n > MB && (off < 0xA0000 || off+n > 0xB0000+0x10000))
566                         error("bad offset/count in write");
567                 memmove(KADDR((uint32_t)off), a, n);
568         }
569         return n;
570 }
571
572 static long
573 rmemread(struct chan*unused, void *a, long n, int64_t off)
574 {
575         return rmemrw(1, a, n, off);
576 }
577
578 static long
579 rmemwrite(struct chan*unused, void *a, long n, int64_t off)
580 {
581         return rmemrw(0, a, n, off);
582 }
583
584 void
585 archinit(void)
586 {
587         spinlock_init(&archwlock);
588         addarchfile("cputype", 0444, cputyperead, NULL);
589         addarchfile("realmodemem", 0660, rmemread, rmemwrite);
590 }
591
592 void
593 archreset(void)
594 {
595         int i;
596
597         /*
598          * And sometimes there is no keyboard...
599          *
600          * The reset register (0xcf9) is usually in one of the bridge
601          * chips. The actual location and sequence could be extracted from
602          * ACPI but why bother, this is the end of the line anyway.
603         print("Takes a licking and keeps on ticking...\n");
604          */
605         i = inb(0xcf9);                                 /* ICHx reset control */
606         i &= 0x06;
607         outb(0xcf9, i|0x02);                            /* SYS_RST */
608         udelay(1000);
609         outb(0xcf9, i|0x06);                            /* RST_CPU transition */
610
611         udelay(100*1000);
612
613         /* some broken hardware -- as well as qemu -- might
614          * never reboot anyway with cf9. This is a standard
615          * keyboard reboot sequence known to work on really
616          * broken stuff -- like qemu. If there is no
617          * keyboard it will do no harm.
618          */
619         for(;;){
620                 (void) inb(0x64);
621                 outb(0x64, 0xFE);
622                 udelay(100*1000);
623         }
624 }
625