]>
Commit | Line | Data |
---|---|---|
cd051479 VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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 | ||
19 | #include <grub/machine/memory.h> | |
20 | #include <grub/memory.h> | |
21 | #ifdef GRUB_MACHINE_PCBIOS | |
22 | #include <grub/machine/biosnum.h> | |
23 | #endif | |
24 | #include <grub/multiboot.h> | |
25 | #include <grub/cpu/multiboot.h> | |
26 | #include <grub/disk.h> | |
27 | #include <grub/device.h> | |
28 | #include <grub/partition.h> | |
29 | #include <grub/mm.h> | |
30 | #include <grub/misc.h> | |
31 | #include <grub/env.h> | |
884ade56 VS |
32 | #include <grub/video.h> |
33 | ||
34 | #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU) | |
35 | #include <grub/i386/pc/vbe.h> | |
36 | #define DEFAULT_VIDEO_MODE "text" | |
37 | #define HAS_VGA_TEXT 1 | |
38 | #else | |
39 | #define DEFAULT_VIDEO_MODE "auto" | |
40 | #define HAS_VGA_TEXT 0 | |
41 | #endif | |
cd051479 VS |
42 | |
43 | struct module | |
44 | { | |
45 | struct module *next; | |
46 | grub_addr_t start; | |
47 | grub_size_t size; | |
48 | char *cmdline; | |
49 | int cmdline_size; | |
50 | }; | |
51 | ||
52 | struct module *modules, *modules_last; | |
53 | static grub_size_t cmdline_size; | |
54 | static grub_size_t total_modcmd; | |
55 | static unsigned modcnt; | |
56 | static char *cmdline = NULL; | |
57 | static grub_uint32_t bootdev; | |
58 | static int bootdev_set; | |
884ade56 VS |
59 | static int accepts_video; |
60 | ||
61 | void | |
62 | grub_multiboot_set_accepts_video (int val) | |
63 | { | |
64 | accepts_video = val; | |
65 | } | |
cd051479 VS |
66 | |
67 | /* Return the length of the Multiboot mmap that will be needed to allocate | |
68 | our platform's map. */ | |
69 | static grub_uint32_t | |
70 | grub_get_multiboot_mmap_len (void) | |
71 | { | |
72 | grub_size_t count = 0; | |
73 | ||
74 | auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); | |
75 | int NESTED_FUNC_ATTR hook (grub_uint64_t addr __attribute__ ((unused)), | |
76 | grub_uint64_t size __attribute__ ((unused)), | |
77 | grub_uint32_t type __attribute__ ((unused))) | |
78 | { | |
79 | count++; | |
80 | return 0; | |
81 | } | |
82 | ||
83 | grub_mmap_iterate (hook); | |
84 | ||
85 | return count * sizeof (struct multiboot_mmap_entry); | |
86 | } | |
87 | ||
88 | grub_size_t | |
89 | grub_multiboot_get_mbi_size (void) | |
90 | { | |
91 | return sizeof (struct multiboot_info) + ALIGN_UP (cmdline_size, 4) | |
92 | + modcnt * sizeof (struct multiboot_mod_list) + total_modcmd | |
884ade56 VS |
93 | + ALIGN_UP (sizeof(PACKAGE_STRING), 4) + grub_get_multiboot_mmap_len () |
94 | + 256 * sizeof (struct multiboot_color); | |
cd051479 VS |
95 | } |
96 | ||
97 | /* Fill previously allocated Multiboot mmap. */ | |
98 | static void | |
99 | grub_fill_multiboot_mmap (struct multiboot_mmap_entry *first_entry) | |
100 | { | |
101 | struct multiboot_mmap_entry *mmap_entry = (struct multiboot_mmap_entry *) first_entry; | |
102 | ||
103 | auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); | |
104 | int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type) | |
105 | { | |
106 | mmap_entry->addr = addr; | |
107 | mmap_entry->len = size; | |
108 | switch (type) | |
109 | { | |
110 | case GRUB_MACHINE_MEMORY_AVAILABLE: | |
111 | mmap_entry->type = MULTIBOOT_MEMORY_AVAILABLE; | |
112 | break; | |
113 | ||
114 | default: | |
115 | mmap_entry->type = MULTIBOOT_MEMORY_RESERVED; | |
116 | break; | |
117 | } | |
118 | mmap_entry->size = sizeof (struct multiboot_mmap_entry) - sizeof (mmap_entry->size); | |
119 | mmap_entry++; | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | grub_mmap_iterate (hook); | |
125 | } | |
126 | ||
884ade56 VS |
127 | static grub_err_t |
128 | set_video_mode (void) | |
129 | { | |
130 | grub_err_t err; | |
131 | const char *modevar; | |
132 | ||
133 | if (accepts_video || !HAS_VGA_TEXT) | |
134 | { | |
135 | modevar = grub_env_get ("gfxpayload"); | |
136 | if (! modevar || *modevar == 0) | |
b0b13907 | 137 | err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0); |
884ade56 VS |
138 | else |
139 | { | |
140 | char *tmp; | |
141 | tmp = grub_malloc (grub_strlen (modevar) | |
142 | + sizeof (DEFAULT_VIDEO_MODE) + 1); | |
143 | if (! tmp) | |
144 | return grub_errno; | |
145 | grub_sprintf (tmp, "%s;" DEFAULT_VIDEO_MODE, modevar); | |
b0b13907 | 146 | err = grub_video_set_mode (tmp, 0, 0); |
884ade56 VS |
147 | grub_free (tmp); |
148 | } | |
149 | } | |
150 | else | |
b0b13907 | 151 | err = grub_video_set_mode ("text", 0, 0); |
884ade56 VS |
152 | |
153 | return err; | |
154 | } | |
155 | ||
156 | static grub_err_t | |
157 | retrieve_video_parameters (struct multiboot_info *mbi, | |
158 | grub_uint8_t *ptrorig, grub_uint32_t ptrdest) | |
159 | { | |
160 | grub_err_t err; | |
161 | struct grub_video_mode_info mode_info; | |
162 | void *framebuffer; | |
163 | grub_video_driver_id_t driv_id; | |
164 | struct grub_video_palette_data palette[256]; | |
165 | ||
166 | err = set_video_mode (); | |
167 | if (err) | |
168 | { | |
169 | grub_print_error (); | |
170 | grub_errno = GRUB_ERR_NONE; | |
171 | } | |
172 | ||
173 | grub_video_get_palette (0, ARRAY_SIZE (palette), palette); | |
174 | ||
175 | driv_id = grub_video_get_driver_id (); | |
176 | if (driv_id == GRUB_VIDEO_DRIVER_NONE) | |
177 | return GRUB_ERR_NONE; | |
178 | ||
179 | err = grub_video_get_info_and_fini (&mode_info, &framebuffer); | |
180 | if (err) | |
181 | return err; | |
182 | ||
183 | mbi->framebuffer_addr = (grub_addr_t) framebuffer; | |
184 | mbi->framebuffer_pitch = mode_info.pitch; | |
185 | ||
186 | mbi->framebuffer_width = mode_info.width; | |
187 | mbi->framebuffer_height = mode_info.height; | |
188 | ||
189 | mbi->framebuffer_bpp = mode_info.bpp; | |
190 | ||
191 | if (mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) | |
192 | { | |
193 | struct multiboot_color *mb_palette; | |
194 | unsigned i; | |
195 | mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED; | |
196 | mbi->framebuffer_palette_addr = ptrdest; | |
197 | mbi->framebuffer_palette_num_colors = mode_info.number_of_colors; | |
198 | if (mbi->framebuffer_palette_num_colors > ARRAY_SIZE (palette)) | |
199 | mbi->framebuffer_palette_num_colors = ARRAY_SIZE (palette); | |
200 | mb_palette = (struct multiboot_color *) ptrorig; | |
201 | for (i = 0; i < mbi->framebuffer_palette_num_colors; i++) | |
202 | { | |
203 | mb_palette[i].red = palette[i].r; | |
204 | mb_palette[i].green = palette[i].g; | |
205 | mb_palette[i].blue = palette[i].b; | |
206 | } | |
207 | ptrorig += mbi->framebuffer_palette_num_colors | |
208 | * sizeof (struct multiboot_color); | |
209 | ptrdest += mbi->framebuffer_palette_num_colors | |
210 | * sizeof (struct multiboot_color); | |
211 | } | |
212 | else | |
213 | { | |
214 | mbi->framebuffer_type = MULTIBOOT_FRAMEBUFFER_TYPE_RGB; | |
215 | mbi->framebuffer_red_field_position = mode_info.green_field_pos; | |
216 | mbi->framebuffer_red_mask_size = mode_info.green_mask_size; | |
217 | mbi->framebuffer_green_field_position = mode_info.green_field_pos; | |
218 | mbi->framebuffer_green_mask_size = mode_info.green_mask_size; | |
219 | mbi->framebuffer_blue_field_position = mode_info.blue_field_pos; | |
220 | mbi->framebuffer_blue_mask_size = mode_info.blue_mask_size; | |
221 | } | |
222 | ||
223 | mbi->flags |= MULTIBOOT_INFO_FRAMEBUFFER_INFO; | |
224 | ||
225 | return GRUB_ERR_NONE; | |
226 | } | |
227 | ||
cd051479 VS |
228 | grub_err_t |
229 | grub_multiboot_make_mbi (void *orig, grub_uint32_t dest, grub_off_t buf_off, | |
230 | grub_size_t bufsize) | |
231 | { | |
232 | grub_uint8_t *ptrorig = (grub_uint8_t *) orig + buf_off; | |
233 | grub_uint32_t ptrdest = dest + buf_off; | |
234 | struct multiboot_info *mbi; | |
235 | struct multiboot_mod_list *modlist; | |
236 | unsigned i; | |
237 | struct module *cur; | |
238 | grub_size_t mmap_size; | |
884ade56 | 239 | grub_err_t err; |
cd051479 VS |
240 | |
241 | if (bufsize < grub_multiboot_get_mbi_size ()) | |
242 | return grub_error (GRUB_ERR_OUT_OF_MEMORY, "mbi buffer is too small"); | |
243 | ||
244 | mbi = (struct multiboot_info *) ptrorig; | |
245 | ptrorig += sizeof (*mbi); | |
246 | ptrdest += sizeof (*mbi); | |
247 | grub_memset (mbi, 0, sizeof (*mbi)); | |
248 | ||
249 | grub_memcpy (ptrorig, cmdline, cmdline_size); | |
250 | mbi->flags |= MULTIBOOT_INFO_CMDLINE; | |
251 | mbi->cmdline = ptrdest; | |
252 | ptrorig += ALIGN_UP (cmdline_size, 4); | |
253 | ptrdest += ALIGN_UP (cmdline_size, 4); | |
254 | ||
255 | grub_memcpy (ptrorig, PACKAGE_STRING, sizeof(PACKAGE_STRING)); | |
256 | mbi->flags |= MULTIBOOT_INFO_BOOT_LOADER_NAME; | |
257 | mbi->boot_loader_name = ptrdest; | |
258 | ptrorig += ALIGN_UP (sizeof(PACKAGE_STRING), 4); | |
259 | ptrdest += ALIGN_UP (sizeof(PACKAGE_STRING), 4); | |
260 | ||
261 | if (modcnt) | |
262 | { | |
263 | mbi->flags |= MULTIBOOT_INFO_MODS; | |
264 | mbi->mods_addr = ptrdest; | |
265 | mbi->mods_count = modcnt; | |
266 | modlist = (struct multiboot_mod_list *) ptrorig; | |
267 | ptrorig += modcnt * sizeof (struct multiboot_mod_list); | |
268 | ptrdest += modcnt * sizeof (struct multiboot_mod_list); | |
269 | ||
270 | for (i = 0, cur = modules; i < modcnt; i++, cur = cur->next) | |
271 | { | |
272 | modlist[i].mod_start = cur->start; | |
273 | modlist[i].mod_end = modlist[i].mod_start + cur->size; | |
274 | modlist[i].cmdline = ptrdest; | |
275 | grub_memcpy (ptrorig, cur->cmdline, cur->cmdline_size); | |
276 | ptrorig += ALIGN_UP (cur->cmdline_size, 4); | |
277 | ptrdest += ALIGN_UP (cur->cmdline_size, 4); | |
278 | } | |
279 | } | |
280 | else | |
281 | { | |
282 | mbi->mods_addr = 0; | |
283 | mbi->mods_count = 0; | |
284 | } | |
285 | ||
286 | mmap_size = grub_get_multiboot_mmap_len (); | |
287 | grub_fill_multiboot_mmap ((struct multiboot_mmap_entry *) ptrorig); | |
288 | mbi->mmap_length = mmap_size; | |
289 | mbi->mmap_addr = ptrdest; | |
290 | mbi->flags |= MULTIBOOT_INFO_MEM_MAP; | |
291 | ptrorig += mmap_size; | |
292 | ptrdest += mmap_size; | |
293 | ||
294 | /* Convert from bytes to kilobytes. */ | |
295 | mbi->mem_lower = grub_mmap_get_lower () / 1024; | |
296 | mbi->mem_upper = grub_mmap_get_upper () / 1024; | |
297 | mbi->flags |= MULTIBOOT_INFO_MEMORY; | |
298 | ||
299 | if (bootdev_set) | |
300 | { | |
301 | mbi->boot_device = bootdev; | |
302 | mbi->flags |= MULTIBOOT_INFO_BOOTDEV; | |
303 | } | |
304 | ||
884ade56 VS |
305 | err = retrieve_video_parameters (mbi, ptrorig, ptrdest); |
306 | if (err) | |
307 | { | |
308 | grub_print_error (); | |
309 | grub_errno = GRUB_ERR_NONE; | |
310 | } | |
311 | ||
cd051479 VS |
312 | return GRUB_ERR_NONE; |
313 | } | |
314 | ||
315 | void | |
316 | grub_multiboot_free_mbi (void) | |
317 | { | |
318 | struct module *cur, *next; | |
319 | ||
320 | cmdline_size = 0; | |
321 | total_modcmd = 0; | |
322 | modcnt = 0; | |
323 | grub_free (cmdline); | |
324 | cmdline = NULL; | |
325 | bootdev_set = 0; | |
326 | ||
327 | for (cur = modules; cur; cur = next) | |
328 | { | |
329 | next = cur->next; | |
330 | grub_free (cur->cmdline); | |
331 | grub_free (cur); | |
332 | } | |
333 | modules = NULL; | |
334 | modules_last = NULL; | |
335 | } | |
336 | ||
337 | grub_err_t | |
338 | grub_multiboot_init_mbi (int argc, char *argv[]) | |
339 | { | |
340 | grub_ssize_t len = 0; | |
341 | char *p; | |
342 | int i; | |
343 | ||
344 | grub_multiboot_free_mbi (); | |
345 | ||
346 | for (i = 0; i < argc; i++) | |
347 | len += grub_strlen (argv[i]) + 1; | |
348 | if (len == 0) | |
349 | len = 1; | |
350 | ||
351 | cmdline = p = grub_malloc (len); | |
352 | if (! cmdline) | |
353 | return grub_errno; | |
354 | cmdline_size = len; | |
355 | ||
356 | for (i = 0; i < argc; i++) | |
357 | { | |
358 | p = grub_stpcpy (p, argv[i]); | |
359 | *(p++) = ' '; | |
360 | } | |
361 | ||
362 | /* Remove the space after the last word. */ | |
363 | if (p != cmdline) | |
364 | p--; | |
365 | *p = '\0'; | |
366 | ||
367 | return GRUB_ERR_NONE; | |
368 | } | |
369 | ||
370 | grub_err_t | |
371 | grub_multiboot_add_module (grub_addr_t start, grub_size_t size, | |
372 | int argc, char *argv[]) | |
373 | { | |
374 | struct module *newmod; | |
375 | char *p; | |
376 | grub_ssize_t len = 0; | |
377 | int i; | |
378 | ||
379 | newmod = grub_malloc (sizeof (*newmod)); | |
380 | if (!newmod) | |
381 | return grub_errno; | |
382 | newmod->start = start; | |
383 | newmod->size = size; | |
384 | ||
385 | for (i = 0; i < argc; i++) | |
386 | len += grub_strlen (argv[i]) + 1; | |
387 | ||
388 | if (len == 0) | |
389 | len = 1; | |
390 | ||
391 | newmod->cmdline = p = grub_malloc (len); | |
392 | if (! newmod->cmdline) | |
393 | { | |
394 | grub_free (newmod); | |
395 | return grub_errno; | |
396 | } | |
397 | newmod->cmdline_size = len; | |
398 | total_modcmd += ALIGN_UP (len, 4); | |
399 | ||
400 | for (i = 0; i < argc; i++) | |
401 | { | |
402 | p = grub_stpcpy (p, argv[i]); | |
403 | *(p++) = ' '; | |
404 | } | |
405 | ||
406 | /* Remove the space after the last word. */ | |
407 | if (p != newmod->cmdline) | |
408 | p--; | |
409 | *p = '\0'; | |
410 | ||
411 | if (modules_last) | |
412 | modules_last->next = newmod; | |
413 | else | |
414 | { | |
415 | modules = newmod; | |
416 | modules_last->next = NULL; | |
417 | } | |
418 | modules_last = newmod; | |
419 | ||
420 | modcnt++; | |
421 | ||
422 | return GRUB_ERR_NONE; | |
423 | } | |
424 | ||
425 | void | |
426 | grub_multiboot_set_bootdev (void) | |
427 | { | |
428 | char *p; | |
429 | grub_uint32_t biosdev, slice = ~0, part = ~0; | |
430 | grub_device_t dev; | |
431 | ||
432 | #ifdef GRUB_MACHINE_PCBIOS | |
433 | biosdev = grub_get_root_biosnumber (); | |
434 | #else | |
435 | biosdev = 0xffffffff; | |
436 | #endif | |
437 | ||
438 | dev = grub_device_open (0); | |
439 | if (dev && dev->disk && dev->disk->partition) | |
440 | { | |
441 | ||
442 | p = dev->disk->partition->partmap->get_name (dev->disk->partition); | |
443 | if (p) | |
444 | { | |
445 | if ((p[0] >= '0') && (p[0] <= '9')) | |
446 | { | |
447 | slice = grub_strtoul (p, &p, 0) - 1; | |
448 | ||
449 | if ((p) && (p[0] == ',')) | |
450 | p++; | |
451 | } | |
452 | ||
453 | if ((p[0] >= 'a') && (p[0] <= 'z')) | |
454 | part = p[0] - 'a'; | |
455 | } | |
456 | } | |
457 | if (dev) | |
458 | grub_device_close (dev); | |
459 | ||
460 | bootdev = ((biosdev & 0xff) << 24) | ((slice & 0xff) << 16) | |
461 | | ((part & 0xff) << 8) | 0xff; | |
462 | bootdev_set = 1; | |
463 | } |