]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/bootstrap/server/src/platform/x86_pc.cc
428898aeff5ca6b600f50058f511eea65338f87e
[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 Lackorznynski <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   if (num_membars <= 1 && num_iobars == 1)
280     {
281       board->first_bar = first_port;
282       board->num_ports = board->bars[first_port].len / 8;
283       board->port_per_bar = false;
284       pci_enable_io(bus, dev, subdev);
285       return 1;
286     }
287
288
289   board->num_ports = 0;
290   board->first_bar = -1;
291
292   for (int bar = 0; bar < NUM_BARS; ++bar)
293     {
294       if (board->bars[bar].type == Resource::IO_BAR && board->bars[bar].len == 8
295           && (board->first_bar == -1
296               || (board->first_bar + board->num_ports) == bar))
297         {
298           ++board->num_ports;
299           if (board->first_bar == -1)
300             board->first_bar = bar;
301         }
302     }
303
304   board->port_per_bar = true;
305   return board->num_ports;
306
307 #if 0
308
309       // for now we only take IO-BARs of size 8
310       if (v & 1)
311         {
312
313           if (!scan_only && !dev_enabled)
314             {
315               pci_enable_io(bus, dev, subdev);
316               dev_enabled = true;
317             }
318
319           if (scan_only)
320             printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
321
322           if (s == 3)
323             {
324               if (scan_only)
325                 printf("   Potential serial port\n");
326               else
327                 return v & ~3;
328             }
329         }
330       else
331         if (scan_only)
332           printf("BAR%d: %08x (sz=%d)\n", bar, v & ~0xf, 1 << s);
333     }
334   return 0;
335 #endif
336 }
337
338 static unsigned long
339 _search_pci_serial_devs(Serial_board *board, unsigned look_for_subclass,
340                         bool print)
341 {
342   l4_umword_t bus, buses, dev;
343
344   for (bus=0, buses=20; bus<buses; bus++)
345     {
346       for (dev = 0; dev < 32; dev++)
347         {
348           unsigned char hdr_type = pci_read(bus, dev, 0, 0xe, 8);
349           l4_umword_t subdevs = (hdr_type & 0x80) ? 8 : 1;
350
351           for (l4_umword_t subdev = 0; subdev < subdevs; subdev++)
352             {
353               unsigned vendor = pci_read(bus, dev, subdev, 0, 16);
354               unsigned device = pci_read(bus, dev, subdev, 2, 16);
355
356               if ((vendor == 0xffff && device == 0xffff) ||
357                   (device == 0x0000 && device == 0x0000))
358                 break;
359
360               unsigned classcode = pci_read(bus, dev, subdev, 0x0b, 8);
361               unsigned subclass  = pci_read(bus, dev, subdev, 0x0a, 8);
362
363               if (classcode == 0x06 && subclass == 0x04)
364                 buses++;
365
366               unsigned prog = pci_read(bus, dev, subdev, 9, 8);
367
368               if (print)
369                 printf("%02lx:%02lx.%1lx Class %02x.%02x Prog %02x: %04x:%04x\n",
370                        bus, dev, subdev, classcode, subclass, prog, vendor, device);
371
372               if (classcode == 7 && subclass == look_for_subclass)
373                 if (unsigned long port = pci_handle_serial_dev(bus, dev,
374                                                                subdev, print, board))
375                   return port;
376             }
377         }
378     }
379   return 0;
380 }
381
382 static unsigned long
383 search_pci_serial_devs(int port_idx)
384 {
385   Serial_board board;
386   if (!_search_pci_serial_devs(&board, 0, true)) // classes should be 7:0
387     if (!_search_pci_serial_devs(&board, 0x80, false)) // but sometimes it's 7:80
388       return 0;
389
390   return board.get_port(port_idx);
391 }
392
393 class Uart_vga : public L4::Uart
394 {
395 public:
396   Uart_vga()
397   { }
398
399   bool startup(L4::Io_register_block const *)
400   {
401     vga_init();
402     return true;
403   }
404
405   ~Uart_vga() {}
406   void shutdown() {}
407   bool enable_rx_irq(bool) { return false; }
408   bool enable_tx_irq(bool) { return false; }
409   bool change_mode(Transfer_mode, Baud_rate) { return true; }
410   int get_char(bool blocking) const
411   {
412     int c;
413     do
414       c = raw_keyboard_getscancode();
415     while (blocking && c == -1);
416     return c;
417   }
418
419   int char_avail() const
420   {
421     return raw_keyboard_getscancode() != -1;
422   }
423
424   int write(char const *s, unsigned long count) const
425   {
426     unsigned long c = count;
427     while (c)
428       {
429         if (*s == 10)
430           vga_putchar(13);
431         vga_putchar(*s++);
432         --c;
433       }
434     return count;
435   }
436 };
437
438 class Dual_uart : public L4::Uart
439 {
440 private:
441   L4::Uart *_u1, *_u2;
442
443 public:
444   Dual_uart(L4::Uart *u1)
445   : _u1(u1), _u2(0)
446   {}
447
448   void set_uart2(L4::Uart *u2)
449   {
450     _u2 = u2;
451   }
452
453   bool startup(L4::Io_register_block const *)
454   {
455     return true;
456   }
457
458   void shutdown()
459   {
460     _u1->shutdown();
461     if (_u2)
462       _u2->shutdown();
463   }
464
465   ~Dual_uart() {}
466 #if 0
467   bool enable_rx_irq(bool e)
468   {
469     bool r1 = _u1->enable_rx_irq(e);
470     bool r2 = _u2 ? _u2->enable_rx_irq(e) : false;
471     return r1 && r2;
472   }
473
474   bool enable_tx_irq(bool e)
475   {
476     bool r1 = _u1->enable_tx_irq(e);
477     bool r2 = _u2 ? _u2->enable_tx_irq(e) : false;
478     return r1 && r2;
479   }
480 #endif
481
482   bool change_mode(Transfer_mode m, Baud_rate r)
483   {
484     bool r1 = _u1->change_mode(m, r);
485     bool r2 = _u2 ? _u2->change_mode(m, r) : false;
486     return r1 && r2;
487   }
488
489   int char_avail() const
490   {
491     return _u1->char_avail() || (_u2 && _u2->char_avail());
492   }
493
494   int get_char(bool blocking) const
495   {
496     int c;
497     do
498       {
499         c = _u1->get_char(false);
500         if (c == -1 && _u2)
501           c = _u2->get_char(false);
502       }
503     while (blocking && c == -1);
504     return c;
505   }
506
507   int write(char const *s, unsigned long count) const
508   {
509     int r = _u1->write(s, count);
510     if (_u2)
511       _u2->write(s, count);
512     return r;
513   }
514
515 };
516
517 l4util_mb_info_t *x86_bootloader_mbi;
518
519 namespace {
520
521 class Platform_x86 : public Platform_base
522 {
523 public:
524   bool probe() { return true; }
525
526   int init_uart(int com_port_or_base, int com_irq, Dual_uart *du)
527   {
528     base_critical_enter();
529
530     switch (com_port_or_base)
531       {
532       case 1: com_port_or_base = 0x3f8;
533               if (com_irq == -1)
534                 com_irq = 4;
535               break;
536       case 2: com_port_or_base = 0x2f8;
537               if (com_irq == -1)
538                 com_irq = 3;
539               break;
540       case 3: com_port_or_base = 0x3e8; break;
541       case 4: com_port_or_base = 0x2e8; break;
542       }
543
544     unsigned baudrate = 115200;
545     static L4::Io_register_block_port uart_regs(com_port_or_base);
546     static L4::Uart_16550 _uart(L4::Uart_16550::Base_rate_x86);
547     if (   !_uart.startup(&uart_regs)
548         || !_uart.change_mode(L4::Uart_16550::MODE_8N1, baudrate))
549       {
550         printf("Could not find or enable UART\n");
551         base_critical_leave();
552         return 1;
553       }
554
555     du->set_uart2(&_uart);
556
557     kuart.access_type  = L4_kernel_options::Uart_type_ioport;
558     kuart.irqno        = com_irq;
559     kuart.base_address = com_port_or_base;
560     kuart.baud         = baudrate;
561     kuart_flags       |=   L4_kernel_options::F_uart_base
562                          | L4_kernel_options::F_uart_baud;
563     if (com_irq != -1)
564       kuart_flags |= L4_kernel_options::F_uart_irq;
565
566     base_critical_leave();
567     return 0;
568   }
569
570   void init()
571   {
572     const char *s;
573     int comport = -1;
574     int comirq = -1;
575     int pci = 0;
576
577     static Uart_vga _vga;
578     static Dual_uart du(&_vga);
579     set_stdio_uart(&du);
580
581     if ((s = check_arg(x86_bootloader_mbi, "-comirq")))
582       {
583         s += 8;
584         comirq = strtoul(s, 0, 0);
585       }
586
587     if ((s = check_arg(x86_bootloader_mbi, "-comport")))
588       {
589         s += 9;
590         if ((pci = !strncmp(s, "pci:", 4)))
591           s += 4;
592
593         comport = strtoul(s, 0, 0);
594       }
595
596     if (!check_arg(x86_bootloader_mbi, "-noserial"))
597       {
598         if (pci)
599           {
600             if (unsigned long port = search_pci_serial_devs(comport))
601               comport = port;
602             else
603               comport = -1;
604
605             printf("PCI IO port = %x\n", comport);
606           }
607
608         if (comport == -1)
609           comport = 1;
610
611         if (init_uart(comport, comirq, &du))
612           printf("UART init failed\n");
613       }
614   }
615
616   void setup_memory_map(l4util_mb_info_t *mbi,
617                         Region_list *ram, Region_list *regions)
618   {
619     if (!(mbi->flags & L4UTIL_MB_MEM_MAP))
620       {
621         assert(mbi->flags & L4UTIL_MB_MEMORY);
622         ram->add(Region::n(0, (mbi->mem_upper + 1024) << 10, ".ram",
623                  Region::Ram));
624       }
625     else
626       {
627         l4util_mb_addr_range_t *mmap;
628         l4util_mb_for_each_mmap_entry(mmap, mbi)
629           {
630             unsigned long long start = (unsigned long long)mmap->addr;
631             unsigned long long end = (unsigned long long)mmap->addr + mmap->size;
632
633             switch (mmap->type)
634               {
635               case 1:
636                 ram->add(Region::n(start, end, ".ram", Region::Ram));
637                 break;
638               case 2:
639               case 3:
640               case 4:
641                 regions->add(Region::n(start, end, ".BIOS", Region::Arch, mmap->type));
642                 break;
643               case 5:
644                 regions->add(Region::n(start, end, ".BIOS", Region::No_mem));
645                 break;
646               default:
647                 break;
648               }
649           }
650       }
651
652     regions->add(Region::n(0, 0x1000, ".BIOS", Region::Arch, 0));
653
654
655     // Quirks
656
657     // Fix EBDA in conventional memory
658     unsigned long p = *(l4_uint16_t *)0x40e << 4;
659
660     if (p > 0x400)
661       {
662         unsigned long e = p + 1024;
663         Region *r = ram->find(Region(p, e - 1));
664         if (r)
665           {
666             if (e - 1 < r->end())
667               ram->add(Region::n(e, r->end(), ".ram", Region::Ram));
668             r->end(p);
669           }
670       }
671   }
672 };
673 }
674
675 REGISTER_PLATFORM(Platform_x86);
676