]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/bootstrap/server/src/platform/x86_pc.cc
update
[l4.git] / l4 / pkg / bootstrap / server / src / platform / x86_pc.cc
1 /*!
2  * \file   support_x86.cc
3  * \brief  Support for the x86 platform
4  *
5  * \date   2008-01-02
6  * \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
7  *
8  */
9 /*
10  * (c) 2008-2009 Author(s)
11  *     economic rights: Technische Universität Dresden (Germany)
12  *
13  * This file is part of TUD:OS and distributed under the terms of the
14  * GNU General Public License 2.
15  * Please see the COPYING-GPL-2 file for details.
16  */
17
18 #include "support.h"
19 #include <l4/drivers/uart_pxa.h>
20 #include <l4/drivers/io_regblock_port.h>
21
22 #include <string.h>
23 #include "base_critical.h"
24 #include "startup.h"
25 #include <l4/util/cpu.h>
26 #include <l4/util/port_io.h>
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30
31
32 /** VGA console output */
33
34 static void vga_init()
35 {
36   /* Reset any scrolling */
37   l4util_out32(0xc, 0x3d4);
38   l4util_out32(0, 0x3d5);
39   l4util_out32(0xd, 0x3d4);
40   l4util_out32(0, 0x3d5);
41 }
42
43 static void vga_putchar(unsigned char c)
44 {
45   static int ofs = -1, esc, esc_val, attr = 0x07;
46   unsigned char *vidbase = (unsigned char*)0xb8000;
47
48   base_critical_enter();
49
50   if (ofs < 0)
51     {
52       /* Called for the first time - initialize.  */
53       ofs = 80*2*24;
54       vga_putchar('\n');
55     }
56
57   switch (esc)
58     {
59     case 1:
60       if (c == '[')
61         {
62           esc++;
63           goto done;
64         }
65       esc = 0;
66       break;
67
68     case 2:
69       if (c >= '0' && c <= '9')
70         {
71           esc_val = 10*esc_val + c - '0';
72           goto done;
73         }
74       if (c == 'm')
75         {
76           attr = esc_val ? 0x0f : 0x07;
77           goto done;
78         }
79       esc = 0;
80       break;
81     }
82
83   switch (c)
84     {
85     case '\n':
86       memmove(vidbase, vidbase+80*2, 80*2*24);
87       memset(vidbase+80*2*24, 0, 80*2);
88       /* fall through... */
89     case '\r':
90       ofs = 0;
91       break;
92
93     case '\t':
94       ofs = (ofs + 8) & ~7;
95       break;
96
97     case '\033':
98       esc = 1;
99       esc_val = 0;
100       break;
101
102     default:
103       /* Wrap if we reach the end of a line.  */
104       if (ofs >= 80)
105         vga_putchar('\n');
106
107       /* Stuff the character into the video buffer. */
108         {
109           volatile unsigned char *p = vidbase + 80*2*24 + ofs*2;
110           p[0] = c;
111           p[1] = attr;
112           ofs++;
113         }
114       break;
115     }
116
117 done:
118   base_critical_leave();
119 }
120
121 /** Poor man's getchar, only returns raw scan code. We don't need to know
122  * _which_ key was pressed, we only want to know _if_ a key was pressed. */
123 static int raw_keyboard_getscancode(void)
124 {
125   unsigned status, scan_code;
126
127   base_critical_enter();
128
129   l4util_cpu_pause();
130
131   /* Wait until a scan code is ready and read it. */
132   status = l4util_in8(0x64);
133   if ((status & 0x01) == 0)
134     {
135       base_critical_leave();
136       return -1;
137     }
138   scan_code = l4util_in8(0x60);
139
140   /* Drop mouse events */
141   if ((status & 0x20) != 0)
142     {
143       base_critical_leave();
144       return -1;
145     }
146
147   base_critical_leave();
148   return scan_code;
149 }
150
151
152 static inline l4_uint32_t
153 pci_conf_addr(l4_uint32_t bus, l4_uint32_t dev, l4_uint32_t fn, l4_uint32_t reg)
154 { return 0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
155
156 static l4_uint32_t pci_read(unsigned char bus, l4_uint32_t dev,
157                             l4_uint32_t fn, l4_uint32_t reg,
158                             unsigned char width)
159 {
160   l4util_out32(pci_conf_addr(bus, dev, fn, reg), 0xcf8);
161
162   switch (width)
163     {
164     case 8:  return l4util_in8(0xcfc + (reg & 3));
165     case 16: return l4util_in16((0xcfc + (reg & 3)) & ~1UL);
166     case 32: return l4util_in32(0xcfc);
167     }
168   return 0;
169 }
170
171 static void pci_write(unsigned char bus, l4_uint32_t dev,
172                       l4_uint32_t fn, l4_uint32_t reg,
173                       l4_uint32_t val, unsigned char width)
174 {
175   l4util_out32(pci_conf_addr(bus, dev, fn, reg), 0xcf8);
176
177   switch (width)
178     {
179     case 8:  l4util_out8(val, 0xcfc + (reg & 3)); break;
180     case 16: l4util_out16(val, (0xcfc + (reg & 3)) & ~1UL); break;
181     case 32: l4util_out32(val, 0xcfc); break;
182     }
183 }
184
185 static void pci_enable_io(unsigned char bus, l4_uint32_t dev,
186                           l4_uint32_t fn)
187 {
188   unsigned cmd = pci_read(bus, dev, fn, 4, 16);
189   pci_write(bus, dev, fn, 4, cmd | 1, 16);
190 }
191
192 namespace {
193
194 struct Resource
195 {
196   enum Type { NO_BAR, IO_BAR, MEM_BAR };
197   Type type;
198   unsigned long base;
199   unsigned long len;
200   Resource() : type(NO_BAR) {}
201 };
202
203 enum { NUM_BARS = 6 };
204
205 struct Serial_board
206 {
207   int num_ports;
208   int first_bar;
209   bool port_per_bar;
210   Resource bars[NUM_BARS];
211
212   unsigned long get_port(int idx)
213   {
214     if (idx >= num_ports)
215       return 0;
216
217     if (port_per_bar)
218       return bars[first_bar + idx].base;
219
220     return bars[first_bar].base + 8 * idx;
221   }
222 };
223
224
225 }
226
227 static
228 int pci_handle_serial_dev(unsigned char bus, l4_uint32_t dev,
229                           l4_uint32_t subdev, bool print,
230                           Serial_board *board)
231 {
232 #if 0
233   bool dev_enabled = false;
234 #endif
235
236   // read bars
237   int num_iobars = 0;
238   int num_membars = 0;
239   int first_port = -1;
240   for (int bar = 0; bar < NUM_BARS; ++bar)
241     {
242       int a = 0x10 + bar * 4;
243
244       unsigned v = pci_read(bus, dev, subdev, a, 32);
245       pci_write(bus, dev, subdev, a, ~0U, 32);
246       unsigned x = pci_read(bus, dev, subdev, a, 32);
247       pci_write(bus, dev, subdev, a, v, 32);
248
249       if (!v)
250         continue;
251
252       int s;
253       for (s = 2; s < 32; ++s)
254         if ((x >> s) & 1)
255           break;
256
257       board->bars[bar].base = v & ~3UL;
258       board->bars[bar].len = 1 << s;
259       board->bars[bar].type = (v & 1) ? Resource::IO_BAR : Resource::MEM_BAR;
260
261       if (print)
262         printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
263
264       switch (board->bars[bar].type)
265         {
266         case Resource::IO_BAR:
267           ++num_iobars;
268           if (first_port == -1)
269             first_port = bar;
270           break;
271         case Resource::MEM_BAR:
272           ++num_membars;
273           break;
274         default:
275           break;
276         }
277     }
278
279   printf("  serial IO card: mem bars: %d   io bars: %d\n", num_membars, num_iobars);
280
281   if (num_membars <= 1 && num_iobars == 1)
282     {
283       board->first_bar = first_port;
284       board->num_ports = board->bars[first_port].len / 8;
285       board->port_per_bar = false;
286       printf("   use serial IO card: bar=%d ports=%d\n", first_port, board->num_ports);
287       pci_enable_io(bus, dev, subdev);
288       return 1;
289     }
290
291
292   board->num_ports = 0;
293   board->first_bar = -1;
294
295   for (int bar = 0; bar < NUM_BARS; ++bar)
296     {
297       if (board->bars[bar].type == Resource::IO_BAR && board->bars[bar].len == 8
298           && (board->first_bar == -1
299               || (board->first_bar + board->num_ports) == bar))
300         {
301           ++board->num_ports;
302           if (board->first_bar == -1)
303             board->first_bar = bar;
304         }
305     }
306
307   board->port_per_bar = true;
308   return board->num_ports;
309
310 #if 0
311
312       // for now we only take IO-BARs of size 8
313       if (v & 1)
314         {
315
316           if (!scan_only && !dev_enabled)
317             {
318               pci_enable_io(bus, dev, subdev);
319               dev_enabled = true;
320             }
321
322           if (scan_only)
323             printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
324
325           if (s == 3)
326             {
327               if (scan_only)
328                 printf("   Potential serial port\n");
329               else
330                 return v & ~3;
331             }
332         }
333       else
334         if (scan_only)
335           printf("BAR%d: %08x (sz=%d)\n", bar, v & ~0xf, 1 << s);
336     }
337   return 0;
338 #endif
339 }
340
341 static unsigned long
342 _search_pci_serial_devs(Serial_board *board, unsigned look_for_subclass,
343                         bool print)
344 {
345   l4_umword_t bus, buses, dev;
346
347   for (bus=0, buses=20; bus<buses; bus++)
348     {
349       for (dev = 0; dev < 32; dev++)
350         {
351           unsigned char hdr_type = pci_read(bus, dev, 0, 0xe, 8);
352           l4_umword_t subdevs = (hdr_type & 0x80) ? 8 : 1;
353
354           for (l4_umword_t subdev = 0; subdev < subdevs; subdev++)
355             {
356               unsigned vendor = pci_read(bus, dev, subdev, 0, 16);
357               unsigned device = pci_read(bus, dev, subdev, 2, 16);
358
359               if (vendor == 0xffff)
360                 {
361                   if (subdev == 0)
362                     break;
363                   else
364                     continue;
365                 }
366
367               unsigned classcode = pci_read(bus, dev, subdev, 0x0b, 8);
368               unsigned subclass  = pci_read(bus, dev, subdev, 0x0a, 8);
369
370               if (classcode == 0x06 && subclass == 0x04)
371                 buses++;
372
373               unsigned prog = pci_read(bus, dev, subdev, 9, 8);
374
375               if (print)
376                 printf("%02lx:%02lx.%1lx Class %02x.%02x Prog %02x: %04x:%04x\n",
377                        bus, dev, subdev, classcode, subclass, prog, vendor, device);
378
379               if (classcode == 7 && subclass == look_for_subclass)
380                 if (unsigned long port = pci_handle_serial_dev(bus, dev,
381                                                                subdev, print, board))
382                   return port;
383             }
384         }
385     }
386   return 0;
387 }
388
389 static unsigned long
390 search_pci_serial_devs(int port_idx)
391 {
392   Serial_board board;
393   if (!_search_pci_serial_devs(&board, 0, true)) // classes should be 7:0
394     if (!_search_pci_serial_devs(&board, 0x80, true)) // but sometimes it's 7:80
395       return 0;
396
397   return board.get_port(port_idx);
398 }
399
400 class Uart_vga : public L4::Uart
401 {
402 public:
403   Uart_vga()
404   { }
405
406   bool startup(L4::Io_register_block const *)
407   {
408     vga_init();
409     return true;
410   }
411
412   ~Uart_vga() {}
413   void shutdown() {}
414   bool enable_rx_irq(bool) { return false; }
415   bool enable_tx_irq(bool) { return false; }
416   bool change_mode(Transfer_mode, Baud_rate) { return true; }
417   int get_char(bool blocking) const
418   {
419     int c;
420     do
421       c = raw_keyboard_getscancode();
422     while (blocking && c == -1);
423     return c;
424   }
425
426   int char_avail() const
427   {
428     return raw_keyboard_getscancode() != -1;
429   }
430
431   int write(char const *s, unsigned long count) const
432   {
433     unsigned long c = count;
434     while (c)
435       {
436         if (*s == 10)
437           vga_putchar(13);
438         vga_putchar(*s++);
439         --c;
440       }
441     return count;
442   }
443 };
444
445 class Dual_uart : public L4::Uart
446 {
447 private:
448   L4::Uart *_u1, *_u2;
449
450 public:
451   Dual_uart(L4::Uart *u1)
452   : _u1(u1), _u2(0)
453   {}
454
455   void set_uart2(L4::Uart *u2)
456   {
457     _u2 = u2;
458   }
459
460   bool startup(L4::Io_register_block const *)
461   {
462     return true;
463   }
464
465   void shutdown()
466   {
467     _u1->shutdown();
468     if (_u2)
469       _u2->shutdown();
470   }
471
472   ~Dual_uart() {}
473 #if 0
474   bool enable_rx_irq(bool e)
475   {
476     bool r1 = _u1->enable_rx_irq(e);
477     bool r2 = _u2 ? _u2->enable_rx_irq(e) : false;
478     return r1 && r2;
479   }
480
481   bool enable_tx_irq(bool e)
482   {
483     bool r1 = _u1->enable_tx_irq(e);
484     bool r2 = _u2 ? _u2->enable_tx_irq(e) : false;
485     return r1 && r2;
486   }
487 #endif
488
489   bool change_mode(Transfer_mode m, Baud_rate r)
490   {
491     bool r1 = _u1->change_mode(m, r);
492     bool r2 = _u2 ? _u2->change_mode(m, r) : false;
493     return r1 && r2;
494   }
495
496   int char_avail() const
497   {
498     return _u1->char_avail() || (_u2 && _u2->char_avail());
499   }
500
501   int get_char(bool blocking) const
502   {
503     int c;
504     do
505       {
506         c = _u1->get_char(false);
507         if (c == -1 && _u2)
508           c = _u2->get_char(false);
509       }
510     while (blocking && c == -1);
511     return c;
512   }
513
514   int write(char const *s, unsigned long count) const
515   {
516     int r = _u1->write(s, count);
517     if (_u2)
518       _u2->write(s, count);
519     return r;
520   }
521
522 };
523
524 l4util_mb_info_t *x86_bootloader_mbi;
525
526 namespace {
527
528 class Platform_x86 : public Platform_base
529 {
530 public:
531   bool probe() { return true; }
532
533   int init_uart(int com_port_or_base, int com_irq, Dual_uart *du)
534   {
535     base_critical_enter();
536
537     switch (com_port_or_base)
538       {
539       case 1: com_port_or_base = 0x3f8;
540               if (com_irq == -1)
541                 com_irq = 4;
542               break;
543       case 2: com_port_or_base = 0x2f8;
544               if (com_irq == -1)
545                 com_irq = 3;
546               break;
547       case 3: com_port_or_base = 0x3e8; break;
548       case 4: com_port_or_base = 0x2e8; break;
549       }
550
551     unsigned baudrate = 115200;
552     static L4::Io_register_block_port uart_regs(com_port_or_base);
553     static L4::Uart_16550 _uart(L4::Uart_16550::Base_rate_x86);
554     if (   !_uart.startup(&uart_regs)
555         || !_uart.change_mode(L4::Uart_16550::MODE_8N1, baudrate))
556       {
557         printf("Could not find or enable UART\n");
558         base_critical_leave();
559         return 1;
560       }
561
562     du->set_uart2(&_uart);
563
564     kuart.access_type  = L4_kernel_options::Uart_type_ioport;
565     kuart.irqno        = com_irq;
566     kuart.base_address = com_port_or_base;
567     kuart.baud         = baudrate;
568     kuart_flags       |=   L4_kernel_options::F_uart_base
569                          | L4_kernel_options::F_uart_baud;
570     if (com_irq != -1)
571       kuart_flags |= L4_kernel_options::F_uart_irq;
572
573     base_critical_leave();
574     return 0;
575   }
576
577   void init()
578   {
579     const char *s;
580     int comport = -1;
581     int comirq = -1;
582     int pci = 0;
583
584     static Uart_vga _vga;
585     static Dual_uart du(&_vga);
586     set_stdio_uart(&du);
587
588     if ((s = check_arg(x86_bootloader_mbi, "-comirq")))
589       {
590         s += 8;
591         comirq = strtoul(s, 0, 0);
592       }
593
594     if ((s = check_arg(x86_bootloader_mbi, "-comport")))
595       {
596         s += 9;
597         if ((pci = !strncmp(s, "pci:", 4)))
598           s += 4;
599
600         comport = strtoul(s, 0, 0);
601       }
602
603     if (!check_arg(x86_bootloader_mbi, "-noserial"))
604       {
605         if (pci)
606           {
607             if (unsigned long port = search_pci_serial_devs(comport))
608               comport = port;
609             else
610               comport = -1;
611
612             printf("PCI IO port = %x\n", comport);
613           }
614
615         if (comport == -1)
616           comport = 1;
617
618         if (init_uart(comport, comirq, &du))
619           printf("UART init failed\n");
620       }
621   }
622
623   void setup_memory_map(l4util_mb_info_t *mbi,
624                         Region_list *ram, Region_list *regions)
625   {
626     if (!(mbi->flags & L4UTIL_MB_MEM_MAP))
627       {
628         assert(mbi->flags & L4UTIL_MB_MEMORY);
629         ram->add(Region::n(0, (mbi->mem_upper + 1024) << 10, ".ram",
630                  Region::Ram));
631       }
632     else
633       {
634         l4util_mb_addr_range_t *mmap;
635         l4util_mb_for_each_mmap_entry(mmap, mbi)
636           {
637             unsigned long long start = (unsigned long long)mmap->addr;
638             unsigned long long end = (unsigned long long)mmap->addr + mmap->size;
639
640             switch (mmap->type)
641               {
642               case 1:
643                 ram->add(Region::n(start, end, ".ram", Region::Ram));
644                 break;
645               case 2:
646               case 3:
647               case 4:
648                 regions->add(Region::n(start, end, ".BIOS", Region::Arch, mmap->type));
649                 break;
650               case 5:
651                 regions->add(Region::n(start, end, ".BIOS", Region::No_mem));
652                 break;
653               default:
654                 break;
655               }
656           }
657       }
658
659     regions->add(Region::n(0, 0x1000, ".BIOS", Region::Arch, 0));
660
661
662     // Quirks
663
664     // Fix EBDA in conventional memory
665     unsigned long p = *(l4_uint16_t *)0x40e << 4;
666
667     if (p > 0x400)
668       {
669         unsigned long e = p + 1024;
670         Region *r = ram->find(Region(p, e - 1));
671         if (r)
672           {
673             if (e - 1 < r->end())
674               ram->add(Region::n(e, r->end(), ".ram", Region::Ram));
675             r->end(p);
676           }
677       }
678   }
679 };
680 }
681
682 REGISTER_PLATFORM(Platform_x86);
683