bochs.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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. #define grub_video_render_target grub_video_fbrender_target
  19. #include <grub/err.h>
  20. #include <grub/types.h>
  21. #include <grub/dl.h>
  22. #include <grub/misc.h>
  23. #include <grub/mm.h>
  24. #include <grub/video.h>
  25. #include <grub/video_fb.h>
  26. #include <grub/pci.h>
  27. #include <grub/vga.h>
  28. GRUB_MOD_LICENSE ("GPLv3+");
  29. static struct
  30. {
  31. struct grub_video_mode_info mode_info;
  32. grub_uint8_t *ptr;
  33. int mapped;
  34. grub_uint32_t base;
  35. grub_pci_device_t dev;
  36. } framebuffer;
  37. #define BOCHS_APERTURE_SIZE 0x800000
  38. #define BOCHS_MAX_WIDTH 1600
  39. #define BOCHS_MAX_HEIGHT 1200
  40. #define BOCHS_WIDTH_ALIGN 8
  41. enum
  42. {
  43. BOCHS_VBE_INDEX = 0x1ce,
  44. BOCHS_VBE_DATA = 0x1cf,
  45. };
  46. enum
  47. {
  48. BOCHS_VBE_WIDTH = 1,
  49. BOCHS_VBE_HEIGHT = 2,
  50. BOCHS_VBE_BPP = 3,
  51. BOCHS_VBE_ENABLE = 4,
  52. BOCHS_VBE_Y_OFFSET = 9,
  53. BOCHS_VBE_MAX
  54. };
  55. static void
  56. vbe_write (grub_uint16_t val, grub_uint16_t addr)
  57. {
  58. grub_outw (addr, BOCHS_VBE_INDEX);
  59. grub_outw (val, BOCHS_VBE_DATA);
  60. }
  61. static grub_uint16_t
  62. vbe_read (grub_uint16_t addr)
  63. {
  64. grub_outw (addr, BOCHS_VBE_INDEX);
  65. return grub_inw (BOCHS_VBE_DATA);
  66. }
  67. struct saved_state
  68. {
  69. grub_uint8_t cr[256];
  70. grub_uint8_t gr[256];
  71. grub_uint8_t sr[256];
  72. grub_uint8_t r[256];
  73. grub_uint8_t g[256];
  74. grub_uint8_t b[256];
  75. grub_uint8_t vbe[BOCHS_VBE_MAX];
  76. int vbe_enable;
  77. /* We need to preserve VGA font and VGA text. */
  78. grub_uint8_t vram[32 * 4 * 256];
  79. };
  80. static struct saved_state initial_state;
  81. static int state_saved = 0;
  82. static void
  83. save_state (struct saved_state *st)
  84. {
  85. unsigned i;
  86. for (i = 0; i < ARRAY_SIZE (st->cr); i++)
  87. st->cr[i] = grub_vga_cr_read (i);
  88. for (i = 0; i < ARRAY_SIZE (st->gr); i++)
  89. st->gr[i] = grub_vga_gr_read (i);
  90. for (i = 0; i < ARRAY_SIZE (st->sr); i++)
  91. st->sr[i] = grub_vga_sr_read (i);
  92. for (i = 0; i < 256; i++)
  93. grub_vga_palette_read (i, st->r + i, st->g + i, st->b + i);
  94. st->vbe_enable = vbe_read (BOCHS_VBE_ENABLE) & 1;
  95. if (st->vbe_enable)
  96. for (i = 0; i < ARRAY_SIZE (st->vbe); i++)
  97. st->vbe[i] = vbe_read (i);
  98. grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE);
  99. grub_memcpy (st->vram, framebuffer.ptr, sizeof (st->vram));
  100. grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE);
  101. }
  102. static void
  103. restore_state (struct saved_state *st)
  104. {
  105. unsigned i;
  106. if (st->vbe_enable)
  107. for (i = 0; i < ARRAY_SIZE (st->vbe); i++)
  108. vbe_write (st->vbe[i], i);
  109. else
  110. vbe_write (0, BOCHS_VBE_ENABLE);
  111. grub_vga_cr_write (0, 0x11);
  112. for (i = 0; i < ARRAY_SIZE (st->cr); i++)
  113. grub_vga_cr_write (st->cr[i], i);
  114. for (i = 0; i < ARRAY_SIZE (st->sr); i++)
  115. grub_vga_sr_write (st->sr[i], i);
  116. for (i = 0; i < ARRAY_SIZE (st->gr); i++)
  117. grub_vga_gr_write (st->gr[i], i);
  118. for (i = 0; i < 256; i++)
  119. grub_vga_palette_write (i, st->r[i], st->g[i], st->b[i]);
  120. grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4, GRUB_VGA_SR_MEMORY_MODE);
  121. grub_memcpy (framebuffer.ptr, st->vram, sizeof (st->vram));
  122. grub_vga_sr_write (st->sr[GRUB_VGA_SR_MEMORY_MODE], GRUB_VGA_SR_MEMORY_MODE);
  123. }
  124. static grub_err_t
  125. grub_video_bochs_video_init (void)
  126. {
  127. /* Reset frame buffer. */
  128. grub_memset (&framebuffer, 0, sizeof(framebuffer));
  129. return grub_video_fb_init ();
  130. }
  131. static grub_err_t
  132. grub_video_bochs_video_fini (void)
  133. {
  134. if (framebuffer.mapped)
  135. grub_pci_device_unmap_range (framebuffer.dev, framebuffer.ptr,
  136. BOCHS_APERTURE_SIZE);
  137. if (state_saved)
  138. {
  139. restore_state (&initial_state);
  140. state_saved = 0;
  141. }
  142. return grub_video_fb_fini ();
  143. }
  144. static grub_err_t
  145. doublebuf_pageflipping_set_page (int page)
  146. {
  147. int start = framebuffer.mode_info.height * page;
  148. vbe_write (start, BOCHS_VBE_Y_OFFSET);
  149. return GRUB_ERR_NONE;
  150. }
  151. static grub_err_t
  152. grub_video_bochs_set_palette (unsigned int start, unsigned int count,
  153. struct grub_video_palette_data *palette_data)
  154. {
  155. if (framebuffer.mode_info.mode_type == GRUB_VIDEO_MODE_TYPE_INDEX_COLOR)
  156. {
  157. unsigned i;
  158. if (start >= 0x100)
  159. return GRUB_ERR_NONE;
  160. if (start + count >= 0x100)
  161. count = 0x100 - start;
  162. for (i = 0; i < count; i++)
  163. grub_vga_palette_write (start + i, palette_data[i].r, palette_data[i].g,
  164. palette_data[i].b);
  165. }
  166. /* Then set color to emulated palette. */
  167. return grub_video_fb_set_palette (start, count, palette_data);
  168. }
  169. /* Helper for grub_video_bochs_setup. */
  170. static int
  171. find_card (grub_pci_device_t dev, grub_pci_id_t pciid, void *data)
  172. {
  173. int *found = data;
  174. grub_pci_address_t addr;
  175. grub_uint32_t class;
  176. addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
  177. class = grub_pci_read (addr);
  178. if (((class >> 16) & 0xffff) != 0x0300 || pciid != 0x11111234)
  179. return 0;
  180. addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
  181. framebuffer.base = grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK;
  182. if (!framebuffer.base)
  183. return 0;
  184. *found = 1;
  185. framebuffer.dev = dev;
  186. /* Enable address spaces. */
  187. addr = grub_pci_make_address (framebuffer.dev, GRUB_PCI_REG_COMMAND);
  188. grub_pci_write (addr, 0x7);
  189. return 1;
  190. }
  191. static grub_err_t
  192. grub_video_bochs_setup (unsigned int width, unsigned int height,
  193. grub_video_mode_type_t mode_type,
  194. grub_video_mode_type_t mode_mask)
  195. {
  196. int depth;
  197. grub_err_t err;
  198. int found = 0;
  199. int pitch, bytes_per_pixel;
  200. grub_size_t page_size; /* The size of a page in bytes. */
  201. /* Decode depth from mode_type. If it is zero, then autodetect. */
  202. depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
  203. >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
  204. if (width == 0 || height == 0)
  205. {
  206. width = 800;
  207. height = 600;
  208. }
  209. if (width > BOCHS_MAX_WIDTH)
  210. return grub_error (GRUB_ERR_IO, "width must be at most",
  211. BOCHS_MAX_WIDTH);
  212. if (height > BOCHS_MAX_HEIGHT)
  213. return grub_error (GRUB_ERR_IO, "height must be at most",
  214. BOCHS_MAX_HEIGHT);
  215. if (width & (BOCHS_WIDTH_ALIGN - 1))
  216. return grub_error (GRUB_ERR_IO, "width must be a multiple of %d",
  217. BOCHS_WIDTH_ALIGN);
  218. if (depth == 0
  219. && !grub_video_check_mode_flag (mode_type, mode_mask,
  220. GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, 0))
  221. depth = 24;
  222. if (depth == 0)
  223. depth = 8;
  224. if (depth != 32 && depth != 24 && depth != 16 && depth != 15 && depth != 8
  225. && depth != 4)
  226. return grub_error (GRUB_ERR_IO, "only 32, 24, 16, 15 and 8-bpp are"
  227. " supported by bochs video");
  228. if (depth == 4)
  229. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "4-bpp isn't cupported");
  230. bytes_per_pixel = (depth + 7) / 8;
  231. if (depth == 4)
  232. pitch = width / 2;
  233. else
  234. pitch = width * bytes_per_pixel;
  235. page_size = pitch * height;
  236. if (page_size > BOCHS_APERTURE_SIZE)
  237. return grub_error (GRUB_ERR_IO, "Not enough video memory for this mode");
  238. grub_pci_iterate (find_card, &found);
  239. if (!found)
  240. return grub_error (GRUB_ERR_IO, "Couldn't find graphics card");
  241. if (found && framebuffer.base == 0)
  242. {
  243. /* FIXME: change framebuffer base */
  244. return grub_error (GRUB_ERR_IO, "PCI BAR not set");
  245. }
  246. /* We can safely discard volatile attribute. */
  247. framebuffer.ptr = (void *) grub_pci_device_map_range (framebuffer.dev,
  248. framebuffer.base,
  249. BOCHS_APERTURE_SIZE);
  250. framebuffer.mapped = 1;
  251. if (!state_saved)
  252. {
  253. save_state (&initial_state);
  254. state_saved = 1;
  255. }
  256. {
  257. vbe_write (0, BOCHS_VBE_ENABLE);
  258. vbe_write (width, BOCHS_VBE_WIDTH);
  259. vbe_write (height, BOCHS_VBE_HEIGHT);
  260. vbe_write (depth, BOCHS_VBE_BPP);
  261. vbe_write (1, BOCHS_VBE_ENABLE);
  262. doublebuf_pageflipping_set_page (0);
  263. }
  264. /* Fill mode info details. */
  265. framebuffer.mode_info.width = width;
  266. framebuffer.mode_info.height = height;
  267. framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
  268. framebuffer.mode_info.bpp = depth;
  269. framebuffer.mode_info.bytes_per_pixel = bytes_per_pixel;
  270. framebuffer.mode_info.pitch = pitch;
  271. framebuffer.mode_info.number_of_colors = 256;
  272. framebuffer.mode_info.reserved_mask_size = 0;
  273. framebuffer.mode_info.reserved_field_pos = 0;
  274. switch (depth)
  275. {
  276. case 4:
  277. case 8:
  278. framebuffer.mode_info.mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
  279. framebuffer.mode_info.number_of_colors = 16;
  280. break;
  281. case 16:
  282. framebuffer.mode_info.red_mask_size = 5;
  283. framebuffer.mode_info.red_field_pos = 11;
  284. framebuffer.mode_info.green_mask_size = 6;
  285. framebuffer.mode_info.green_field_pos = 5;
  286. framebuffer.mode_info.blue_mask_size = 5;
  287. framebuffer.mode_info.blue_field_pos = 0;
  288. break;
  289. case 15:
  290. framebuffer.mode_info.red_mask_size = 5;
  291. framebuffer.mode_info.red_field_pos = 10;
  292. framebuffer.mode_info.green_mask_size = 5;
  293. framebuffer.mode_info.green_field_pos = 5;
  294. framebuffer.mode_info.blue_mask_size = 5;
  295. framebuffer.mode_info.blue_field_pos = 0;
  296. break;
  297. case 32:
  298. framebuffer.mode_info.reserved_mask_size = 8;
  299. framebuffer.mode_info.reserved_field_pos = 24;
  300. /* Fallthrough. */
  301. case 24:
  302. framebuffer.mode_info.red_mask_size = 8;
  303. framebuffer.mode_info.red_field_pos = 16;
  304. framebuffer.mode_info.green_mask_size = 8;
  305. framebuffer.mode_info.green_field_pos = 8;
  306. framebuffer.mode_info.blue_mask_size = 8;
  307. framebuffer.mode_info.blue_field_pos = 0;
  308. break;
  309. }
  310. framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info);
  311. if (BOCHS_APERTURE_SIZE >= 2 * page_size)
  312. err = grub_video_fb_setup (mode_type, mode_mask,
  313. &framebuffer.mode_info,
  314. framebuffer.ptr,
  315. doublebuf_pageflipping_set_page,
  316. framebuffer.ptr + page_size);
  317. else
  318. err = grub_video_fb_setup (mode_type, mode_mask,
  319. &framebuffer.mode_info,
  320. framebuffer.ptr, 0, 0);
  321. /* Copy default palette to initialize emulated palette. */
  322. err = grub_video_bochs_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
  323. grub_video_fbstd_colors);
  324. return err;
  325. }
  326. static struct grub_video_adapter grub_video_bochs_adapter =
  327. {
  328. .name = "Bochs PCI Video Driver",
  329. .id = GRUB_VIDEO_DRIVER_BOCHS,
  330. .prio = GRUB_VIDEO_ADAPTER_PRIO_NATIVE,
  331. .init = grub_video_bochs_video_init,
  332. .fini = grub_video_bochs_video_fini,
  333. .setup = grub_video_bochs_setup,
  334. .get_info = grub_video_fb_get_info,
  335. .get_info_and_fini = grub_video_fb_get_info_and_fini,
  336. .set_palette = grub_video_bochs_set_palette,
  337. .get_palette = grub_video_fb_get_palette,
  338. .set_viewport = grub_video_fb_set_viewport,
  339. .get_viewport = grub_video_fb_get_viewport,
  340. .set_region = grub_video_fb_set_region,
  341. .get_region = grub_video_fb_get_region,
  342. .set_area_status = grub_video_fb_set_area_status,
  343. .get_area_status = grub_video_fb_get_area_status,
  344. .map_color = grub_video_fb_map_color,
  345. .map_rgb = grub_video_fb_map_rgb,
  346. .map_rgba = grub_video_fb_map_rgba,
  347. .unmap_color = grub_video_fb_unmap_color,
  348. .fill_rect = grub_video_fb_fill_rect,
  349. .blit_bitmap = grub_video_fb_blit_bitmap,
  350. .blit_render_target = grub_video_fb_blit_render_target,
  351. .scroll = grub_video_fb_scroll,
  352. .swap_buffers = grub_video_fb_swap_buffers,
  353. .create_render_target = grub_video_fb_create_render_target,
  354. .delete_render_target = grub_video_fb_delete_render_target,
  355. .set_active_render_target = grub_video_fb_set_active_render_target,
  356. .get_active_render_target = grub_video_fb_get_active_render_target,
  357. .next = 0
  358. };
  359. GRUB_MOD_INIT(video_bochs)
  360. {
  361. grub_video_register (&grub_video_bochs_adapter);
  362. }
  363. GRUB_MOD_FINI(video_bochs)
  364. {
  365. grub_video_unregister (&grub_video_bochs_adapter);
  366. }