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.


It seems FreeBSD sets the cursor to an out-of-screen location while booting. This...
[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     V3_ASSERT(screen_byte_offset <= FRAMEBUF_SIZE - SCREEN_SIZE);
304     V3_ASSERT(offset < SCREEN_SIZE);
305     V3_ASSERT(length <= SCREEN_SIZE);
306     V3_ASSERT(offset + length <= SCREEN_SIZE);
307     memcpy(dst, state->framebuf + screen_byte_offset + offset, length);
308
309     return 0;
310 }
311
312
313
314 static int free_device(struct vm_device * dev) {
315     struct video_internal * video_state = (struct video_internal *)dev->private_data;
316     struct v3_vm_info * vm = dev->vm;
317
318     if (video_state->framebuf_pa) {
319         V3_FreePages((void *)(video_state->framebuf_pa), (FRAMEBUF_SIZE / 4096));
320     }
321
322     v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, START_ADDR);
323
324     v3_unhook_io_port(vm, 0x3b0);
325     v3_unhook_io_port(vm, 0x3b1);
326     v3_unhook_io_port(vm, 0x3b2);
327     v3_unhook_io_port(vm, 0x3b3);
328     v3_unhook_io_port(vm, 0x3b4);
329     v3_unhook_io_port(vm, 0x3b5);
330     v3_unhook_io_port(vm, 0x3b6);
331     v3_unhook_io_port(vm, 0x3b7);
332     v3_unhook_io_port(vm, 0x3b8);
333     v3_unhook_io_port(vm, 0x3b9);
334     v3_unhook_io_port(vm, 0x3ba);
335     v3_unhook_io_port(vm, 0x3bb);
336     v3_unhook_io_port(vm, 0x3c0);
337     v3_unhook_io_port(vm, 0x3c1);
338     v3_unhook_io_port(vm, 0x3c2);
339     v3_unhook_io_port(vm, 0x3c3);
340     v3_unhook_io_port(vm, 0x3c4);
341     v3_unhook_io_port(vm, 0x3c5);
342     v3_unhook_io_port(vm, 0x3c6);
343     v3_unhook_io_port(vm, 0x3c7);
344     v3_unhook_io_port(vm, 0x3c8);
345     v3_unhook_io_port(vm, 0x3c9);
346     v3_unhook_io_port(vm, 0x3ca);
347     v3_unhook_io_port(vm, 0x3cb);
348     v3_unhook_io_port(vm, 0x3cc);
349     v3_unhook_io_port(vm, 0x3cd);
350     v3_unhook_io_port(vm, 0x3ce);
351     v3_unhook_io_port(vm, 0x3cf);
352     v3_unhook_io_port(vm, 0x3d0);
353     v3_unhook_io_port(vm, 0x3d1);
354     v3_unhook_io_port(vm, 0x3d2);
355     v3_unhook_io_port(vm, 0x3d3);
356     v3_unhook_io_port(vm, 0x3d4);
357     v3_unhook_io_port(vm, 0x3d5);
358     v3_unhook_io_port(vm, 0x3d6);
359     v3_unhook_io_port(vm, 0x3d7);
360     v3_unhook_io_port(vm, 0x3d8);
361     v3_unhook_io_port(vm, 0x3d9);
362     v3_unhook_io_port(vm, 0x3da);
363     v3_unhook_io_port(vm, 0x3db);
364     v3_unhook_io_port(vm, 0x3dc);
365     v3_unhook_io_port(vm, 0x3dd);
366     v3_unhook_io_port(vm, 0x3de);
367     v3_unhook_io_port(vm, 0x3df);
368
369     V3_Free(video_state);
370
371     return 0;
372 }
373
374
375 static struct v3_device_ops dev_ops = {
376     .free = free_device,
377 };
378
379 static int cga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
380     struct video_internal * video_state = NULL;
381     char * dev_id = v3_cfg_val(cfg, "ID");
382     char * passthrough_str = v3_cfg_val(cfg, "passthrough");
383     
384     PrintDebug("video: init_device\n");
385
386     video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
387     memset(video_state, 0, sizeof(struct video_internal));
388
389     struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, video_state);
390
391     if (v3_attach_device(vm, dev) == -1) {
392         PrintError("Could not attach device %s\n", dev_id);
393         V3_Free(video_state);
394         return -1;
395     }
396
397     video_state->framebuf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
398     video_state->framebuf = V3_VAddr((void *)(video_state->framebuf_pa));
399     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
400
401     PrintDebug("PA of array: %p\n", (void *)(video_state->framebuf_pa));
402
403     if ((passthrough_str != NULL) &&
404         (strcasecmp(passthrough_str, "enable") == 0)) {;
405         video_state->passthrough = 1;
406     }
407
408
409     if (video_state->passthrough) {
410         PrintDebug("Enabling CGA Passthrough\n");
411         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
412                               START_ADDR, &video_write_mem, dev) == -1) {
413             PrintDebug("\n\nVideo Hook failed.\n\n");
414         }
415     } else {
416         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
417                               video_state->framebuf_pa, &video_write_mem, dev) == -1) {
418             PrintDebug("\n\nVideo Hook failed.\n\n");
419         }
420     }
421
422
423     v3_hook_io_port(vm, 0x3b0, &video_read_port, &video_write_port, dev);
424     v3_hook_io_port(vm, 0x3b1, &video_read_port, &video_write_port, dev);
425     v3_hook_io_port(vm, 0x3b2, &video_read_port, &video_write_port, dev);
426     v3_hook_io_port(vm, 0x3b3, &video_read_port, &video_write_port, dev);
427     v3_hook_io_port(vm, 0x3b4, &video_read_port, &video_write_port, dev);
428     v3_hook_io_port(vm, 0x3b5, &video_read_port, &video_write_port, dev);
429     v3_hook_io_port(vm, 0x3b6, &video_read_port, &video_write_port, dev);
430     v3_hook_io_port(vm, 0x3b7, &video_read_port, &video_write_port, dev);
431     v3_hook_io_port(vm, 0x3b8, &video_read_port, &video_write_port, dev);
432     v3_hook_io_port(vm, 0x3b9, &video_read_port, &video_write_port, dev);
433     v3_hook_io_port(vm, 0x3ba, &video_read_port, &video_write_port, dev);
434     v3_hook_io_port(vm, 0x3bb, &video_read_port, &video_write_port, dev);
435     v3_hook_io_port(vm, 0x3c0, &video_read_port, &video_write_port, dev);
436     v3_hook_io_port(vm, 0x3c1, &video_read_port, &video_write_port, dev);
437     v3_hook_io_port(vm, 0x3c2, &video_read_port, &video_write_port, dev);
438     v3_hook_io_port(vm, 0x3c3, &video_read_port, &video_write_port, dev);
439     v3_hook_io_port(vm, 0x3c4, &video_read_port, &video_write_port, dev);
440     v3_hook_io_port(vm, 0x3c5, &video_read_port, &video_write_port, dev);
441     v3_hook_io_port(vm, 0x3c6, &video_read_port, &video_write_port, dev);
442     v3_hook_io_port(vm, 0x3c7, &video_read_port, &video_write_port, dev);
443     v3_hook_io_port(vm, 0x3c8, &video_read_port, &video_write_port, dev);
444     v3_hook_io_port(vm, 0x3c9, &video_read_port, &video_write_port, dev);
445     v3_hook_io_port(vm, 0x3ca, &video_read_port, &video_write_port, dev);
446     v3_hook_io_port(vm, 0x3cb, &video_read_port, &video_write_port, dev);
447     v3_hook_io_port(vm, 0x3cc, &video_read_port, &video_write_port, dev);
448     v3_hook_io_port(vm, 0x3cd, &video_read_port, &video_write_port, dev);
449     v3_hook_io_port(vm, 0x3ce, &video_read_port, &video_write_port, dev);
450     v3_hook_io_port(vm, 0x3cf, &video_read_port, &video_write_port, dev);
451     v3_hook_io_port(vm, 0x3d0, &video_read_port, &video_write_port, dev);
452     v3_hook_io_port(vm, 0x3d1, &video_read_port, &video_write_port, dev);
453     v3_hook_io_port(vm, 0x3d2, &video_read_port, &video_write_port, dev);
454     v3_hook_io_port(vm, 0x3d3, &video_read_port, &video_write_port, dev);
455     v3_hook_io_port(vm, 0x3d4, &video_read_port, &crtc_index_write, dev);
456     v3_hook_io_port(vm, 0x3d5, &video_read_port, &crtc_data_write, dev);
457     v3_hook_io_port(vm, 0x3d6, &video_read_port, &video_write_port, dev);
458     v3_hook_io_port(vm, 0x3d7, &video_read_port, &video_write_port, dev);
459     v3_hook_io_port(vm, 0x3d8, &video_read_port, &video_write_port, dev);
460     v3_hook_io_port(vm, 0x3d9, &video_read_port, &video_write_port, dev);
461     v3_hook_io_port(vm, 0x3da, &video_read_port, &video_write_port, dev);
462     v3_hook_io_port(vm, 0x3db, &video_read_port, &video_write_port, dev);
463     v3_hook_io_port(vm, 0x3dc, &video_read_port, &video_write_port, dev);
464     v3_hook_io_port(vm, 0x3dd, &video_read_port, &video_write_port, dev);
465     v3_hook_io_port(vm, 0x3de, &video_read_port, &video_write_port, dev);
466     v3_hook_io_port(vm, 0x3df, &video_read_port, &video_write_port, dev);
467
468
469     return 0;
470 }
471
472 device_register("CGA_VIDEO", cga_init);
473
474
475 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
476     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
477     
478     video_state->ops = ops;
479     video_state->private_data = private_data;
480
481     return 0;
482 }