]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/bootstrap/server/src/support_x86_pc.cc
fedfbddce8ee14db68a3186dc201b3518caa8a95
[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 static bool pci_handle_serial_dev(unsigned char bus, l4_uint32_t dev,
268                                   l4_uint32_t subdev, bool scan_only)
269 {
270   bool dev_enabled = false;
271
272   // read bars
273   for (int bar = 0; bar < 6; ++bar)
274     {
275       int a = 0x10 + bar * 4;
276
277       unsigned v = pci_read(bus, dev, subdev, a, 32);
278       pci_write(bus, dev, subdev, a, ~0U, 32);
279       unsigned x = pci_read(bus, dev, subdev, a, 32);
280
281       if (!v)
282         continue;
283
284       int s;
285       for (s = 2; s < 32; ++s)
286         if ((x >> s) & 1)
287           break;
288
289       // for now we only take IO-BARs of size 8
290       if (v & 1)
291         {
292
293           if (!scan_only && !dev_enabled)
294             {
295               pci_enable_io(bus, dev, subdev);
296               dev_enabled = true;
297             }
298
299           if (scan_only)
300             printf("BAR%d: %04x (sz=%d)\n", bar, v & ~3, 1 << s);
301
302           if (s == 3)
303             {
304               if (scan_only)
305                 printf("   Potential serial port\n");
306               else
307                 return v & ~3;
308             }
309         }
310       else
311         if (scan_only)
312           printf("BAR%d: %08x (sz=%d)\n", bar, v & ~0xf, 1 << s);
313     }
314   return false;
315 }
316
317 unsigned long search_pci_serial_devs(bool scan_only)
318 {
319   l4_umword_t bus, buses, dev;
320
321   for (bus=0, buses=1; bus<buses; bus++)
322     {
323       for (dev = 0; dev < 32; dev++)
324         {
325           unsigned char hdr_type = pci_read(bus, dev, 0, 0xe, 8);
326           l4_umword_t subdevs = (hdr_type & 0x80) ? 8 : 1;
327
328           for (l4_umword_t subdev = 0; subdev < subdevs; subdev++)
329             {
330               unsigned vendor = pci_read(bus, dev, subdev, 0, 16);
331               unsigned device = pci_read(bus, dev, subdev, 2, 16);
332
333               if ((vendor == 0xffff && device == 0xffff) ||
334                   (device == 0x0000 && device == 0x0000))
335                 break;
336
337               unsigned classcode = pci_read(bus, dev, subdev, 0x0b, 8);
338               unsigned subclass  = pci_read(bus, dev, subdev, 0x0a, 8);
339
340               if (classcode == 0x06 && subclass == 0x04)
341                 buses++;
342
343               unsigned prog = pci_read(bus, dev, subdev, 9, 8);
344
345               if (scan_only)
346                 printf("%02lx:%02lx.%1lx Class %02x.%02x Prog %02x: %04x:%04x\n",
347                        bus, dev, subdev, classcode, subclass, prog, vendor, device);
348
349               if (classcode == 7 && subclass == 0)
350                 if (unsigned long port = pci_handle_serial_dev(bus, dev,
351                                                                subdev, scan_only))
352                   return port;
353             }
354         }
355     }
356   return 0;
357 }
358
359 namespace {
360
361 class Platform_x86 : public Platform_base
362 {
363 public:
364   bool probe() { return true; }
365   void init()
366   {
367     // this is just a wrapper around serial.c
368     // if you think this could be done better you're right...
369     static L4::Uart_x86 _uart(1,1);
370     _uart.startup(0);
371     set_stdio_uart(&_uart);
372   }
373
374   void setup_memory_map(l4util_mb_info_t *mbi,
375                         Region_list *ram, Region_list *regions)
376   {
377     if (!(mbi->flags & L4UTIL_MB_MEM_MAP))
378       {
379         assert(mbi->flags & L4UTIL_MB_MEMORY);
380         ram->add(Region::n(0, (mbi->mem_upper + 1024) << 10, ".ram",
381                   Region::Ram));
382       }
383     else
384       {
385         l4util_mb_addr_range_t *mmap;
386         l4util_mb_for_each_mmap_entry(mmap, mbi)
387           {
388             unsigned long long start = (unsigned long long)mmap->addr;
389             unsigned long long end = (unsigned long long)mmap->addr + mmap->size;
390
391             switch (mmap->type)
392               {
393               case 1:
394                 ram->add(Region::n(start, end, ".ram", Region::Ram));
395                 break;
396               case 2:
397               case 3:
398               case 4:
399                 regions->add(Region::n(start, end, ".BIOS", Region::Arch, mmap->type));
400                 break;
401               case 5:
402                 regions->add(Region::n(start, end, ".BIOS", Region::No_mem));
403                 break;
404               default:
405                 break;
406               }
407           }
408       }
409
410     regions->add(Region::n(0, 0x1000, ".BIOS", Region::Arch, 0));
411   }
412 };
413 }
414
415 REGISTER_PLATFORM(Platform_x86);
416