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.


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