grml...
[seabios.git] / src / bootsplash.c
1 // Option rom scanning code.
2 //
3 // Copyright (C) 2009-2010  coresystems GmbH
4 // Copyright (C) 2010  Kevin O'Connor <kevin@koconnor.net>
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "bregs.h" // struct bregs
9 #include "farptr.h" // FLATPTR_TO_SEG
10 #include "config.h" // CONFIG_*
11 #include "util.h" // dprintf
12 #include "jpeg.h" // splash
13 #include "biosvar.h" // SET_EBDA
14 #include "paravirt.h" // romfile_find
15 #include "vbe.h" // struct vbe_info
16 #include "bmp.h"
17
18 /****************************************************************
19  * Helper functions
20  ****************************************************************/
21
22 // Call int10 vga handler.
23 static void
24 call16_int10(struct bregs *br)
25 {
26     br->flags = F_IF;
27     start_preempt();
28     call16_int(0x10, br);
29     finish_preempt();
30 }
31
32
33 /****************************************************************
34  * VGA text / graphics console
35  ****************************************************************/
36
37 void
38 enable_vga_console(void)
39 {
40     dprintf(1, "Turning on vga text mode console\n");
41     struct bregs br;
42
43     /* Enable VGA text mode */
44     memset(&br, 0, sizeof(br));
45     br.ax = 0x0003;
46     call16_int10(&br);
47
48     // Write to screen.
49     printf("SeaBIOS (version %s)\n\n", VERSION);
50 }
51
52 static int
53 find_videomode(struct vbe_info *vesa_info, struct vbe_mode_info *mode_info
54                , int width, int height, int bpp_req)
55 {
56     dprintf(3, "Finding vesa mode with dimensions %d/%d\n", width, height);
57     u16 *videomodes = SEGOFF_TO_FLATPTR(vesa_info->video_mode);
58     for (;; videomodes++) {
59         u16 videomode = *videomodes;
60         if (videomode == 0xffff) {
61             dprintf(1, "Unable to find vesa video mode dimensions %d/%d\n"
62                     , width, height);
63             return -1;
64         }
65         struct bregs br;
66         memset(&br, 0, sizeof(br));
67         br.ax = 0x4f01;
68         br.cx = (1 << 14) | videomode;
69         br.di = FLATPTR_TO_OFFSET(mode_info);
70         br.es = FLATPTR_TO_SEG(mode_info);
71         call16_int10(&br);
72         if (br.ax != 0x4f) {
73             dprintf(1, "get_mode failed.\n");
74             continue;
75         }
76         if (mode_info->xres != width
77             || mode_info->yres != height)
78             continue;
79         u8 depth = mode_info->bits_per_pixel;
80         if (bpp_req == 0) {
81             if (depth != 16 && depth != 24 && depth != 32)
82                 continue;
83         } else {
84             if (depth != bpp_req)
85                 continue;
86         }
87         return videomode;
88     }
89 }
90
91 static int BootsplashActive;
92
93 void
94 enable_bootsplash(void)
95 {
96     if (!CONFIG_BOOTSPLASH)
97         return;
98     /* splash picture can be bmp or jpeg file */
99     dprintf(3, "Checking for bootsplash\n");
100     u8 type = 0; /* 0 means jpg, 1 means bmp, default is 0=jpg */
101     int filesize;
102     u8 *filedata = romfile_loadfile("bootsplash.jpg", &filesize);
103     if (!filedata) {
104         filedata = romfile_loadfile("bootsplash.bmp", &filesize);
105         if (!filedata)
106             return;
107         type = 1;
108     }
109     dprintf(3, "start showing bootsplash\n");
110
111     u8 *picture = NULL; /* data buff used to be flushed to the video buf */
112     struct jpeg_decdata *jpeg = NULL;
113     struct bmp_decdata *bmp = NULL;
114     struct vbe_info *vesa_info = malloc_tmplow(sizeof(*vesa_info));
115     struct vbe_mode_info *mode_info = malloc_tmplow(sizeof(*mode_info));
116     if (!vesa_info || !mode_info) {
117         warn_noalloc();
118         goto done;
119     }
120
121     /* Check whether we have a VESA 2.0 compliant BIOS */
122     memset(vesa_info, 0, sizeof(struct vbe_info));
123     vesa_info->signature = VBE2_SIGNATURE;
124     struct bregs br;
125     memset(&br, 0, sizeof(br));
126     br.ax = 0x4f00;
127     br.di = FLATPTR_TO_OFFSET(vesa_info);
128     br.es = FLATPTR_TO_SEG(vesa_info);
129     call16_int10(&br);
130     if (vesa_info->signature != VESA_SIGNATURE) {
131         dprintf(1,"No VBE2 found.\n");
132         goto done;
133     }
134
135     /* Print some debugging information about our card. */
136     char *vendor = SEGOFF_TO_FLATPTR(vesa_info->oem_vendor_string);
137     char *product = SEGOFF_TO_FLATPTR(vesa_info->oem_product_string);
138     dprintf(3, "VESA %d.%d\nVENDOR: %s\nPRODUCT: %s\n",
139             vesa_info->version>>8, vesa_info->version&0xff,
140             vendor, product);
141
142     int ret, width, height;
143     int bpp_require = 0;
144     if (type == 0) {
145         jpeg = jpeg_alloc();
146         if (!jpeg) {
147             warn_noalloc();
148             goto done;
149         }
150         /* Parse jpeg and get image size. */
151         dprintf(5, "Decoding bootsplash.jpg\n");
152         ret = jpeg_decode(jpeg, filedata);
153         if (ret) {
154             dprintf(1, "jpeg_decode failed with return code %d...\n", ret);
155             goto done;
156         }
157         jpeg_get_size(jpeg, &width, &height);
158     } else {
159         bmp = bmp_alloc();
160         if (!bmp) {
161             warn_noalloc();
162             goto done;
163         }
164         /* Parse bmp and get image size. */
165         dprintf(5, "Decoding bootsplash.bmp\n");
166         ret = bmp_decode(bmp, filedata, filesize);
167         if (ret) {
168             dprintf(1, "bmp_decode failed with return code %d...\n", ret);
169             goto done;
170         }
171         bmp_get_size(bmp, &width, &height);
172         bpp_require = 24;
173     }
174     /* jpeg would use 16 or 24 bpp video mode, BMP use 24bpp mode only */
175
176     // Try to find a graphics mode with the corresponding dimensions.
177     int videomode = find_videomode(vesa_info, mode_info, width, height,
178                                        bpp_require);
179     if (videomode < 0) {
180         dprintf(1, "failed to find a videomode with %dx%d %dbpp (0=any).\n",
181                     width, height, bpp_require);
182         goto done;
183     }
184     void *framebuffer = (void *)mode_info->phys_base;
185     int depth = mode_info->bits_per_pixel;
186     dprintf(3, "mode: %04x\n", videomode);
187     dprintf(3, "framebuffer: %p\n", framebuffer);
188     dprintf(3, "bytes per scanline: %d\n", mode_info->bytes_per_scanline);
189     dprintf(3, "bits per pixel: %d\n", depth);
190
191     // Allocate space for image and decompress it.
192     int imagesize = height * mode_info->bytes_per_scanline;
193     picture = malloc_tmphigh(imagesize);
194     if (!picture) {
195         warn_noalloc();
196         goto done;
197     }
198
199     if (type == 0) {
200         dprintf(5, "Decompressing bootsplash.jpg\n");
201         ret = jpeg_show(jpeg, picture, width, height, depth,
202                             mode_info->bytes_per_scanline);
203         if (ret) {
204             dprintf(1, "jpeg_show failed with return code %d...\n", ret);
205             goto done;
206         }
207     } else {
208         dprintf(5, "Decompressing bootsplash.bmp\n");
209         ret = bmp_show(bmp, picture, width, height, depth,
210                            mode_info->bytes_per_scanline);
211         if (ret) {
212             dprintf(1, "bmp_show failed with return code %d...\n", ret);
213             goto done;
214         }
215     }
216
217     /* Switch to graphics mode */
218     dprintf(5, "Switching to graphics mode\n");
219     memset(&br, 0, sizeof(br));
220     br.ax = 0x4f02;
221     br.bx = (1 << 14) | videomode;
222     call16_int10(&br);
223     if (br.ax != 0x4f) {
224         dprintf(1, "set_mode failed.\n");
225         goto done;
226     }
227
228     /* Show the picture */
229     dprintf(5, "Showing bootsplash picture\n");
230     iomemcpy(framebuffer, picture, imagesize);
231     dprintf(5, "Bootsplash copy complete\n");
232     BootsplashActive = 1;
233
234 done:
235     free(filedata);
236     free(picture);
237     free(vesa_info);
238     free(mode_info);
239     free(jpeg);
240     free(bmp);
241     return;
242 }
243
244 void
245 disable_bootsplash(void)
246 {
247     if (!CONFIG_BOOTSPLASH || !BootsplashActive)
248         return;
249     BootsplashActive = 0;
250     enable_vga_console();
251 }