]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/bootstrap/server/src/support_x86_pc.cc
update
[l4.git] / l4 / pkg / bootstrap / server / src / support_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
20 namespace L4
21 {
22   class Uart_x86 : public Uart
23   {
24   private:
25     unsigned long _base;
26
27     inline unsigned long rd(unsigned long reg) const;
28     inline void wr(unsigned long reg, unsigned long val) const;
29
30   public:
31     Uart_x86(int rx_irq, int tx_irq)
32       : Uart(rx_irq, tx_irq), _base(~0UL) {}
33     bool startup(unsigned long base);
34     void shutdown();
35     bool enable_rx_irq(bool enable = true);
36     bool enable_tx_irq(bool enable = true);
37     bool change_mode(Transfer_mode m, Baud_rate r);
38     int get_char(bool blocking = true) const;
39     int char_avail() const;
40     inline void out_char(char c) const;
41     int write(char const *s, unsigned long count) const;
42   };
43 };
44
45
46 #include <string.h>
47 #include "base_critical.h"
48 #include "ARCH-x86/serial.h"
49 #include <l4/util/cpu.h>
50 #include <l4/util/port_io.h>
51 #include <assert.h>
52
53 /** VGA console output */
54
55 static void
56 vga_init()
57 {
58   /* Reset any scrolling */
59   l4util_out32(0xc, 0x3d4);
60   l4util_out32(0, 0x3d5);
61   l4util_out32(0xd, 0x3d4);
62   l4util_out32(0, 0x3d5);
63 }
64
65 static void
66 vga_putchar(unsigned char c)
67 {
68   static int ofs = -1, esc, esc_val, attr = 0x07;
69   unsigned char *vidbase = (unsigned char*)0xb8000;
70
71   base_critical_enter();
72
73   if (ofs < 0)
74     {
75       /* Called for the first time - initialize.  */
76       ofs = 80*2*24;
77       vga_putchar('\n');
78     }
79
80   switch (esc)
81     {
82     case 1:
83       if (c == '[')
84         {
85           esc++;
86           goto done;
87         }
88       esc = 0;
89       break;
90
91     case 2:
92       if (c >= '0' && c <= '9')
93         {
94           esc_val = 10*esc_val + c - '0';
95           goto done;
96         }
97       if (c == 'm')
98         {
99           attr = esc_val ? 0x0f : 0x07;
100           goto done;
101         }
102       esc = 0;
103       break;
104     }
105
106   switch (c)
107     {
108     case '\n':
109       memmove(vidbase, vidbase+80*2, 80*2*24);
110       memset(vidbase+80*2*24, 0, 80*2);
111       /* fall through... */
112     case '\r':
113       ofs = 0;
114       break;
115
116     case '\t':
117       ofs = (ofs + 8) & ~7;
118       break;
119
120     case '\033':
121       esc = 1;
122       esc_val = 0;
123       break;
124
125     default:
126       /* Wrap if we reach the end of a line.  */
127       if (ofs >= 80)
128         vga_putchar('\n');
129
130       /* Stuff the character into the video buffer. */
131         {
132           volatile unsigned char *p = vidbase + 80*2*24 + ofs*2;
133           p[0] = c;
134           p[1] = attr;
135           ofs++;
136         }
137       break;
138     }
139
140 done:
141   base_critical_leave();
142 }
143
144 /** Poor man's getchar, only returns raw scan code. We don't need to know
145  * _which_ key was pressed, we only want to know _if_ a key was pressed. */
146 static int
147 raw_keyboard_getscancode(void)
148 {
149   unsigned status, scan_code;
150
151   base_critical_enter();
152
153   l4util_cpu_pause();
154
155   /* Wait until a scan code is ready and read it. */
156   status = l4util_in8(0x64);
157   if ((status & 0x01) == 0)
158     {
159       base_critical_leave();
160       return -1;
161     }
162   scan_code = l4util_in8(0x60);
163
164   /* Drop mouse events */
165   if ((status & 0x20) != 0)
166     {
167       base_critical_leave();
168       return -1;
169     }
170
171   base_critical_leave();
172   return scan_code;
173 }
174
175 namespace L4
176 {
177   bool Uart_x86::startup(unsigned long /*base*/)
178   // real uart init will be made by startup.cc if told by cmdline
179   { vga_init(); return true; }
180
181   void Uart_x86::shutdown() {}
182   bool Uart_x86::enable_rx_irq(bool) { return true; }
183   bool Uart_x86::enable_tx_irq(bool) { return false; }
184   bool Uart_x86::change_mode(Transfer_mode, Baud_rate) { return false; }
185
186   int Uart_x86::get_char(bool blocking) const
187   {
188     int c;
189     do {
190       c = com_cons_try_getchar();
191       if (c == -1)
192         c = raw_keyboard_getscancode();
193       l4util_cpu_pause();
194     } while (c == -1 && blocking);
195
196     return c;
197   }
198
199   int Uart_x86::char_avail() const
200   {
201     return com_cons_char_avail();
202   }
203
204   void Uart_x86::out_char(char c) const
205   {
206     vga_putchar(c);      // vga out
207     com_cons_putchar(c); // serial out
208   }
209
210   int Uart_x86::write(char const *s, unsigned long count) const
211   {
212     unsigned long c = count;
213     while (c)
214       {
215         if (*s == 10)
216           out_char(13);
217         out_char(*s++);
218         --c;
219       }
220     return count;
221   }
222 };
223
224
225 static inline l4_uint32_t
226 pci_conf_addr(l4_uint32_t bus, l4_uint32_t dev, l4_uint32_t fn, l4_uint32_t reg)
227 { return 0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
228
229 static l4_uint32_t pci_read(unsigned char bus, l4_uint32_t dev,
230                             l4_uint32_t fn, l4_uint32_t reg,
231                             unsigned char width)
232 {
233   l4util_out32(pci_conf_addr(bus, dev, fn, reg), 0xcf8);
234
235   switch (width)
236     {
237     case 8:  return l4util_in8(0xcfc + (reg & 3));
238     case 16: return l4util_in16((0xcfc + (reg & 3)) & ~1UL);
239     case 32: return l4util_in32(0xcfc);
240     }
241   return 0;
242 }
243
244 static void pci_write(unsigned char bus, l4_uint32_t dev,
245                       l4_uint32_t fn, l4_uint32_t reg,
246                       l4_uint32_t val, unsigned char width)
247 {
248   l4util_out32(pci_conf_addr(bus, dev, fn, reg), 0xcf8);
249
250   switch (width)
251     {
252     case 8:  l4util_out8(val, 0xcfc + (reg & 3)); break;
253     case 16: l4util_out16(val, (0xcfc + (reg & 3)) & ~1UL); break;
254     case 32: l4util_out32(val, 0xcfc); break;
255     }
256 }
257
258 static void pci_enable_io(unsigned char bus, l4_uint32_t dev,
259                           l4_uint32_t fn)
260 {
261   unsigned cmd = pci_read(bus, dev, fn, 4, 16);
262   pci_write(bus, dev, fn, 4, cmd | 1, 16);
263 }
264
265 #include <stdio.h>
266
267 namespace {
268
269 struct Resource
270 {
271   enum Type { NO_BAR, IO_BAR, MEM_BAR };
272   Type type;
273   unsigned long base;
274   unsigned long len;
275   Resource() : type(NO_BAR) {}
276 };
277
278 enum { NUM_BARS = 6 };
279
280 struct Serial_board
281 {
282   int num_ports;
283   int first_bar;
284   bool port_per_bar;
285   Resource bars[NUM_BARS];
286
287   unsigned long get_port(int idx)
288   {
289     if (idx >= num_ports)
290       return 0;
291
292     if (port_per_bar)
293       return bars[first_bar + idx].base;
294
295     return bars[first_bar].base + 8 * idx;
296   }
297 };
298
299
300 }
301
302 static
303 int pci_handle_serial_dev(unsigned char bus, l4_uint32_t dev,
304                           l4_uint32_t subdev, bool scan_only,
305                           Serial_board *board)
306 {
307 #if 0
308   bool dev_enabled = false;
309 #endif
310
311
312   // read bars
313   int num_iobars = 0;
314   int num_membars = 0;
315   int first_port = -1;
316   for (int bar = 0; bar < NUM_BARS; ++bar)
317     {
318       int a = 0x10 + bar * 4;
319
320       unsigned v = pci_read(bus, dev, subdev, a, 32);
321       pci_write(bus, dev, subdev, a, ~0U, 32);
322       unsigned x = pci_read(bus, dev, subdev, a, 32);
323       pci_write(bus, dev, subdev, a, v, 32);
324
325       if (!v)
326         continue;
327
328       int s;
329       for (s = 2; s < 32; ++s)
330         if ((x >> s) & 1)
331           break;
332
333       board->bars[bar].base = v & ~3UL;
334       board->bars[bar].len = 1 << s;
335       board->bars[bar].type = (v & 1) ? Resource::IO_BAR : Resource::MEM_BAR;
336
337       if (scan_only)
338         printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
339
340       switch (board->bars[bar].type)
341         {
342         case Resource::IO_BAR:
343           ++num_iobars;
344           if (first_port == -1)
345             first_port = bar;
346           break;
347         case Resource::MEM_BAR:
348           ++num_membars;
349           break;
350         default:
351           break;
352         }
353     }
354
355   if (num_membars <= 1 && num_iobars == 1)
356     {
357       board->first_bar = first_port;
358       board->num_ports = board->bars[first_port].len / 8;
359       board->port_per_bar = false;
360       pci_enable_io(bus, dev, subdev);
361       return 1;
362     }
363
364
365   board->num_ports = 0;
366   board->first_bar = -1;
367
368   for (int bar = 0; bar < NUM_BARS; ++bar)
369     {
370       if (board->bars[bar].type == Resource::IO_BAR && board->bars[bar].len == 8
371           && (board->first_bar == -1
372               || (board->first_bar + board->num_ports) == bar))
373         {
374           ++board->num_ports;
375           if (board->first_bar == -1)
376             board->first_bar = bar;
377         }
378     }
379
380   board->port_per_bar = true;
381   return board->num_ports;
382
383 #if 0
384
385       // for now we only take IO-BARs of size 8
386       if (v & 1)
387         {
388
389           if (!scan_only && !dev_enabled)
390             {
391               pci_enable_io(bus, dev, subdev);
392               dev_enabled = true;
393             }
394
395           if (scan_only)
396             printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
397
398           if (s == 3)
399             {
400               if (scan_only)
401                 printf("   Potential serial port\n");
402               else
403                 return v & ~3;
404             }
405         }
406       else
407         if (scan_only)
408           printf("BAR%d: %08x (sz=%d)\n", bar, v & ~0xf, 1 << s);
409     }
410   return 0;
411 #endif
412 }
413
414 static unsigned long _search_pci_serial_devs(Serial_board *board, bool scan_only)
415 {
416   l4_umword_t bus, buses, dev;
417
418   for (bus=0, buses=20; bus<buses; bus++)
419     {
420       for (dev = 0; dev < 32; dev++)
421         {
422           unsigned char hdr_type = pci_read(bus, dev, 0, 0xe, 8);
423           l4_umword_t subdevs = (hdr_type & 0x80) ? 8 : 1;
424
425           for (l4_umword_t subdev = 0; subdev < subdevs; subdev++)
426             {
427               unsigned vendor = pci_read(bus, dev, subdev, 0, 16);
428               unsigned device = pci_read(bus, dev, subdev, 2, 16);
429
430               if ((vendor == 0xffff && device == 0xffff) ||
431                   (device == 0x0000 && device == 0x0000))
432                 break;
433
434               unsigned classcode = pci_read(bus, dev, subdev, 0x0b, 8);
435               unsigned subclass  = pci_read(bus, dev, subdev, 0x0a, 8);
436
437               if (classcode == 0x06 && subclass == 0x04)
438                 buses++;
439
440               unsigned prog = pci_read(bus, dev, subdev, 9, 8);
441
442               if (scan_only)
443                 printf("%02lx:%02lx.%1lx Class %02x.%02x Prog %02x: %04x:%04x\n",
444                        bus, dev, subdev, classcode, subclass, prog, vendor, device);
445
446               if (classcode == 7 && subclass == 0)
447                 if (unsigned long port = pci_handle_serial_dev(bus, dev,
448                                                                subdev, scan_only, board))
449                   return port;
450             }
451         }
452     }
453   return 0;
454 }
455
456 unsigned long search_pci_serial_devs(int port_idx, bool scan_only)
457 {
458   Serial_board board;
459   if (!_search_pci_serial_devs(&board, scan_only))
460     return 0;
461
462   return board.get_port(port_idx);
463 }
464
465 namespace {
466
467 class Platform_x86 : public Platform_base
468 {
469 public:
470   bool probe() { return true; }
471   void init()
472   {
473     // this is just a wrapper around serial.c
474     // if you think this could be done better you're right...
475     static L4::Uart_x86 _uart(1,1);
476     _uart.startup(0);
477     set_stdio_uart(&_uart);
478   }
479
480   void setup_memory_map(l4util_mb_info_t *mbi,
481                         Region_list *ram, Region_list *regions)
482   {
483     if (!(mbi->flags & L4UTIL_MB_MEM_MAP))
484       {
485         assert(mbi->flags & L4UTIL_MB_MEMORY);
486         ram->add(Region::n(0, (mbi->mem_upper + 1024) << 10, ".ram",
487                   Region::Ram));
488       }
489     else
490       {
491         l4util_mb_addr_range_t *mmap;
492         l4util_mb_for_each_mmap_entry(mmap, mbi)
493           {
494             unsigned long long start = (unsigned long long)mmap->addr;
495             unsigned long long end = (unsigned long long)mmap->addr + mmap->size;
496
497             switch (mmap->type)
498               {
499               case 1:
500                 ram->add(Region::n(start, end, ".ram", Region::Ram));
501                 break;
502               case 2:
503               case 3:
504               case 4:
505                 regions->add(Region::n(start, end, ".BIOS", Region::Arch, mmap->type));
506                 break;
507               case 5:
508                 regions->add(Region::n(start, end, ".BIOS", Region::No_mem));
509                 break;
510               default:
511                 break;
512               }
513           }
514       }
515
516     regions->add(Region::n(0, 0x1000, ".BIOS", Region::Arch, 0));
517   }
518 };
519 }
520
521 REGISTER_PLATFORM(Platform_x86);
522