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.


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