27c5d757295f2303876e8e081cb61b8bfb2205d0
[polintos/scott/priv.git] / kernel / io / console / vga.cc
1 // io/console/vga.cc -- Kernel debugging console for standard VGA text mode
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following conditions:
11 // 
12 //     * Redistributions of source code must retain the above copyright notice,
13 //       this list of conditions and the following disclaimers.
14 // 
15 //     * Redistributions in binary form must reproduce the above copyright notice,
16 //       this list of conditions and the following disclaimers in the
17 //       documentation and/or other materials provided with the distribution.
18 // 
19 //     * The names of the Software's authors and/or contributors
20 //       may not be used to endorse or promote products derived from
21 //       this Software without specific prior written permission.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
25 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
29 // SOFTWARE.
30
31 #include <System/IO.h>
32 #include <kern/kernel.h>
33 #include <kern/console.h>
34 #include <kern/notifier.h>
35 #include <kern/io.h>
36 #include <kern/libc.h>
37 #include <kern/addrs.h>
38 #include <util/lock.h>
39
40 void vga_write(char *str);
41
42 namespace IO {
43 namespace Console {
44         class VGA : public Console {
45                 Lock::SpinLock lock;
46         
47                 u16 *mem_base;
48                 unsigned long io_base;
49                 
50                 int line_width;
51                 int lines;
52                 int pos;
53                 
54                 void putc(int ch, int attr)
55                 {
56                         if (ch == '\n') {
57                                 pos += line_width;
58                                 pos -= pos % line_width;
59                         } else if (ch == '\r') {
60                                 pos -= pos % line_width;
61                         } else {
62                                 // Use ll_swap_le16 rather than store_le16 in the hopes
63                                 // that the compiler can simply change the shifts around
64                                 // rather than assembling it one way and then ll_swapping.
65                                 //
66                                 // It's done as one atomic store rather than two single
67                                 // byte writes (which would eliminate endianness concerns)
68                                 // to eliminate the possibility of a race where the video
69                                 // card reads a character before its attribute has been
70                                 // written.
71                                 
72                                 mem_base[pos++] = ll_swap_le16(ch | (attr << 8));
73                         }
74                         
75                         if (pos >= lines * line_width) {
76                                 pos -= line_width;
77                                 
78                                 // FIXME: until memcpy is more than a stupid byte-at-a-time
79                                 // loop, this could race with the video card.
80                                 
81                                 memcpy(mem_base, mem_base + line_width,
82                                        line_width * (lines - 1) * sizeof(u16));
83                                 
84                                 for (int i = 0; i < line_width; i++)
85                                         mem_base[pos + i] = ll_swap_le16(' ' | (7 << 8));
86                         }
87                 }
88                 
89                 void get_cursor()
90                 {
91                         ll_out_8(0x3d4, 14);
92                         pos = (u16)ll_in_8(0x3d5) << 8;
93                         ll_out_8(0x3d4, 15);
94                         pos |= ll_in_8(0x3d5);
95                 }
96
97                 void set_cursor()
98                 {
99                         ll_out_8(0x3d4, 14);
100                         ll_out_8(0x3d5, pos >> 8);
101                         ll_out_8(0x3d4, 15);
102                         ll_out_8(0x3d5, pos & 0xff);
103                 }
104                 
105         public:
106                 #include <servers/io/console/vga/IO/Console/VGA.h>
107                 
108                 VGA()
109                 {
110                         init_iface();
111
112                         // FIXME: should be PCI mem space, not phys mem.
113                         mem_base = (u16 *)Arch::phys_to_kvirt(0xb8000);
114                         io_base = 0x3d0;
115                         
116                         // FIXME: detect
117                         line_width = 80;
118                         lines = 25;
119
120                         primary_console = this;
121                 }
122                 
123                 void write(Array<octet> buf, u64 *LEN)
124                 {
125                         if (!valid_size_t(*LEN))
126                                 /* throw something */
127                                 return;
128                         
129                         size_t len = *LEN;
130                         
131                         if (len > buf.count)
132                                 /* throw something */
133                                 return;
134                 
135                         Lock::AutoSpinLockRecIRQ autolock(lock);
136                         get_cursor();
137                         
138                         for (size_t i = 0; i < len; i++)
139                                 putc(buf.ptr[i], 7);
140
141                         set_cursor();                   
142                 }
143                 
144                 void write_async(Array<octet> buf, u64 len, Notifier notifier)
145                 {
146                         write(buf, &len);
147                         io_notify(notifier, len, io_result::Success);
148                 }
149         };
150         
151         // Use a static constructor, so that it can be used before
152         // memory management is up and running.
153         
154         VGA vga;
155 }
156 }
157
158 #include <servers/io/console/vga/footer.cc>