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.


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