Merge branch 'afs' into befs2
[grub2/phcoder.git] / loader / i386 / bsdXX.c
blob3f15579dd22a923586bd34d6399e1c5931e08fc9
1 #include <grub/loader.h>
2 #include <grub/cpu/bsd.h>
3 #include <grub/mm.h>
4 #include <grub/elf.h>
5 #include <grub/misc.h>
6 #include <grub/i386/loader.h>
8 #define ALIGN_PAGE(a) ALIGN_UP (a, 4096)
10 static inline grub_err_t
11 load (grub_file_t file, void *where, grub_off_t off, grub_size_t size)
13 if (PTR_TO_UINT32 (where) + size > grub_os_area_addr + grub_os_area_size)
14 return grub_error (GRUB_ERR_OUT_OF_RANGE,
15 "Not enough memory for the module");
16 if (grub_file_seek (file, off) == (grub_off_t) -1)
17 return grub_errno;
18 if (grub_file_read (file, where, size)
19 != (grub_ssize_t) size)
21 if (grub_errno)
22 return grub_errno;
23 else
24 return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
26 return GRUB_ERR_NONE;
29 static inline grub_err_t
30 read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr)
32 if (grub_file_seek (file, 0) == (grub_off_t) -1)
33 return grub_errno;
35 if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e))
37 if (grub_errno)
38 return grub_errno;
39 else
40 return grub_error (GRUB_ERR_BAD_OS, "file is too short");
43 if (e->e_ident[EI_MAG0] != ELFMAG0
44 || e->e_ident[EI_MAG1] != ELFMAG1
45 || e->e_ident[EI_MAG2] != ELFMAG2
46 || e->e_ident[EI_MAG3] != ELFMAG3
47 || e->e_ident[EI_VERSION] != EV_CURRENT
48 || e->e_version != EV_CURRENT)
49 return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
51 if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
52 return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic");
54 *shdr = grub_malloc (e->e_shnum * e->e_shentsize);
55 if (! *shdr)
56 return grub_errno;
58 if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1)
59 return grub_errno;
61 if (grub_file_read (file, *shdr, e->e_shnum * e->e_shentsize)
62 != e->e_shnum * e->e_shentsize)
64 if (grub_errno)
65 return grub_errno;
66 else
67 return grub_error (GRUB_ERR_BAD_OS, "file is truncated");
70 return GRUB_ERR_NONE;
73 /* On i386 FreeBSD uses "elf module" approarch for 32-bit variant
74 and "elf obj module" for 64-bit variant. However it may differ on other
75 platforms. So I keep both versions. */
76 #if OBJSYM
77 grub_err_t
78 SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc,
79 char *argv[], grub_addr_t *kern_end)
81 Elf_Ehdr e;
82 Elf_Shdr *s;
83 char *shdr;
84 grub_addr_t curload, module;
85 grub_err_t err;
87 err = read_headers (file, &e, &shdr);
88 if (err)
89 return err;
91 curload = module = ALIGN_PAGE (*kern_end);
93 for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
94 + e.e_shnum * e.e_shentsize);
95 s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
97 if (s->sh_size == 0)
98 continue;
100 if (s->sh_addralign)
101 curload = ALIGN_UP (curload, s->sh_addralign);
102 s->sh_addr = curload;
104 grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
105 (unsigned) curload, (int) s->sh_size,
106 (int) s->sh_addralign);
108 switch (s->sh_type)
110 default:
111 case SHT_PROGBITS:
112 err = load (file, UINT_TO_PTR (curload), s->sh_offset, s->sh_size);
113 if (err)
114 return err;
115 break;
116 case SHT_NOBITS:
117 if (curload + s->sh_size > grub_os_area_addr + grub_os_area_size)
118 return grub_error (GRUB_ERR_OUT_OF_RANGE,
119 "Not enough memory for the module");
120 grub_memset (UINT_TO_PTR (curload), 0, s->sh_size);
121 break;
123 curload += s->sh_size;
126 *kern_end = ALIGN_PAGE (curload);
128 err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE_OBJ,
129 argc - 1, argv + 1, module,
130 curload - module);
131 if (! err)
132 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA
133 | FREEBSD_MODINFOMD_ELFHDR,
134 &e, sizeof (e));
135 if (! err)
136 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA
137 | FREEBSD_MODINFOMD_SHDR,
138 shdr, e.e_shnum * e.e_shentsize);
140 return err;
143 #else
145 grub_err_t
146 SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[],
147 grub_addr_t *kern_end)
149 Elf_Ehdr e;
150 Elf_Shdr *s;
151 char *shdr;
152 grub_addr_t curload, module;
153 grub_err_t err;
155 err = read_headers (file, &e, &shdr);
156 if (err)
157 return err;
159 curload = module = ALIGN_PAGE (*kern_end);
161 for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr
162 + e.e_shnum * e.e_shentsize);
163 s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
165 if (s->sh_size == 0)
166 continue;
168 if (! (s->sh_flags & SHF_ALLOC))
169 continue;
171 grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n",
172 (unsigned) curload, (int) s->sh_size,
173 (int) s->sh_addralign);
175 switch (s->sh_type)
177 default:
178 case SHT_PROGBITS:
179 err = load (file, UINT_TO_PTR (module + s->sh_addr),
180 s->sh_offset, s->sh_size);
181 if (err)
182 return err;
183 break;
184 case SHT_NOBITS:
185 if (module + s->sh_addr + s->sh_size
186 > grub_os_area_addr + grub_os_area_size)
187 return grub_error (GRUB_ERR_OUT_OF_RANGE,
188 "Not enough memory for the module");
189 grub_memset (UINT_TO_PTR (module + s->sh_addr), 0, s->sh_size);
190 break;
192 if (curload < module + s->sh_addr + s->sh_size)
193 curload = module + s->sh_addr + s->sh_size;
196 load (file, UINT_TO_PTR (module), 0, sizeof (e));
197 if (curload < module + sizeof (e))
198 curload = module + sizeof (e);
200 load (file, UINT_TO_PTR (module + e.e_shoff), e.e_shoff,
201 e.e_shnum * e.e_shentsize);
202 if (curload < module + e.e_shoff + e.e_shnum * e.e_shentsize)
203 curload = module + e.e_shoff + e.e_shnum * e.e_shentsize;
205 load (file, UINT_TO_PTR (module + e.e_phoff), e.e_phoff,
206 e.e_phnum * e.e_phentsize);
207 if (curload < module + e.e_phoff + e.e_phnum * e.e_phentsize)
208 curload = module + e.e_phoff + e.e_phnum * e.e_phentsize;
210 *kern_end = curload;
212 grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE,
213 argc - 1, argv + 1, module,
214 curload - module);
215 return SUFFIX (grub_freebsd_load_elf_meta) (file, kern_end);
218 #endif
220 grub_err_t
221 SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end)
223 grub_err_t err;
224 Elf_Ehdr e;
225 Elf_Shdr *s;
226 char *shdr;
227 unsigned symoff, stroff, symsize, strsize;
228 grub_addr_t curload;
229 grub_freebsd_addr_t symstart, symend, symentsize, dynamic;
230 Elf_Sym *sym;
231 const char *str;
232 unsigned i;
234 err = read_headers (file, &e, &shdr);
235 if (err)
236 return err;
238 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
239 FREEBSD_MODINFOMD_ELFHDR, &e,
240 sizeof (e));
241 if (err)
242 return err;
244 for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr
245 + e.e_shnum * e.e_shentsize);
246 s = (Elf_Shdr *) ((char *) s + e.e_shentsize))
247 if (s->sh_type == SHT_SYMTAB)
248 break;
249 if (s >= (Elf_Shdr *) ((char *) shdr
250 + e.e_shnum * e.e_shentsize))
251 return grub_error (GRUB_ERR_BAD_OS, "no symbol table");
252 symoff = s->sh_offset;
253 symsize = s->sh_size;
254 symentsize = s->sh_entsize;
255 s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link);
256 stroff = s->sh_offset;
257 strsize = s->sh_size;
259 if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize
260 > grub_os_area_addr + grub_os_area_size)
261 return grub_error (GRUB_ERR_OUT_OF_RANGE,
262 "Not enough memory for kernel symbols");
264 symstart = curload = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t));
265 *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize;
266 curload += sizeof (grub_freebsd_addr_t);
267 if (grub_file_seek (file, symoff) == (grub_off_t) -1)
268 return grub_errno;
269 sym = (Elf_Sym *) UINT_TO_PTR (curload);
270 if (grub_file_read (file, UINT_TO_PTR (curload), symsize) !=
271 (grub_ssize_t) symsize)
273 if (! grub_errno)
274 return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
275 return grub_errno;
277 curload += symsize;
279 *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize;
280 curload += sizeof (grub_freebsd_addr_t);
281 if (grub_file_seek (file, stroff) == (grub_off_t) -1)
282 return grub_errno;
283 str = (char *) UINT_TO_PTR (curload);
284 if (grub_file_read (file, UINT_TO_PTR (curload), strsize)
285 != (grub_ssize_t) strsize)
287 if (! grub_errno)
288 return grub_error (GRUB_ERR_BAD_OS, "invalid elf");
289 return grub_errno;
291 curload += strsize;
292 curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t));
293 symend = curload;
295 for (i = 0;
296 i * symentsize < symsize;
297 i++, sym = (Elf_Sym *) ((char *) sym + symentsize))
299 const char *name = str + sym->st_name;
300 if (grub_strcmp (name, "_DYNAMIC") == 0)
301 break;
304 if (i * symentsize < symsize)
306 dynamic = sym->st_value;
307 grub_dprintf ("bsd", "dynamic = %llx\n", (unsigned long long) dynamic);
308 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
309 FREEBSD_MODINFOMD_DYNAMIC, &dynamic,
310 sizeof (dynamic));
311 if (err)
312 return err;
315 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
316 FREEBSD_MODINFOMD_SSYM, &symstart,
317 sizeof (symstart));
318 if (err)
319 return err;
321 err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA |
322 FREEBSD_MODINFOMD_ESYM, &symend,
323 sizeof (symend));
324 if (err)
325 return err;
326 *kern_end = ALIGN_PAGE (curload);
328 return GRUB_ERR_NONE;