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.


reworked device IO hook framework
[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 video_internal * video_state = priv_data;
155
156
157     PrintDebug("Video: Read port 0x%x\n", port);
158
159     if (video_state->passthrough) {
160         passthrough_in(port, dest, length);
161     }
162
163     return length;
164 }
165
166
167
168 static int video_write_port(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
169     struct video_internal * video_state = priv_data;
170
171
172     PrintDebug("Video: write port 0x%x...\n", port);
173
174     if (video_state->passthrough) {
175         passthrough_out(port, src, length);
176     } 
177
178     return length;
179 }
180
181
182
183 static int crtc_data_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
184     struct video_internal * video_state = priv_data;
185     uint8_t val = *(uint8_t *)src;
186     uint_t index = video_state->crtc_index_reg;
187
188     if (length != 1) {
189         PrintError("Invalid write length for port 0x%x\n", port);
190         return -1;
191     }
192
193     PrintDebug("Video: write on port 0x%x... (val=0x%x)\n", port, val);
194
195     video_state->crtc_data_regs[index] = val;
196
197     switch (index) {
198         case 0x0c: { // scroll high byte
199             uint16_t tmp_val = val;
200             video_state->tmp_screen_offset = ((tmp_val << 8) & 0xff00);
201             break;
202         }
203         case 0x0d: {  // Scroll low byte
204             int diff = 0;
205
206             video_state->tmp_screen_offset += val;
207             diff = (video_state->tmp_screen_offset - video_state->screen_offset) / NUM_COLS;
208
209             // Update the true offset value
210             video_state->screen_offset = video_state->tmp_screen_offset;
211             video_state->tmp_screen_offset = 0;
212
213             PrintDebug("Scroll lines = %d, new screen offset=%d\n", 
214                        diff, video_state->screen_offset * BYTES_PER_COL);
215
216             if (video_state->ops) {
217                 if (video_state->ops->scroll(diff, video_state->private_data) == -1) {
218                     PrintError("Error sending scroll event\n");
219                     return -1;
220                 }
221             }
222             break;
223         }
224         case 0x0E: {  // Cursor adjustment High byte
225             uint16_t tmp_val = val;
226             video_state->cursor_offset = ((tmp_val << 8) & 0xff00);
227
228             break;
229         }
230         case 0x0F: { // cursor adjustment low byte
231             uint_t x = 0;
232             uint_t y = 0;
233             
234             video_state->cursor_offset += val;
235             
236             x = video_state->cursor_offset % NUM_COLS;
237             y = (video_state->cursor_offset - video_state->screen_offset) / NUM_COLS;
238             
239             PrintDebug("New Cursor Location; X=%d Y=%d\n", x, y);
240             
241             if (video_state->ops) {
242                 if (video_state->ops->update_cursor(x, y, video_state->private_data) == -1) {
243                     PrintError("Error updating cursor\n");
244                     return -1;
245                 }
246             } 
247
248             break;
249         }
250         default:
251             break;
252     }
253
254     if (video_state->passthrough) {
255         passthrough_out(port, src, length);
256     }
257
258     return length;
259 }
260
261
262 static int crtc_index_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
263     struct video_internal * video_state = priv_data;
264     
265     if (length > 2) {
266         PrintError("Invalid write length for crtc index register port: %d (0x%x)\n",
267                    port, port);
268         return -1;
269     }
270                    
271
272     video_state->crtc_index_reg = *(uint8_t *)src;
273
274     // Only do the passthrough IO for the first byte
275     // the second byte will be done in the data register handler
276     if (video_state->passthrough) {
277         passthrough_out(port, src, 1);
278     }
279
280     if (length == 2) {
281         if (crtc_data_write(core, port + 1, src + 1, length - 1, video_state) != (length - 1)) {
282             PrintError("could not handle implicit crtc data write\n");
283             return -1;
284         }
285     }
286
287     return length;
288 }
289
290
291
292 int v3_cons_get_fb(struct vm_device * frontend_dev, uint8_t * dst, uint_t offset, uint_t length) {
293     struct video_internal * state = (struct video_internal *)frontend_dev->private_data;
294     uint_t screen_byte_offset = state->screen_offset * BYTES_PER_COL;
295
296     PrintDebug("Getting framebuffer for screen; framebuf=%p, screen_offset=%d, offset=%d, length=%d\n", 
297                state->framebuf, screen_byte_offset, offset, length);
298
299     V3_ASSERT(screen_byte_offset <= FRAMEBUF_SIZE - SCREEN_SIZE);
300     V3_ASSERT(offset < SCREEN_SIZE);
301     V3_ASSERT(length <= SCREEN_SIZE);
302     V3_ASSERT(offset + length <= SCREEN_SIZE);
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
313     if (video_state->framebuf_pa) {
314         V3_FreePages((void *)(video_state->framebuf_pa), (FRAMEBUF_SIZE / 4096));
315     }
316
317     v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, START_ADDR);
318
319
320     V3_Free(video_state);
321
322     return 0;
323 }
324
325
326 static struct v3_device_ops dev_ops = {
327     .free = free_device,
328 };
329
330 static int cga_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
331     struct video_internal * video_state = NULL;
332     char * dev_id = v3_cfg_val(cfg, "ID");
333     char * passthrough_str = v3_cfg_val(cfg, "passthrough");
334     
335     PrintDebug("video: init_device\n");
336
337     video_state = (struct video_internal *)V3_Malloc(sizeof(struct video_internal));
338     memset(video_state, 0, sizeof(struct video_internal));
339
340     struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, video_state);
341
342     if (v3_attach_device(vm, dev) == -1) {
343         PrintError("Could not attach device %s\n", dev_id);
344         V3_Free(video_state);
345         return -1;
346     }
347
348     video_state->framebuf_pa = (addr_t)V3_AllocPages(FRAMEBUF_SIZE / 4096);
349     video_state->framebuf = V3_VAddr((void *)(video_state->framebuf_pa));
350     memset(video_state->framebuf, 0, FRAMEBUF_SIZE);
351
352     PrintDebug("PA of array: %p\n", (void *)(video_state->framebuf_pa));
353
354     if ((passthrough_str != NULL) &&
355         (strcasecmp(passthrough_str, "enable") == 0)) {;
356         video_state->passthrough = 1;
357     }
358
359
360     if (video_state->passthrough) {
361         PrintDebug("Enabling CGA Passthrough\n");
362         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
363                               START_ADDR, &video_write_mem, dev) == -1) {
364             PrintDebug("\n\nVideo Hook failed.\n\n");
365         }
366     } else {
367         if (v3_hook_write_mem(vm, V3_MEM_CORE_ANY, START_ADDR, END_ADDR, 
368                               video_state->framebuf_pa, &video_write_mem, dev) == -1) {
369             PrintDebug("\n\nVideo Hook failed.\n\n");
370         }
371     }
372
373
374     v3_dev_hook_io(dev, 0x3b0, &video_read_port, &video_write_port);
375     v3_dev_hook_io(dev, 0x3b1, &video_read_port, &video_write_port);
376     v3_dev_hook_io(dev, 0x3b2, &video_read_port, &video_write_port);
377     v3_dev_hook_io(dev, 0x3b3, &video_read_port, &video_write_port);
378     v3_dev_hook_io(dev, 0x3b4, &video_read_port, &video_write_port);
379     v3_dev_hook_io(dev, 0x3b5, &video_read_port, &video_write_port);
380     v3_dev_hook_io(dev, 0x3b6, &video_read_port, &video_write_port);
381     v3_dev_hook_io(dev, 0x3b7, &video_read_port, &video_write_port);
382     v3_dev_hook_io(dev, 0x3b8, &video_read_port, &video_write_port);
383     v3_dev_hook_io(dev, 0x3b9, &video_read_port, &video_write_port);
384     v3_dev_hook_io(dev, 0x3ba, &video_read_port, &video_write_port);
385     v3_dev_hook_io(dev, 0x3bb, &video_read_port, &video_write_port);
386     v3_dev_hook_io(dev, 0x3c0, &video_read_port, &video_write_port);
387     v3_dev_hook_io(dev, 0x3c1, &video_read_port, &video_write_port);
388     v3_dev_hook_io(dev, 0x3c2, &video_read_port, &video_write_port);
389     v3_dev_hook_io(dev, 0x3c3, &video_read_port, &video_write_port);
390     v3_dev_hook_io(dev, 0x3c4, &video_read_port, &video_write_port);
391     v3_dev_hook_io(dev, 0x3c5, &video_read_port, &video_write_port);
392     v3_dev_hook_io(dev, 0x3c6, &video_read_port, &video_write_port);
393     v3_dev_hook_io(dev, 0x3c7, &video_read_port, &video_write_port);
394     v3_dev_hook_io(dev, 0x3c8, &video_read_port, &video_write_port);
395     v3_dev_hook_io(dev, 0x3c9, &video_read_port, &video_write_port);
396     v3_dev_hook_io(dev, 0x3ca, &video_read_port, &video_write_port);
397     v3_dev_hook_io(dev, 0x3cb, &video_read_port, &video_write_port);
398     v3_dev_hook_io(dev, 0x3cc, &video_read_port, &video_write_port);
399     v3_dev_hook_io(dev, 0x3cd, &video_read_port, &video_write_port);
400     v3_dev_hook_io(dev, 0x3ce, &video_read_port, &video_write_port);
401     v3_dev_hook_io(dev, 0x3cf, &video_read_port, &video_write_port);
402     v3_dev_hook_io(dev, 0x3d0, &video_read_port, &video_write_port);
403     v3_dev_hook_io(dev, 0x3d1, &video_read_port, &video_write_port);
404     v3_dev_hook_io(dev, 0x3d2, &video_read_port, &video_write_port);
405     v3_dev_hook_io(dev, 0x3d3, &video_read_port, &video_write_port);
406     v3_dev_hook_io(dev, 0x3d4, &video_read_port, &crtc_index_write);
407     v3_dev_hook_io(dev, 0x3d5, &video_read_port, &crtc_data_write);
408     v3_dev_hook_io(dev, 0x3d6, &video_read_port, &video_write_port);
409     v3_dev_hook_io(dev, 0x3d7, &video_read_port, &video_write_port);
410     v3_dev_hook_io(dev, 0x3d8, &video_read_port, &video_write_port);
411     v3_dev_hook_io(dev, 0x3d9, &video_read_port, &video_write_port);
412     v3_dev_hook_io(dev, 0x3da, &video_read_port, &video_write_port);
413     v3_dev_hook_io(dev, 0x3db, &video_read_port, &video_write_port);
414     v3_dev_hook_io(dev, 0x3dc, &video_read_port, &video_write_port);
415     v3_dev_hook_io(dev, 0x3dd, &video_read_port, &video_write_port);
416     v3_dev_hook_io(dev, 0x3de, &video_read_port, &video_write_port);
417     v3_dev_hook_io(dev, 0x3df, &video_read_port, &video_write_port);
418
419
420     return 0;
421 }
422
423 device_register("CGA_VIDEO", cga_init);
424
425
426 int v3_console_register_cga(struct vm_device * cga_dev, struct v3_console_ops * ops, void * private_data) {
427     struct video_internal * video_state = (struct video_internal *)cga_dev->private_data;
428     
429     video_state->ops = ops;
430     video_state->private_data = private_data;
431
432     return 0;
433 }