efi_gop.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2005,2006,2007,2008,2009 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/efi/api.h>
  27. #include <grub/efi/efi.h>
  28. #include <grub/efi/graphics_output.h>
  29. static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
  30. static struct grub_efi_gop *gop;
  31. static unsigned old_mode;
  32. static int restore_needed;
  33. static struct
  34. {
  35. struct grub_video_mode_info mode_info;
  36. struct grub_video_render_target *render_target;
  37. grub_uint8_t *ptr;
  38. } framebuffer;
  39. static int
  40. check_protocol (void)
  41. {
  42. gop = grub_efi_locate_protocol (&graphics_output_guid, 0);
  43. if (gop)
  44. return 1;
  45. return 0;
  46. }
  47. static grub_err_t
  48. grub_video_gop_init (void)
  49. {
  50. grub_memset (&framebuffer, 0, sizeof(framebuffer));
  51. return grub_video_fb_init ();
  52. }
  53. static grub_err_t
  54. grub_video_gop_fini (void)
  55. {
  56. if (restore_needed)
  57. {
  58. efi_call_2 (gop->set_mode, gop, old_mode);
  59. restore_needed = 0;
  60. }
  61. return grub_video_fb_fini ();
  62. }
  63. static int
  64. grub_video_gop_get_bpp (struct grub_efi_gop_mode_info *in)
  65. {
  66. grub_uint32_t total_mask;
  67. int i;
  68. switch (in->pixel_format)
  69. {
  70. case GRUB_EFI_GOT_BGRA8:
  71. case GRUB_EFI_GOT_RGBA8:
  72. return 32;
  73. case GRUB_EFI_GOT_BITMASK:
  74. /* Check overlaps. */
  75. if ((in->pixel_bitmask.r & in->pixel_bitmask.g)
  76. || (in->pixel_bitmask.r & in->pixel_bitmask.b)
  77. || (in->pixel_bitmask.g & in->pixel_bitmask.b)
  78. || (in->pixel_bitmask.r & in->pixel_bitmask.a)
  79. || (in->pixel_bitmask.g & in->pixel_bitmask.a)
  80. || (in->pixel_bitmask.b & in->pixel_bitmask.a))
  81. return 0;
  82. total_mask = in->pixel_bitmask.r | in->pixel_bitmask.g
  83. | in->pixel_bitmask.b | in->pixel_bitmask.a;
  84. for (i = 31; i >= 0; i--)
  85. if (total_mask & (1 << i))
  86. return i + 1;
  87. /* Fall through. */
  88. default:
  89. return 0;
  90. }
  91. }
  92. static void
  93. grub_video_gop_get_bitmask (grub_uint32_t mask, unsigned int *mask_size,
  94. unsigned int *field_pos)
  95. {
  96. int i;
  97. int last_p;
  98. for (i = 31; i >= 0; i--)
  99. if (mask & (1 << i))
  100. break;
  101. if (i == -1)
  102. {
  103. *mask_size = *field_pos = 0;
  104. return;
  105. }
  106. last_p = i;
  107. for (; i >= 0; i--)
  108. if (!(mask & (1 << i)))
  109. break;
  110. *field_pos = i + 1;
  111. *mask_size = last_p - *field_pos + 1;
  112. }
  113. static grub_err_t
  114. grub_video_gop_fill_mode_info (struct grub_efi_gop_mode_info *in,
  115. struct grub_video_mode_info *out)
  116. {
  117. out->number_of_colors = 256;
  118. out->width = in->width;
  119. out->height = in->height;
  120. out->mode_type = GRUB_VIDEO_MODE_TYPE_RGB;
  121. out->bpp = grub_video_gop_get_bpp (in);
  122. out->bytes_per_pixel = out->bpp >> 3;
  123. if (!out->bpp)
  124. return grub_error (GRUB_ERR_IO, "unsupported video mode");
  125. out->pitch = in->pixels_per_scanline * out->bytes_per_pixel;
  126. switch (in->pixel_format)
  127. {
  128. case GRUB_EFI_GOT_RGBA8:
  129. out->red_mask_size = 8;
  130. out->red_field_pos = 0;
  131. out->green_mask_size = 8;
  132. out->green_field_pos = 8;
  133. out->blue_mask_size = 8;
  134. out->blue_field_pos = 16;
  135. out->reserved_mask_size = 8;
  136. out->reserved_field_pos = 24;
  137. break;
  138. case GRUB_EFI_GOT_BGRA8:
  139. out->red_mask_size = 8;
  140. out->red_field_pos = 16;
  141. out->green_mask_size = 8;
  142. out->green_field_pos = 8;
  143. out->blue_mask_size = 8;
  144. out->blue_field_pos = 0;
  145. out->reserved_mask_size = 8;
  146. out->reserved_field_pos = 24;
  147. break;
  148. case GRUB_EFI_GOT_BITMASK:
  149. grub_video_gop_get_bitmask (in->pixel_bitmask.r, &out->red_mask_size,
  150. &out->red_field_pos);
  151. grub_video_gop_get_bitmask (in->pixel_bitmask.g, &out->green_mask_size,
  152. &out->green_field_pos);
  153. grub_video_gop_get_bitmask (in->pixel_bitmask.b, &out->blue_mask_size,
  154. &out->blue_field_pos);
  155. grub_video_gop_get_bitmask (in->pixel_bitmask.a, &out->reserved_mask_size,
  156. &out->reserved_field_pos);
  157. break;
  158. default:
  159. return grub_error (GRUB_ERR_IO, "unsupported video mode");
  160. }
  161. out->blit_format = grub_video_get_blit_format (out);
  162. return GRUB_ERR_NONE;
  163. }
  164. static grub_err_t
  165. grub_video_gop_setup (unsigned int width, unsigned int height,
  166. unsigned int mode_type, unsigned int mode_mask __attribute__ ((unused)))
  167. {
  168. unsigned int depth;
  169. struct grub_efi_gop_mode_info *info = NULL;
  170. unsigned best_mode = 0;
  171. grub_err_t err;
  172. unsigned bpp;
  173. int found = 0;
  174. unsigned long long best_volume = 0;
  175. depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
  176. >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
  177. /* Keep current mode if possible. */
  178. if (gop->mode->info)
  179. {
  180. bpp = grub_video_gop_get_bpp (gop->mode->info);
  181. if (bpp && ((width == gop->mode->info->width
  182. && height == gop->mode->info->height)
  183. || (width == 0 && height == 0))
  184. && (depth == bpp || depth == 0))
  185. {
  186. grub_dprintf ("video", "GOP: keeping mode %d\n", gop->mode->mode);
  187. best_mode = gop->mode->mode;
  188. found = 1;
  189. }
  190. }
  191. if (!found)
  192. {
  193. unsigned mode;
  194. grub_dprintf ("video", "GOP: %d modes detected\n", gop->mode->max_mode);
  195. for (mode = 0; mode < gop->mode->max_mode; mode++)
  196. {
  197. grub_efi_uintn_t size;
  198. grub_efi_status_t status;
  199. status = efi_call_4 (gop->query_mode, gop, mode, &size, &info);
  200. if (status)
  201. {
  202. info = 0;
  203. break;
  204. }
  205. grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width,
  206. info->height);
  207. bpp = grub_video_gop_get_bpp (info);
  208. if (!bpp)
  209. {
  210. grub_dprintf ("video", "GOP: mode %d: incompatible pixel mode\n",
  211. mode);
  212. continue;
  213. }
  214. grub_dprintf ("video", "GOP: mode %d: depth %d\n", mode, bpp);
  215. if (!(((info->width == width && info->height == height)
  216. || (width == 0 && height == 0))
  217. && (bpp == depth || depth == 0)))
  218. {
  219. grub_dprintf ("video", "GOP: mode %d: rejected\n", mode);
  220. continue;
  221. }
  222. if (best_volume < ((unsigned long long) info->width)
  223. * ((unsigned long long) info->height)
  224. * ((unsigned long long) bpp))
  225. {
  226. best_volume = ((unsigned long long) info->width)
  227. * ((unsigned long long) info->height)
  228. * ((unsigned long long) bpp);
  229. best_mode = mode;
  230. }
  231. found = 1;
  232. }
  233. }
  234. if (!found)
  235. {
  236. grub_dprintf ("video", "GOP: no mode found\n");
  237. return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found");
  238. }
  239. if (best_mode != gop->mode->mode)
  240. {
  241. if (!restore_needed)
  242. {
  243. old_mode = gop->mode->mode;
  244. restore_needed = 1;
  245. }
  246. efi_call_2 (gop->set_mode, gop, best_mode);
  247. }
  248. info = gop->mode->info;
  249. err = grub_video_gop_fill_mode_info (info, &framebuffer.mode_info);
  250. if (err)
  251. {
  252. grub_dprintf ("video", "GOP: couldn't fill mode info\n");
  253. return err;
  254. }
  255. framebuffer.ptr = (void *) (grub_addr_t) gop->mode->fb_base;
  256. grub_dprintf ("video", "GOP: initialising FB @ %p %dx%dx%d\n",
  257. framebuffer.ptr, framebuffer.mode_info.width,
  258. framebuffer.mode_info.height, framebuffer.mode_info.bpp);
  259. err = grub_video_fb_create_render_target_from_pointer
  260. (&framebuffer.render_target, &framebuffer.mode_info, framebuffer.ptr);
  261. if (err)
  262. {
  263. grub_dprintf ("video", "GOP: Couldn't create FB target\n");
  264. return err;
  265. }
  266. err = grub_video_fb_set_active_render_target (framebuffer.render_target);
  267. if (err)
  268. {
  269. grub_dprintf ("video", "GOP: Couldn't set FB target\n");
  270. return err;
  271. }
  272. err = grub_video_fb_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS,
  273. grub_video_fbstd_colors);
  274. if (err)
  275. grub_dprintf ("video", "GOP: Couldn't set palette\n");
  276. else
  277. grub_dprintf ("video", "GOP: Success\n");
  278. return err;
  279. }
  280. static grub_err_t
  281. grub_video_gop_swap_buffers (void)
  282. {
  283. /* TODO: Implement buffer swapping. */
  284. return GRUB_ERR_NONE;
  285. }
  286. static grub_err_t
  287. grub_video_gop_set_active_render_target (struct grub_video_render_target *target)
  288. {
  289. if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY)
  290. target = framebuffer.render_target;
  291. return grub_video_fb_set_active_render_target (target);
  292. }
  293. static grub_err_t
  294. grub_video_gop_get_info_and_fini (struct grub_video_mode_info *mode_info,
  295. void **framebuf)
  296. {
  297. grub_memcpy (mode_info, &(framebuffer.mode_info), sizeof (*mode_info));
  298. *framebuf = (char *) framebuffer.ptr;
  299. grub_video_fb_fini ();
  300. return GRUB_ERR_NONE;
  301. }
  302. static struct grub_video_adapter grub_video_gop_adapter =
  303. {
  304. .name = "EFI GOP driver",
  305. .id = GRUB_VIDEO_DRIVER_EFI_GOP,
  306. .init = grub_video_gop_init,
  307. .fini = grub_video_gop_fini,
  308. .setup = grub_video_gop_setup,
  309. .get_info = grub_video_fb_get_info,
  310. .get_info_and_fini = grub_video_gop_get_info_and_fini,
  311. .set_palette = grub_video_fb_set_palette,
  312. .get_palette = grub_video_fb_get_palette,
  313. .set_viewport = grub_video_fb_set_viewport,
  314. .get_viewport = grub_video_fb_get_viewport,
  315. .map_color = grub_video_fb_map_color,
  316. .map_rgb = grub_video_fb_map_rgb,
  317. .map_rgba = grub_video_fb_map_rgba,
  318. .unmap_color = grub_video_fb_unmap_color,
  319. .fill_rect = grub_video_fb_fill_rect,
  320. .blit_bitmap = grub_video_fb_blit_bitmap,
  321. .blit_render_target = grub_video_fb_blit_render_target,
  322. .scroll = grub_video_fb_scroll,
  323. .swap_buffers = grub_video_gop_swap_buffers,
  324. .create_render_target = grub_video_fb_create_render_target,
  325. .delete_render_target = grub_video_fb_delete_render_target,
  326. .set_active_render_target = grub_video_gop_set_active_render_target,
  327. .get_active_render_target = grub_video_fb_get_active_render_target,
  328. .next = 0
  329. };
  330. GRUB_MOD_INIT(efi_gop)
  331. {
  332. if (check_protocol ())
  333. grub_video_register (&grub_video_gop_adapter);
  334. }
  335. GRUB_MOD_FINI(efi_gop)
  336. {
  337. if (restore_needed)
  338. {
  339. efi_call_2 (gop->set_mode, gop, old_mode);
  340. restore_needed = 0;
  341. }
  342. if (gop)
  343. grub_video_unregister (&grub_video_gop_adapter);
  344. }