Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


6c82c585dffe3e48f4ecfd58bac894cabe7d9adb
[palacios.git] / palacios / src / devices / cga.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2009, Robert Deloatch <rtdeloatch@gmail.com>
11  * Copyright (c) 2009, Steven Jaconette <stevenjaconette2007@u.northwestern.edu> 
12  * Copyright (c) 2009, The V3VEE Project <http://www.v3vee.org> 
13  * All rights reserved.
14  *
15  * Author: Robdert Deloatch <rtdeloatch@gmail.com>
16  *         Steven Jaconette <stevenjaconette2007@u.northwestern.edu>
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22 #include <palacios/vmm.h>
23 #include <palacios/vmm_dev_mgr.h>
24 #include <palacios/vmm_emulator.h>
25 #include <palacios/vm_guest_mem.h>
26
27 #include <devices/console.h>
28
29
30
31
32 #ifndef DEBUG_CGA
33 #undef PrintDebug
34 #define PrintDebug(fmt, args...)
35 #endif
36
37
38 #define START_ADDR 0xB8000
39 #define END_ADDR 0xC0000
40
41 #define FRAMEBUF_SIZE (END_ADDR - START_ADDR)
42 #define SCREEN_SIZE 4000
43
44 #define BASE_CGA_PORT 0x3B0
45
46 #define NUM_COLS 80
47 #define NUM_ROWS 25
48 #define BYTES_PER_ROW (NUM_COLS * 2)
49 #define BYTES_PER_COL 2
50
51
52 struct video_internal {
53     uint8_t * framebuf;
54
55     // These store the values for unhandled ports, in case of a read op
56     uint8_t port_store[44];
57
58
59     uint8_t crtc_index_reg;          // io port 3D4
60     uint8_t crtc_data_regs[25];      // io port 3D5
61     
62     /* IMPORTANT: These are column offsets _NOT_ byte offsets */
63     uint16_t screen_offset; // relative to the framebuffer
64     uint16_t cursor_offset; // relative to the framebuffer
65     /* ** */
66
67     // updating the screen offset is not atomic, 
68     // so we need a temp variable to hold the partial update
69     uint16_t tmp_screen_offset; 
70     
71
72     uint8_t passthrough;
73
74
75     struct v3_console_ops * ops;
76     void * private_data;
77
78
79
80 };
81
82
83
84
85 static void passthrough_in(uint16_t port, void * src, uint_t length) {
86     switch (length) {
87         case 1:
88             *(uint8_t *)src = v3_inb(port);
89             break;
90         case 2:
91             *(uint16_t *)src = v3_inw(port);
92             break;
93         case 4:
94             *(uint32_t *)src = v3_indw(port);
95             break;
96         default:
97             break;
98     }
99 }
100
101
102 static void passthrough_out(uint16_t port, void * src, uint_t length) {
103     switch (length) {
104         case 1:
105             v3_outb(port, *(uint8_t *)src);
106             break;
107         case 2:
108             v3_outw(port, *(uint16_t *)src);
109             break;
110         case 4:
111             v3_outdw(port, *(uint32_t *)src);
112             break;
113         default:
114             break;
115     }
116 }
117
118 static int video_write_mem(addr_t guest_addr, void * dest, uint_t length, void * priv_data) {
119     struct vm_device * dev = (struct vm_device *)priv_data;
120     struct video_internal * state = (struct video_internal *)dev->private_data;
121     uint_t fb_offset = guest_addr - START_ADDR;
122     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
123     uint_t screen_length;
124
125     PrintDebug("Guest address: %p length = %d, fb_offset=%d, screen_offset=%d\n", 
126                (void *)guest_addr, length, fb_offset, screen_byte_offset);
127
128     if (state->passthrough) {
129         memcpy(state->framebuf + fb_offset, V3_VAddr((void *)guest_addr), length);
130     }
131
132     if ((fb_offset >= screen_byte_offset) && (fb_offset < (screen_byte_offset + SCREEN_SIZE))) {
133         uint_t screen_pos = fb_offset - screen_byte_offset;
134         uint_t x = (screen_pos % BYTES_PER_ROW) / BYTES_PER_COL;
135         uint_t y = screen_pos / BYTES_PER_ROW;
136         PrintDebug("Sending Screen update\n");
137         
138         if (state->ops) {
139             PrintDebug("\tcalling update_screen()\n");
140             
141             /* avoid updates past end of screen */
142             screen_length = SCREEN_SIZE - screen_byte_offset;
143             if (screen_length > length) screen_length = length;
144             state->ops->update_screen(x, y, screen_length, state->private_data);
145         }
146     }
147
148     return length;
149 }
150
151 static int video_read_port(uint16_t port, void * dest, uint_t length, struct vm_device * dev) {
152     struct video_internal * video_state = (struct video_internal *)dev->private_data;
153
154
155     PrintDebug("Video: Read port 0x%x\n", port);
156
157     if (video_state->passthrough) {
158         passthrough_in(port, dest, length);
159     }
160
161     return length;
162 }
163
164
165
166 static int video_write_port(uint16_t port, void * src, uint_t length, struct vm_device * dev) {
167     struct video_internal * video_state = (struct video_internal *)dev->private_data;
168
169
170     PrintDebug("Video: write port 0x%x...\n", port);
171
172     if (video_state->passthrough) {
173         passthrough_out(port, src, length);
174     } 
175
176     return length;
177 }
178
179
180
181 static int crtc_data_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) {
182     struct video_internal * video_state = (struct video_internal *)dev->private_data;
183     uint8_t val = *(uint8_t *)src;
184     uint_t index = video_state->crtc_index_reg;
185
186     if (length != 1) {
187         PrintError("Invalid write length for port 0x%x\n", port);
188         return -1;
189     }
190
191     PrintDebug("Video: write on port 0x%x... (val=0x%x)\n", port, val);
192
193     video_state->crtc_data_regs[index] = val;
194
195     switch (index) {
196         case 0x0c: { // scroll high byte
197             uint16_t tmp_val = val;
198             video_state->tmp_screen_offset = ((tmp_val << 8) & 0xff00);
199             break;
200         }
201         case 0x0d: {  // Scroll low byte
202             int diff = 0;
203
204             video_state->tmp_screen_offset += val;
205             diff = (video_state->tmp_screen_offset - video_state->screen_offset) / NUM_COLS;
206
207             // Update the true offset value
208             video_state->screen_offset = video_state->tmp_screen_offset;
209             video_state->tmp_screen_offset = 0;
210
211             PrintDebug("Scroll lines = %d, new screen offset=%d\n", 
212                        diff, video_state->screen_offset * BYTES_PER_COL);
213
214             if (video_state->ops) {
215                 if (video_state->ops->scroll(diff, video_state->private_data) == -1) {
216                     PrintError("Error sending scroll event\n");
217                     return -1;
218                 }
219             }
220             break;
221         }
222         case 0x0E: {  // Cursor adjustment High byte
223             uint16_t tmp_val = val;
224             video_state->cursor_offset = ((tmp_val << 8) & 0xff00);
225
226             break;
227         }
228         case 0x0F: { // cursor adjustment low byte
229             uint_t x = 0;
230             uint_t y = 0;
231             
232             video_state->cursor_offset += val;
233             
234             x = video_state->cursor_offset % NUM_COLS;
235             y = (video_state->cursor_offset - video_state->screen_offset) / NUM_COLS;
236             
237             PrintDebug("New Cursor Location; X=%d Y=%d\n", x, y);
238             
239             if (video_state->ops) {
240                 if (video_state->ops->update_cursor(x, y, video_state->private_data) == -1) {
241                     PrintError("Error updating cursor\n");
242                     return -1;
243                 }
244             } 
245
246             break;
247         }
248         default:
249             break;
250     }
251
252     if (video_state->passthrough) {
253         passthrough_out(port, src, length);
254     }
255
256     return length;
257 }
258
259
260 static int crtc_index_write(uint16_t port, void * src, uint_t length, struct vm_device * dev) {
261     struct video_internal * video_state = (struct video_internal *)dev->private_data;
262     
263     if (length > 2) {
264         PrintError("Invalid write length for crtc index register port: %d (0x%x)\n",
265                    port, port);
266         return -1;
267     }
268                    
269
270     video_state->crtc_index_reg = *(uint8_t *)src;
271
272     // Only do the passthrough IO for the first byte
273     // the second byte will be done in the data register handler
274     if (video_state->passthrough) {
275         passthrough_out(port, src, 1);
276     }
277
278     if (length == 2) {
279         if (crtc_data_write(port + 1, src + 1, length - 1, dev) != (length - 1)) {
280             PrintError("could not handle implicit crtc data write\n");
281             return -1;
282         }
283     }
284
285     return length;
286 }
287
288
289
290 int v3_cons_get_fb(struct vm_device * frontend_dev, uint8_t * dst, uint_t offset, uint_t length) {
291     struct video_internal * state = (struct video_internal *)frontend_dev->private_data;
292     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
293
294     PrintDebug("Getting framebuffer for screen; framebuf=%p, screen_offset=%d, offset=%d, length=%d\n", 
295                state->framebuf, screen_byte_offset, offset, length);
296
297     memcpy(dst, state->framebuf + screen_byte_offset + offset, length);
298
299     return 0;
300 }
301
302
303
304 static int free_device(struct vm_device * dev) {
305     v3_unhook_mem(dev->vm, START_ADDR);
306     return 0;
307 }
308
309
310
311 static struct v3_device_ops dev_ops = {
312     .free = free_device,
313     .reset = NULL,
314     .start = NULL,
315     .stop = NULL,
316 };
317
318 static int cga_init(struct guest_info * vm, v3_cfg_tree_t * cfg) {
319     struct video_internal * video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
320     addr_t frame_buf_pa = 0;
321     int enable_passthrough = 0;
322     char * name = v3_cfg_val(cfg, "name");
323
324     enable_passthrough = (strcasecmp(v3_cfg_val(cfg, "passthrough"), "enable") == 0) ? 1 : 0;
325
326     PrintDebug("video: init_device\n");
327
328     struct vm_device * dev = v3_allocate_device(name, &dev_ops, video_state);
329
330     if (v3_attach_device(vm, dev) == -1) {
331         PrintError("Could not attach device %s\n", name);
332         return -1;
333     }
334
335     frame_buf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
336
337     video_state->framebuf = V3_VAddr((void *)frame_buf_pa);
338     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
339
340     PrintDebug("PA of array: %p\n", (void *)frame_buf_pa);
341
342     video_state->passthrough = enable_passthrough;
343
344     video_state->ops = NULL;
345     video_state->private_data = NULL;
346
347     if (enable_passthrough) {
348         PrintDebug("Enabling CGA Passthrough\n");
349
350         if (v3_hook_write_mem(vm, START_ADDR, END_ADDR, START_ADDR, &video_write_mem, dev) == -1) {
351             PrintDebug("\n\nVideo Hook failed.\n\n");
352         }
353     } else {
354         if (v3_hook_write_mem(vm, START_ADDR, END_ADDR, frame_buf_pa, &video_write_mem, dev) == -1) {
355             PrintDebug("\n\nVideo Hook failed.\n\n");
356         }
357     }
358
359
360     v3_dev_hook_io(dev, 0x3b0, &video_read_port, &video_write_port);
361     v3_dev_hook_io(dev, 0x3b1, &video_read_port, &video_write_port);
362     v3_dev_hook_io(dev, 0x3b2, &video_read_port, &video_write_port);
363     v3_dev_hook_io(dev, 0x3b3, &video_read_port, &video_write_port);
364     v3_dev_hook_io(dev, 0x3b4, &video_read_port, &video_write_port);
365     v3_dev_hook_io(dev, 0x3b5, &video_read_port, &video_write_port);
366     v3_dev_hook_io(dev, 0x3b6, &video_read_port, &video_write_port);
367     v3_dev_hook_io(dev, 0x3b7, &video_read_port, &video_write_port);
368     v3_dev_hook_io(dev, 0x3b8, &video_read_port, &video_write_port);
369     v3_dev_hook_io(dev, 0x3b9, &video_read_port, &video_write_port);
370     v3_dev_hook_io(dev, 0x3ba, &video_read_port, &video_write_port);
371     v3_dev_hook_io(dev, 0x3bb, &video_read_port, &video_write_port);
372     v3_dev_hook_io(dev, 0x3c0, &video_read_port, &video_write_port);
373     v3_dev_hook_io(dev, 0x3c1, &video_read_port, &video_write_port);
374     v3_dev_hook_io(dev, 0x3c2, &video_read_port, &video_write_port);
375     v3_dev_hook_io(dev, 0x3c3, &video_read_port, &video_write_port);
376     v3_dev_hook_io(dev, 0x3c4, &video_read_port, &video_write_port);
377     v3_dev_hook_io(dev, 0x3c5, &video_read_port, &video_write_port);
378     v3_dev_hook_io(dev, 0x3c6, &video_read_port, &video_write_port);
379     v3_dev_hook_io(dev, 0x3c7, &video_read_port, &video_write_port);
380     v3_dev_hook_io(dev, 0x3c8, &video_read_port, &video_write_port);
381     v3_dev_hook_io(dev, 0x3c9, &video_read_port, &video_write_port);
382     v3_dev_hook_io(dev, 0x3ca, &video_read_port, &video_write_port);
383     v3_dev_hook_io(dev, 0x3cb, &video_read_port, &video_write_port);
384     v3_dev_hook_io(dev, 0x3cc, &video_read_port, &video_write_port);
385     v3_dev_hook_io(dev, 0x3cd, &video_read_port, &video_write_port);
386     v3_dev_hook_io(dev, 0x3ce, &video_read_port, &video_write_port);
387     v3_dev_hook_io(dev, 0x3cf, &video_read_port, &video_write_port);
388     v3_dev_hook_io(dev, 0x3d0, &video_read_port, &video_write_port);
389     v3_dev_hook_io(dev, 0x3d1, &video_read_port, &video_write_port);
390     v3_dev_hook_io(dev, 0x3d2, &video_read_port, &video_write_port);
391     v3_dev_hook_io(dev, 0x3d3, &video_read_port, &video_write_port);
392     v3_dev_hook_io(dev, 0x3d4, &video_read_port, &crtc_index_write);
393     v3_dev_hook_io(dev, 0x3d5, &video_read_port, &crtc_data_write);
394     v3_dev_hook_io(dev, 0x3d6, &video_read_port, &video_write_port);
395     v3_dev_hook_io(dev, 0x3d7, &video_read_port, &video_write_port);
396     v3_dev_hook_io(dev, 0x3d8, &video_read_port, &video_write_port);
397     v3_dev_hook_io(dev, 0x3d9, &video_read_port, &video_write_port);
398     v3_dev_hook_io(dev, 0x3da, &video_read_port, &video_write_port);
399     v3_dev_hook_io(dev, 0x3db, &video_read_port, &video_write_port);
400     v3_dev_hook_io(dev, 0x3dc, &video_read_port, &video_write_port);
401     v3_dev_hook_io(dev, 0x3dd, &video_read_port, &video_write_port);
402     v3_dev_hook_io(dev, 0x3de, &video_read_port, &video_write_port);
403     v3_dev_hook_io(dev, 0x3df, &video_read_port, &video_write_port);
404
405
406     return 0;
407 }
408
409 device_register("CGA_VIDEO", cga_init);
410
411
412 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
413     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
414     
415     video_state->ops = ops;
416     video_state->private_data = private_data;
417
418     return 0;
419 }