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.


more device free updates
[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 #include <palacios/vmm_io.h>
27
28 #include <devices/console.h>
29
30
31
32
33 #ifndef DEBUG_CGA
34 #undef PrintDebug
35 #define PrintDebug(fmt, args...)
36 #endif
37
38
39 #define START_ADDR 0xB8000
40 #define END_ADDR 0xC0000
41
42 #define FRAMEBUF_SIZE (END_ADDR - START_ADDR)
43 #define SCREEN_SIZE 4000
44
45 #define BASE_CGA_PORT 0x3B0
46
47 #define NUM_COLS 80
48 #define NUM_ROWS 25
49 #define BYTES_PER_ROW (NUM_COLS * 2)
50 #define BYTES_PER_COL 2
51
52
53 struct video_internal {
54     uint8_t * framebuf;
55     addr_t framebuf_pa;
56
57     // These store the values for unhandled ports, in case of a read op
58     uint8_t port_store[44];
59
60
61     uint8_t crtc_index_reg;          // io port 3D4
62     uint8_t crtc_data_regs[25];      // io port 3D5
63     
64     /* IMPORTANT: These are column offsets _NOT_ byte offsets */
65     uint16_t screen_offset; // relative to the framebuffer
66     uint16_t cursor_offset; // relative to the framebuffer
67     /* ** */
68
69     // updating the screen offset is not atomic, 
70     // so we need a temp variable to hold the partial update
71     uint16_t tmp_screen_offset; 
72     
73
74     uint8_t passthrough;
75
76
77     struct v3_console_ops * ops;
78     void * private_data;
79
80
81
82 };
83
84
85
86
87 static void passthrough_in(uint16_t port, void * src, uint_t length) {
88     switch (length) {
89         case 1:
90             *(uint8_t *)src = v3_inb(port);
91             break;
92         case 2:
93             *(uint16_t *)src = v3_inw(port);
94             break;
95         case 4:
96             *(uint32_t *)src = v3_indw(port);
97             break;
98         default:
99             break;
100     }
101 }
102
103
104 static void passthrough_out(uint16_t port, void * src, uint_t length) {
105     switch (length) {
106         case 1:
107             v3_outb(port, *(uint8_t *)src);
108             break;
109         case 2:
110             v3_outw(port, *(uint16_t *)src);
111             break;
112         case 4:
113             v3_outdw(port, *(uint32_t *)src);
114             break;
115         default:
116             break;
117     }
118 }
119
120 static int video_write_mem(struct guest_info * core, addr_t guest_addr, void * dest, uint_t length, void * priv_data) {
121     struct vm_device * dev = (struct vm_device *)priv_data;
122     struct video_internal * state = (struct video_internal *)dev->private_data;
123     uint_t fb_offset = guest_addr - START_ADDR;
124     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
125     uint_t screen_length;
126
127     PrintDebug("Guest address: %p length = %d, fb_offset=%d, screen_offset=%d\n", 
128                (void *)guest_addr, length, fb_offset, screen_byte_offset);
129
130     if (state->passthrough) {
131         memcpy(state->framebuf + fb_offset, V3_VAddr((void *)guest_addr), length);
132     }
133
134     if ((fb_offset >= screen_byte_offset) && (fb_offset < (screen_byte_offset + SCREEN_SIZE))) {
135         uint_t screen_pos = fb_offset - screen_byte_offset;
136         uint_t x = (screen_pos % BYTES_PER_ROW) / BYTES_PER_COL;
137         uint_t y = screen_pos / BYTES_PER_ROW;
138         PrintDebug("Sending Screen update\n");
139         
140         if (state->ops) {
141             PrintDebug("\tcalling update_screen()\n");
142             
143             /* avoid updates past end of screen */
144             screen_length = SCREEN_SIZE - screen_byte_offset;
145             if (screen_length > length) screen_length = length;
146             state->ops->update_screen(x, y, screen_length, state->private_data);
147         }
148     }
149
150     return length;
151 }
152
153 static int video_read_port(struct guest_info * core, uint16_t port, void * dest, uint_t length, void * priv_data) {
154     struct vm_device * dev = (struct vm_device *)priv_data;
155     struct video_internal * video_state = (struct video_internal *)dev->private_data;
156
157
158     PrintDebug("Video: Read port 0x%x\n", port);
159
160     if (video_state->passthrough) {
161         passthrough_in(port, dest, length);
162     }
163
164     return length;
165 }
166
167
168
169 static int video_write_port(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
170     struct vm_device * dev = (struct vm_device *)priv_data;
171     struct video_internal * video_state = (struct video_internal *)dev->private_data;
172
173
174     PrintDebug("Video: write port 0x%x...\n", port);
175
176     if (video_state->passthrough) {
177         passthrough_out(port, src, length);
178     } 
179
180     return length;
181 }
182
183
184
185 static int crtc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
186     struct vm_device * dev = (struct vm_device *)priv_data;
187     struct video_internal * video_state = (struct video_internal *)dev->private_data;
188     uint8_t val = *(uint8_t *)src;
189     uint_t index = video_state->crtc_index_reg;
190
191     if (length != 1) {
192         PrintError("Invalid write length for port 0x%x\n", port);
193         return -1;
194     }
195
196     PrintDebug("Video: write on port 0x%x... (val=0x%x)\n", port, val);
197
198     video_state->crtc_data_regs[index] = val;
199
200     switch (index) {
201         case 0x0c: { // scroll high byte
202             uint16_t tmp_val = val;
203             video_state->tmp_screen_offset = ((tmp_val << 8) & 0xff00);
204             break;
205         }
206         case 0x0d: {  // Scroll low byte
207             int diff = 0;
208
209             video_state->tmp_screen_offset += val;
210             diff = (video_state->tmp_screen_offset - video_state->screen_offset) / NUM_COLS;
211
212             // Update the true offset value
213             video_state->screen_offset = video_state->tmp_screen_offset;
214             video_state->tmp_screen_offset = 0;
215
216             PrintDebug("Scroll lines = %d, new screen offset=%d\n", 
217                        diff, video_state->screen_offset * BYTES_PER_COL);
218
219             if (video_state->ops) {
220                 if (video_state->ops->scroll(diff, video_state->private_data) == -1) {
221                     PrintError("Error sending scroll event\n");
222                     return -1;
223                 }
224             }
225             break;
226         }
227         case 0x0E: {  // Cursor adjustment High byte
228             uint16_t tmp_val = val;
229             video_state->cursor_offset = ((tmp_val << 8) & 0xff00);
230
231             break;
232         }
233         case 0x0F: { // cursor adjustment low byte
234             uint_t x = 0;
235             uint_t y = 0;
236             
237             video_state->cursor_offset += val;
238             
239             x = video_state->cursor_offset % NUM_COLS;
240             y = (video_state->cursor_offset - video_state->screen_offset) / NUM_COLS;
241             
242             PrintDebug("New Cursor Location; X=%d Y=%d\n", x, y);
243             
244             if (video_state->ops) {
245                 if (video_state->ops->update_cursor(x, y, video_state->private_data) == -1) {
246                     PrintError("Error updating cursor\n");
247                     return -1;
248                 }
249             } 
250
251             break;
252         }
253         default:
254             break;
255     }
256
257     if (video_state->passthrough) {
258         passthrough_out(port, src, length);
259     }
260
261     return length;
262 }
263
264
265 static int crtc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
266     struct vm_device * dev = (struct vm_device *)priv_data;
267     struct video_internal * video_state = (struct video_internal *)dev->private_data;
268     
269     if (length > 2) {
270         PrintError("Invalid write length for crtc index register port: %d (0x%x)\n",
271                    port, port);
272         return -1;
273     }
274                    
275
276     video_state->crtc_index_reg = *(uint8_t *)src;
277
278     // Only do the passthrough IO for the first byte
279     // the second byte will be done in the data register handler
280     if (video_state->passthrough) {
281         passthrough_out(port, src, 1);
282     }
283
284     if (length == 2) {
285         if (crtc_data_write(core, port + 1, src + 1, length - 1, dev) != (length - 1)) {
286             PrintError("could not handle implicit crtc data write\n");
287             return -1;
288         }
289     }
290
291     return length;
292 }
293
294
295
296 int v3_cons_get_fb(struct vm_device * frontend_dev, uint8_t * dst, uint_t offset, uint_t length) {
297     struct video_internal * state = (struct video_internal *)frontend_dev->private_data;
298     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
299
300     PrintDebug("Getting framebuffer for screen; framebuf=%p, screen_offset=%d, offset=%d, length=%d\n", 
301                state->framebuf, screen_byte_offset, offset, length);
302
303     memcpy(dst, state->framebuf + screen_byte_offset + offset, length);
304
305     return 0;
306 }
307
308
309
310 static int free_device(struct vm_device * dev) {
311     struct video_internal * video_state = (struct video_internal *)dev->private_data;
312     struct v3_vm_info * vm = dev->vm;
313
314     if (video_state->framebuf_pa) {
315         V3_FreePages((void *)(video_state->framebuf_pa), (FRAMEBUF_SIZE / 4096));
316     }
317
318     v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, START_ADDR);
319
320     v3_unhook_io_port(vm, 0x3b0);
321     v3_unhook_io_port(vm, 0x3b1);
322     v3_unhook_io_port(vm, 0x3b2);
323     v3_unhook_io_port(vm, 0x3b3);
324     v3_unhook_io_port(vm, 0x3b4);
325     v3_unhook_io_port(vm, 0x3b5);
326     v3_unhook_io_port(vm, 0x3b6);
327     v3_unhook_io_port(vm, 0x3b7);
328     v3_unhook_io_port(vm, 0x3b8);
329     v3_unhook_io_port(vm, 0x3b9);
330     v3_unhook_io_port(vm, 0x3ba);
331     v3_unhook_io_port(vm, 0x3bb);
332     v3_unhook_io_port(vm, 0x3c0);
333     v3_unhook_io_port(vm, 0x3c1);
334     v3_unhook_io_port(vm, 0x3c2);
335     v3_unhook_io_port(vm, 0x3c3);
336     v3_unhook_io_port(vm, 0x3c4);
337     v3_unhook_io_port(vm, 0x3c5);
338     v3_unhook_io_port(vm, 0x3c6);
339     v3_unhook_io_port(vm, 0x3c7);
340     v3_unhook_io_port(vm, 0x3c8);
341     v3_unhook_io_port(vm, 0x3c9);
342     v3_unhook_io_port(vm, 0x3ca);
343     v3_unhook_io_port(vm, 0x3cb);
344     v3_unhook_io_port(vm, 0x3cc);
345     v3_unhook_io_port(vm, 0x3cd);
346     v3_unhook_io_port(vm, 0x3ce);
347     v3_unhook_io_port(vm, 0x3cf);
348     v3_unhook_io_port(vm, 0x3d0);
349     v3_unhook_io_port(vm, 0x3d1);
350     v3_unhook_io_port(vm, 0x3d2);
351     v3_unhook_io_port(vm, 0x3d3);
352     v3_unhook_io_port(vm, 0x3d4);
353     v3_unhook_io_port(vm, 0x3d5);
354     v3_unhook_io_port(vm, 0x3d6);
355     v3_unhook_io_port(vm, 0x3d7);
356     v3_unhook_io_port(vm, 0x3d8);
357     v3_unhook_io_port(vm, 0x3d9);
358     v3_unhook_io_port(vm, 0x3da);
359     v3_unhook_io_port(vm, 0x3db);
360     v3_unhook_io_port(vm, 0x3dc);
361     v3_unhook_io_port(vm, 0x3dd);
362     v3_unhook_io_port(vm, 0x3de);
363     v3_unhook_io_port(vm, 0x3df);
364
365     V3_Free(video_state);
366
367     return 0;
368 }
369
370
371 static struct v3_device_ops dev_ops = {
372     .free = free_device,
373 };
374
375 static int cga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
376     struct video_internal * video_state = NULL;
377     char * dev_id = v3_cfg_val(cfg, "ID");
378     char * passthrough_str = v3_cfg_val(cfg, "passthrough");
379     
380     PrintDebug("video: init_device\n");
381
382     video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
383     memset(video_state, 0, sizeof(struct video_internal));
384
385     struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, video_state);
386
387     if (v3_attach_device(vm, dev) == -1) {
388         PrintError("Could not attach device %s\n", dev_id);
389         V3_Free(video_state);
390         return -1;
391     }
392
393     video_state->framebuf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
394     video_state->framebuf = V3_VAddr((void *)(video_state->framebuf_pa));
395     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
396
397     PrintDebug("PA of array: %p\n", (void *)(video_state->framebuf_pa));
398
399     if ((passthrough_str != NULL) &&
400         (strcasecmp(passthrough_str, "enable") == 0)) {;
401         video_state->passthrough = 1;
402     }
403
404
405     if (video_state->passthrough) {
406         PrintDebug("Enabling CGA Passthrough\n");
407         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
408                               START_ADDR, &video_write_mem, dev) == -1) {
409             PrintDebug("\n\nVideo Hook failed.\n\n");
410         }
411     } else {
412         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
413                               video_state->framebuf_pa, &video_write_mem, dev) == -1) {
414             PrintDebug("\n\nVideo Hook failed.\n\n");
415         }
416     }
417
418
419     v3_hook_io_port(vm, 0x3b0, &video_read_port, &video_write_port, dev);
420     v3_hook_io_port(vm, 0x3b1, &video_read_port, &video_write_port, dev);
421     v3_hook_io_port(vm, 0x3b2, &video_read_port, &video_write_port, dev);
422     v3_hook_io_port(vm, 0x3b3, &video_read_port, &video_write_port, dev);
423     v3_hook_io_port(vm, 0x3b4, &video_read_port, &video_write_port, dev);
424     v3_hook_io_port(vm, 0x3b5, &video_read_port, &video_write_port, dev);
425     v3_hook_io_port(vm, 0x3b6, &video_read_port, &video_write_port, dev);
426     v3_hook_io_port(vm, 0x3b7, &video_read_port, &video_write_port, dev);
427     v3_hook_io_port(vm, 0x3b8, &video_read_port, &video_write_port, dev);
428     v3_hook_io_port(vm, 0x3b9, &video_read_port, &video_write_port, dev);
429     v3_hook_io_port(vm, 0x3ba, &video_read_port, &video_write_port, dev);
430     v3_hook_io_port(vm, 0x3bb, &video_read_port, &video_write_port, dev);
431     v3_hook_io_port(vm, 0x3c0, &video_read_port, &video_write_port, dev);
432     v3_hook_io_port(vm, 0x3c1, &video_read_port, &video_write_port, dev);
433     v3_hook_io_port(vm, 0x3c2, &video_read_port, &video_write_port, dev);
434     v3_hook_io_port(vm, 0x3c3, &video_read_port, &video_write_port, dev);
435     v3_hook_io_port(vm, 0x3c4, &video_read_port, &video_write_port, dev);
436     v3_hook_io_port(vm, 0x3c5, &video_read_port, &video_write_port, dev);
437     v3_hook_io_port(vm, 0x3c6, &video_read_port, &video_write_port, dev);
438     v3_hook_io_port(vm, 0x3c7, &video_read_port, &video_write_port, dev);
439     v3_hook_io_port(vm, 0x3c8, &video_read_port, &video_write_port, dev);
440     v3_hook_io_port(vm, 0x3c9, &video_read_port, &video_write_port, dev);
441     v3_hook_io_port(vm, 0x3ca, &video_read_port, &video_write_port, dev);
442     v3_hook_io_port(vm, 0x3cb, &video_read_port, &video_write_port, dev);
443     v3_hook_io_port(vm, 0x3cc, &video_read_port, &video_write_port, dev);
444     v3_hook_io_port(vm, 0x3cd, &video_read_port, &video_write_port, dev);
445     v3_hook_io_port(vm, 0x3ce, &video_read_port, &video_write_port, dev);
446     v3_hook_io_port(vm, 0x3cf, &video_read_port, &video_write_port, dev);
447     v3_hook_io_port(vm, 0x3d0, &video_read_port, &video_write_port, dev);
448     v3_hook_io_port(vm, 0x3d1, &video_read_port, &video_write_port, dev);
449     v3_hook_io_port(vm, 0x3d2, &video_read_port, &video_write_port, dev);
450     v3_hook_io_port(vm, 0x3d3, &video_read_port, &video_write_port, dev);
451     v3_hook_io_port(vm, 0x3d4, &video_read_port, &crtc_index_write, dev);
452     v3_hook_io_port(vm, 0x3d5, &video_read_port, &crtc_data_write, dev);
453     v3_hook_io_port(vm, 0x3d6, &video_read_port, &video_write_port, dev);
454     v3_hook_io_port(vm, 0x3d7, &video_read_port, &video_write_port, dev);
455     v3_hook_io_port(vm, 0x3d8, &video_read_port, &video_write_port, dev);
456     v3_hook_io_port(vm, 0x3d9, &video_read_port, &video_write_port, dev);
457     v3_hook_io_port(vm, 0x3da, &video_read_port, &video_write_port, dev);
458     v3_hook_io_port(vm, 0x3db, &video_read_port, &video_write_port, dev);
459     v3_hook_io_port(vm, 0x3dc, &video_read_port, &video_write_port, dev);
460     v3_hook_io_port(vm, 0x3dd, &video_read_port, &video_write_port, dev);
461     v3_hook_io_port(vm, 0x3de, &video_read_port, &video_write_port, dev);
462     v3_hook_io_port(vm, 0x3df, &video_read_port, &video_write_port, dev);
463
464
465     return 0;
466 }
467
468 device_register("CGA_VIDEO", cga_init);
469
470
471 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
472     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
473     
474     video_state->ops = ops;
475     video_state->private_data = private_data;
476
477     return 0;
478 }