]> git.proxmox.com Git - grub2.git/blame - grub-core/video/bochs.c
Merge mainline into newreloc. For now without boot tests
[grub2.git] / grub-core / video / bochs.c
CommitLineData
368e544b
VS
1/*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#define grub_video_render_target grub_video_fbrender_target
20
21#include <grub/err.h>
22#include <grub/types.h>
23#include <grub/dl.h>
24#include <grub/misc.h>
25#include <grub/mm.h>
26#include <grub/video.h>
27#include <grub/video_fb.h>
28#include <grub/pci.h>
967828eb 29#include <grub/vga.h>
368e544b
VS
30
31static struct
32{
33 struct grub_video_mode_info mode_info;
34
35 grub_uint8_t *ptr;
36 int mapped;
37 grub_uint32_t base;
38 grub_pci_device_t dev;
39} framebuffer;
40
41#define BOCHS_APERTURE_SIZE 0x800000
42#define BOCHS_MAX_WIDTH 1600
43#define BOCHS_MAX_HEIGHT 1200
44#define BOCHS_WIDTH_ALIGN 8
45
368e544b
VS
46enum
47 {
48 BOCHS_VBE_INDEX = 0x1ce,
49 BOCHS_VBE_DATA = 0x1cf,
368e544b
VS
50 };
51
52enum
53 {
54 BOCHS_VBE_WIDTH = 1,
55 BOCHS_VBE_HEIGHT = 2,
56 BOCHS_VBE_BPP = 3,
57 BOCHS_VBE_ENABLE = 4,
58 BOCHS_VBE_Y_OFFSET = 9,
59 BOCHS_VBE_MAX
60 };
61
62static void
63vbe_write (grub_uint16_t val, grub_uint16_t addr)
64{
65 grub_outw (addr, BOCHS_VBE_INDEX);
66 grub_outw (val, BOCHS_VBE_DATA);
67}
68
69static grub_uint16_t
70vbe_read (grub_uint16_t addr)
71{
72 grub_outw (addr, BOCHS_VBE_INDEX);
73 return grub_inw (BOCHS_VBE_DATA);
74}
75
368e544b
VS
76struct saved_state
77{
78 grub_uint8_t cr[256];
79 grub_uint8_t gr[256];
80 grub_uint8_t sr[256];
81 grub_uint8_t r[256];
82 grub_uint8_t g[256];
83 grub_uint8_t b[256];
84 grub_uint8_t vbe[BOCHS_VBE_MAX];
85 int vbe_enable;
86 /* We need to preserve VGA font and VGA text. */
87 grub_uint8_t vram[32 * 4 * 256];
88};
89
90static struct saved_state initial_state;
91static int state_saved = 0;
92
93static void
94save_state (struct saved_state *st)
95{
96 unsigned i;
97
98 for (i = 0; i < ARRAY_SIZE (st->cr); i++)
967828eb 99 st->cr[i] = grub_vga_cr_read (i);
368e544b 100 for (i = 0; i < ARRAY_SIZE (st->gr); i++)
967828eb 101 st->gr[i] = grub_vga_gr_read (i);
368e544b 102 for (i = 0; i < ARRAY_SIZE (st->sr); i++)
967828eb 103 st->sr[i] = grub_vga_sr_read (i);
368e544b
VS
104
105 for (i = 0; i < 256; i++)
967828eb 106 grub_vga_palette_read (i, st->r + i, st->g + i, st->b + i);
368e544b
VS
107
108 st->vbe_enable = vbe_read (BOCHS_VBE_ENABLE) & 1;
109 if (st->vbe_enable)
110 for (i = 0; i < ARRAY_SIZE (st->vbe); i++)
111 st->vbe[i] = vbe_read (i);
112
967828eb 113 grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE);
368e544b 114 grub_memcpy (st->vram, framebuffer.ptr, sizeof (st->vram));
967828eb 115 grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE);
368e544b
VS
116}
117
118static void
119restore_state (struct saved_state *st)
120{
121 unsigned i;
122
123 if (st->vbe_enable)
124 for (i = 0; i < ARRAY_SIZE (st->vbe); i++)
125 vbe_write (st->vbe[i], i);
126 else
127 vbe_write (0, BOCHS_VBE_ENABLE);
128
967828eb 129 grub_vga_cr_write (0, 0x11);
368e544b 130 for (i = 0; i < ARRAY_SIZE (st->cr); i++)
967828eb 131 grub_vga_cr_write (st->cr[i], i);
368e544b 132 for (i = 0; i < ARRAY_SIZE (st->sr); i++)
967828eb 133 grub_vga_sr_write (st->sr[i], i);
368e544b 134 for (i = 0; i < ARRAY_SIZE (st->gr); i++)
967828eb 135 grub_vga_gr_write (st->gr[i], i);
368e544b
VS
136
137 for (i = 0; i < 256; i++)
967828eb 138 grub_vga_palette_write (i, st->r[i], st->g[i], st->b[i]);
368e544b 139
967828eb 140 grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE);
368e544b 141 grub_memcpy (framebuffer.ptr, st->vram, sizeof (st->vram));
967828eb 142 grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE);
368e544b
VS
143}
144
145static grub_err_t
146grub_video_bochs_video_init (void)
147{
148 /* Reset frame buffer. */
149 grub_memset (&framebuffer, 0, sizeof(framebuffer));
150
151 return grub_video_fb_init ();
152}
153
154static grub_err_t
155grub_video_bochs_video_fini (void)
156{
157 if (framebuffer.mapped)
158 grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr,
159 BOCHS_APERTURE_SIZE);
160
161 if (state_saved)
162 {
163 restore_state (&initial_state);
164 state_saved = 0;
165 }
166
167 return grub_video_fb_fini ();
168}
169
170static grub_err_t
171doublebuf_pageflipping_set_page (int page)
172{
173 int start = framebuffer.mode_info.height * page;
174
175 vbe_write (start, BOCHS_VBE_Y_OFFSET);
176 return GRUB_ERR_NONE;
177}
178
179static grub_err_t
180grub_video_bochs_set_palette (unsigned int start, unsigned int count,
181 struct grub_video_palette_data *palette_data)
182{
183 if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
184 {
185 unsigned i;
186 if (start >= 0x100)
187 return GRUB_ERR_NONE;
188 if (start + count >= 0x100)
189 count = 0x100 - start;
190
191 for (i = 0; i < count; i++)
967828eb
VS
192 grub_vga_palette_write (start + i, palette_data[i].r, palette_data[i].g,
193 palette_data[i].b);
368e544b
VS
194 }
195
196 /* Then set color to emulated palette. */
197 return grub_video_fb_set_palette (start, count, palette_data);
198}
199
200static grub_err_t
201grub_video_bochs_setup (unsigned int width, unsigned int height,
202 unsigned int mode_type, unsigned int mode_mask)
203{
204 int depth;
205 grub_err_t err;
206 int found = 0;
207 int pitch, bytes_per_pixel;
208 grub_size_t page_size; /* The size of a page in bytes. */
209
210 auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused)));
211 int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, grub_pci_id_t pciid)
212 {
213 grub_pci_address_t addr;
214 grub_uint32_t class;
215
216 addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
217 class = grub_pci_read (addr);
218
219 if (((class >> 16) & 0xffff) != 0x0300 || pciid != 0x11111234)
220 return 0;
221
222 found = 1;
223
224 addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
225 framebuffer.base = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK;
226 framebuffer.dev = dev;
227
228 return 1;
229 }
230
231 /* Decode depth from mode_type. If it is zero, then autodetect. */
232 depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
233 >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
234
235 if (width == 0 || height == 0)
236 {
237 width = 800;
238 height = 600;
239 }
240
241 if (width > BOCHS_MAX_WIDTH)
242 return grub_error (GRUB_ERR_IO, "width must be at most",
243 BOCHS_MAX_WIDTH);
244
245 if (height > BOCHS_MAX_HEIGHT)
246 return grub_error (GRUB_ERR_IO, "height must be at most",
247 BOCHS_MAX_HEIGHT);
248
249 if (width & (BOCHS_WIDTH_ALIGN - 1))
250 return grub_error (GRUB_ERR_IO, "width must be a multiple of %d",
251 BOCHS_WIDTH_ALIGN);
252
253 if (depth == 0
254 && !grub_video_check_mode_flag (mode_type, mode_mask,
255 GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, 0))
256 depth = 24;
257
258 if (depth == 0)
259 depth = 8;
260
261 if (depth != 32 && depth != 24 && depth != 16 && depth != 15 && depth != 8
262 && depth != 4)
263 return grub_error (GRUB_ERR_IO, "only 32, 24, 16, 15 and 8-bpp are"
264 " supported by bochs video");
265
266 if (depth == 4)
267 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "4-bpp isn't cupported");
268
269 bytes_per_pixel = (depth + 7) / 8;
270 if (depth == 4)
271 pitch = width / 2;
272 else
273 pitch = width * bytes_per_pixel;
274
275 page_size = pitch * height;
276
277 if (page_size > BOCHS_APERTURE_SIZE)
278 return grub_error (GRUB_ERR_IO, "Not enough video memory for this mode");
279
280 grub_pci_iterate (find_card);
281 if (!found)
282 return grub_error (GRUB_ERR_IO, "Couldn't find graphics card");
283
284 if (found && framebuffer.base == 0)
285 {
286 /* FIXME: change framebuffer base */
287 return grub_error (GRUB_ERR_IO, "PCI BAR not set");
288 }
289
290 /* We can safely discard volatile attribute. */
291 framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev,
292 framebuffer.base,
293 BOCHS_APERTURE_SIZE);
294 framebuffer.mapped = 1;
295
296 if (!state_saved)
297 {
298 save_state (&initial_state);
299 state_saved = 1;
300 }
301
302 {
303 vbe_write (0, BOCHS_VBE_ENABLE);
304
305 vbe_write (width, BOCHS_VBE_WIDTH);
306 vbe_write (height, BOCHS_VBE_HEIGHT);
307 vbe_write (depth, BOCHS_VBE_BPP);
308
309 vbe_write (1, BOCHS_VBE_ENABLE);
310 doublebuf_pageflipping_set_page (0);
311 }
312
313 /* Fill mode info details. */
314 framebuffer.mode_info.width = width;
315 framebuffer.mode_info.height = height;
316 framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
317 framebuffer.mode_info.bpp = depth;
318 framebuffer.mode_info.bytes_per_pixel = bytes_per_pixel;
319 framebuffer.mode_info.pitch = pitch;
320 framebuffer.mode_info.number_of_colors = 256;
321 framebuffer.mode_info.reserved_mask_size = 0;
322 framebuffer.mode_info.reserved_field_pos = 0;
323
324 switch (depth)
325 {
326 case 4:
327 case 8:
328 framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
329 break;
330 case 16:
331 framebuffer.mode_info.red_mask_size = 5;
332 framebuffer.mode_info.red_field_pos = 11;
333 framebuffer.mode_info.green_mask_size = 6;
334 framebuffer.mode_info.green_field_pos = 5;
335 framebuffer.mode_info.blue_mask_size = 5;
336 framebuffer.mode_info.blue_field_pos = 0;
337 break;
338
339 case 15:
340 framebuffer.mode_info.red_mask_size = 5;
341 framebuffer.mode_info.red_field_pos = 10;
342 framebuffer.mode_info.green_mask_size = 5;
343 framebuffer.mode_info.green_field_pos = 5;
344 framebuffer.mode_info.blue_mask_size = 5;
345 framebuffer.mode_info.blue_field_pos = 0;
346 break;
347
348 case 32:
349 framebuffer.mode_info.reserved_mask_size = 8;
350 framebuffer.mode_info.reserved_field_pos = 24;
351
352 case 24:
353 framebuffer.mode_info.red_mask_size = 8;
354 framebuffer.mode_info.red_field_pos = 16;
355 framebuffer.mode_info.green_mask_size = 8;
356 framebuffer.mode_info.green_field_pos = 8;
357 framebuffer.mode_info.blue_mask_size = 8;
358 framebuffer.mode_info.blue_field_pos = 0;
359 break;
360 }
361
362 framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info);
363
364 if (BOCHS_APERTURE_SIZE >= 2 * page_size)
365 err = grub_video_fb_setup (mode_type, mode_mask,
366 &framebuffer.mode_info,
367 framebuffer.ptr,
368 doublebuf_pageflipping_set_page,
369 framebuffer.ptr + page_size);
370 else
371 err = grub_video_fb_setup (mode_type, mode_mask,
372 &framebuffer.mode_info,
373 framebuffer.ptr, 0, 0);
374
375
376 /* Copy default palette to initialize emulated palette. */
377 err = grub_video_bochs_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
378 grub_video_fbstd_colors);
379 return err;
380}
381
382static struct grub_video_adapter grub_video_bochs_adapter =
383 {
384 .name = "Bochs PCI Video Driver",
385 .id = GRUB_VIDEO_DRIVER_BOCHS,
386
05e51879
VS
387 .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE,
388
368e544b
VS
389 .init = grub_video_bochs_video_init,
390 .fini = grub_video_bochs_video_fini,
391 .setup = grub_video_bochs_setup,
392 .get_info = grub_video_fb_get_info,
393 .get_info_and_fini = grub_video_fb_get_info_and_fini,
394 .set_palette = grub_video_bochs_set_palette,
395 .get_palette = grub_video_fb_get_palette,
396 .set_viewport = grub_video_fb_set_viewport,
397 .get_viewport = grub_video_fb_get_viewport,
398 .map_color = grub_video_fb_map_color,
399 .map_rgb = grub_video_fb_map_rgb,
400 .map_rgba = grub_video_fb_map_rgba,
401 .unmap_color = grub_video_fb_unmap_color,
402 .fill_rect = grub_video_fb_fill_rect,
403 .blit_bitmap = grub_video_fb_blit_bitmap,
404 .blit_render_target = grub_video_fb_blit_render_target,
405 .scroll = grub_video_fb_scroll,
406 .swap_buffers = grub_video_fb_swap_buffers,
407 .create_render_target = grub_video_fb_create_render_target,
408 .delete_render_target = grub_video_fb_delete_render_target,
409 .set_active_render_target = grub_video_fb_set_active_render_target,
410 .get_active_render_target = grub_video_fb_get_active_render_target,
411
412 .next = 0
413 };
414
415GRUB_MOD_INIT(video_bochs)
416{
417 grub_video_register (&grub_video_bochs_adapter);
418}
419
420GRUB_MOD_FINI(video_bochs)
421{
422 grub_video_unregister (&grub_video_bochs_adapter);
423}