direct-io.hg

view tools/libxc/xc_load_elf.c @ 12988:e080700efa56

[TOOLS] Fix the build. Clearly demarcate PPC-specific stuff.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Dec 13 10:23:53 2006 +0000 (2006-12-13)
parents 2dd4569e0640
children ac51e8f37108
line source
1 /******************************************************************************
2 * xc_elf_load.c
3 */
5 #include "xg_private.h"
6 #include "xc_elf.h"
7 #include <stdlib.h>
8 #include <inttypes.h>
10 #define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
11 #define round_pgdown(_p) ((_p)&PAGE_MASK)
13 static int
14 parseelfimage(
15 const char *image, unsigned long image_size,
16 struct domain_setup_info *dsi);
17 static int
18 loadelfimage(
19 const char *image, unsigned long image_size, int xch, uint32_t dom,
20 xen_pfn_t *parray, struct domain_setup_info *dsi);
21 static int
22 loadelfsymtab(
23 const char *image, int xch, uint32_t dom, xen_pfn_t *parray,
24 struct domain_setup_info *dsi);
26 /*
27 * Elf header attributes we require for each supported host platform.
28 * These are checked in parseelfimage().
29 */
30 #if defined(__ia64__)
31 #define ELFCLASS ELFCLASS64
32 #define ELFCLASS_DESC "64-bit"
34 #define ELFDATA ELFDATA2LSB
35 #define ELFDATA_DESC "Little-Endian"
37 #define ELFMACHINE EM_IA_64
38 #define ELFMACHINE_DESC "ia64"
41 #elif defined(__i386__)
42 #define ELFCLASS ELFCLASS32
43 #define ELFCLASS_DESC "32-bit"
45 #define ELFDATA ELFDATA2LSB
46 #define ELFDATA_DESC "Little-Endian"
48 #define ELFMACHINE EM_386
49 #define ELFMACHINE_DESC "i386"
52 #elif defined(__x86_64__)
53 #define ELFCLASS ELFCLASS64
54 #define ELFCLASS_DESC "64-bit"
56 #define ELFDATA ELFDATA2LSB
57 #define ELFDATA_DESC "Little-Endian"
59 #define ELFMACHINE EM_X86_64
60 #define ELFMACHINE_DESC "x86_64"
63 #elif defined(__powerpc__)
64 #define ELFCLASS ELFCLASS64
65 #define ELFCLASS_DESC "64-bit"
67 #define ELFDATA ELFDATA2MSB
68 #define ELFDATA_DESC "Big-Endian"
70 #define ELFMACHINE EM_PPC64
71 #define ELFMACHINE_DESC "ppc64"
72 #endif
74 int probe_elf(const char *image,
75 unsigned long image_size,
76 struct load_funcs *load_funcs)
77 {
78 Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
80 if ( !IS_ELF(*ehdr) )
81 return -EINVAL;
83 load_funcs->parseimage = parseelfimage;
84 load_funcs->loadimage = loadelfimage;
86 return 0;
87 }
89 static inline int is_loadable_phdr(Elf_Phdr *phdr)
90 {
91 return ((phdr->p_type == PT_LOAD) &&
92 ((phdr->p_flags & (PF_W|PF_X)) != 0));
93 }
95 /*
96 * Fallback for kernels containing only the legacy __xen_guest string
97 * and no ELF notes.
98 */
99 static int is_xen_guest_section(Elf_Shdr *shdr, const char *shstrtab)
100 {
101 return strcmp(&shstrtab[shdr->sh_name], "__xen_guest") == 0;
102 }
104 static const char *xen_guest_lookup(struct domain_setup_info *dsi, int type)
105 {
106 const char *xenguest_fallbacks[] = {
107 [XEN_ELFNOTE_ENTRY] = "VIRT_ENTRY=",
108 [XEN_ELFNOTE_HYPERCALL_PAGE] = "HYPERCALL_PAGE=",
109 [XEN_ELFNOTE_VIRT_BASE] = "VIRT_BASE=",
110 [XEN_ELFNOTE_PADDR_OFFSET] = "ELF_PADDR_OFFSET=",
111 [XEN_ELFNOTE_XEN_VERSION] = "XEN_VER=",
112 [XEN_ELFNOTE_GUEST_OS] = "GUEST_OS=",
113 [XEN_ELFNOTE_GUEST_VERSION] = "GUEST_VER=",
114 [XEN_ELFNOTE_LOADER] = "LOADER=",
115 [XEN_ELFNOTE_PAE_MODE] = "PAE=",
116 [XEN_ELFNOTE_FEATURES] = "FEATURES=",
117 [XEN_ELFNOTE_BSD_SYMTAB] = "BSD_SYMTAB=",
118 };
119 const char *fallback;
120 const char *p;
122 if ( !dsi->__xen_guest_string )
123 return NULL;
125 if ( type > sizeof(xenguest_fallbacks) )
126 return NULL;
128 if ( (fallback = xenguest_fallbacks[type]) == NULL )
129 return NULL;
131 if ( (p = strstr(dsi->__xen_guest_string,fallback)) == NULL )
132 return NULL;
134 return p + strlen(fallback);
135 }
137 static const char *xen_guest_string(struct domain_setup_info *dsi, int type)
138 {
139 const char *p = xen_guest_lookup(dsi, type);
141 /*
142 * We special case this since the __xen_guest_section treats the
143 * mere precense of the BSD_SYMTAB string as true or false.
144 */
145 if ( type == XEN_ELFNOTE_BSD_SYMTAB )
146 return p ? "yes" : "no";
148 return p;
149 }
151 static unsigned long long xen_guest_numeric(struct domain_setup_info *dsi,
152 int type, int *defined)
153 {
154 const char *p = xen_guest_lookup(dsi, type);
155 unsigned long long value;
157 if ( p == NULL )
158 return 0;
160 errno = 0;
161 value = strtoull(p, NULL, 0);
162 if ( errno < 0 )
163 return 0;
165 /* We special case this since __xen_guest_section contains a PFN
166 * for this field not a virtual address.
167 */
168 if (type == XEN_ELFNOTE_HYPERCALL_PAGE)
169 value = dsi->v_start + (value<<PAGE_SHIFT);
171 *defined = 1;
172 return value;
173 }
175 /*
176 * Interface to the Xen ELF notes.
177 */
178 #define ELFNOTE_NAME(_n_) ((void*)(_n_) + sizeof(*(_n_)))
179 #define ELFNOTE_DESC(_n_) (ELFNOTE_NAME(_n_) + (((_n_)->namesz+3)&~3))
180 #define ELFNOTE_NEXT(_n_) (ELFNOTE_DESC(_n_) + (((_n_)->descsz+3)&~3))
182 static int is_xen_elfnote_section(const char *image, Elf_Shdr *shdr)
183 {
184 Elf_Note *note;
186 if ( shdr->sh_type != SHT_NOTE )
187 return 0;
189 for ( note = (Elf_Note *)(image + shdr->sh_offset);
190 note < (Elf_Note *)(image + shdr->sh_offset + shdr->sh_size);
191 note = ELFNOTE_NEXT(note) )
192 {
193 if ( !strncmp(ELFNOTE_NAME(note), "Xen", 4) )
194 return 1;
195 }
197 return 0;
198 }
200 static Elf_Note *xen_elfnote_lookup(struct domain_setup_info *dsi, int type)
201 {
202 Elf_Note *note;
204 if ( !dsi->__elfnote_section )
205 return NULL;
207 for ( note = (Elf_Note *)dsi->__elfnote_section;
208 note < (Elf_Note *)dsi->__elfnote_section_end;
209 note = ELFNOTE_NEXT(note) )
210 {
211 if ( strncmp(ELFNOTE_NAME(note), "Xen", 4) )
212 continue;
214 if ( note->type == type )
215 return note;
216 }
218 return NULL;
219 }
221 const char *xen_elfnote_string(struct domain_setup_info *dsi, int type)
222 {
223 Elf_Note *note;
225 if ( !dsi->__elfnote_section )
226 return xen_guest_string(dsi, type);
228 note = xen_elfnote_lookup(dsi, type);
229 if ( note == NULL )
230 return NULL;
232 return (const char *)ELFNOTE_DESC(note);
233 }
235 unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi,
236 int type, int *defined)
237 {
238 Elf_Note *note;
240 *defined = 0;
242 if ( !dsi->__elfnote_section )
243 return xen_guest_numeric(dsi, type, defined);
245 note = xen_elfnote_lookup(dsi, type);
246 if ( note == NULL )
247 {
248 return 0;
249 }
251 switch ( note->descsz )
252 {
253 case 4:
254 *defined = 1;
255 return *(uint32_t*)ELFNOTE_DESC(note);
256 case 8:
257 *defined = 1;
258 return *(uint64_t*)ELFNOTE_DESC(note);
259 default:
260 xc_set_error(XC_INVALID_KERNEL,
261 "elfnotes: unknown data size %#x for numeric type note %#x\n",
262 note->descsz, type);
263 return 0;
264 }
265 }
267 static int parseelfimage(const char *image,
268 unsigned long image_len,
269 struct domain_setup_info *dsi)
270 {
271 Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
272 Elf_Phdr *phdr;
273 Elf_Shdr *shdr;
274 Elf_Addr kernstart = ~0, kernend = 0, vaddr, virt_entry;
275 const char *shstrtab, *p;
276 int h, virt_base_defined, elf_pa_off_defined, virt_entry_defined;
278 if ( !IS_ELF(*ehdr) )
279 {
280 xc_set_error(XC_INVALID_KERNEL,
281 "Kernel image does not have an ELF header.");
282 return -EINVAL;
283 }
285 if (ehdr->e_machine != ELFMACHINE)
286 {
287 xc_set_error(XC_INVALID_KERNEL,
288 "Kernel ELF architecture '%d' does not match Xen architecture '%d' (%s)",
289 ehdr->e_machine, ELFMACHINE, ELFMACHINE_DESC);
290 return -EINVAL;
291 }
292 if (ehdr->e_ident[EI_CLASS] != ELFCLASS)
293 {
294 xc_set_error(XC_INVALID_KERNEL,
295 "Kernel ELF wordsize '%d' does not match Xen wordsize '%d' (%s)",
296 ehdr->e_ident[EI_CLASS], ELFCLASS, ELFCLASS_DESC);
297 return -EINVAL;
298 }
299 if (ehdr->e_ident[EI_DATA] != ELFDATA)
300 {
301 xc_set_error(XC_INVALID_KERNEL,
302 "Kernel ELF endianness '%d' does not match Xen endianness '%d' (%s)",
303 ehdr->e_ident[EI_DATA], ELFDATA, ELFDATA_DESC);
304 return -EINVAL;
305 }
306 if (ehdr->e_type != ET_EXEC)
307 {
308 xc_set_error(XC_INVALID_KERNEL,
309 "Kernel ELF type '%d' does not match Xen type '%d'",
310 ehdr->e_type, ET_EXEC);
311 return -EINVAL;
312 }
314 if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > image_len )
315 {
316 xc_set_error(XC_INVALID_KERNEL,
317 "ELF program headers extend beyond end of image.");
318 return -EINVAL;
319 }
321 if ( (ehdr->e_shoff + (ehdr->e_shnum*ehdr->e_shentsize)) > image_len )
322 {
323 xc_set_error(XC_INVALID_KERNEL,
324 "ELF section headers extend beyond end of image.");
325 return -EINVAL;
326 }
328 /* Find the section-header strings table. */
329 if ( ehdr->e_shstrndx == SHN_UNDEF )
330 {
331 xc_set_error(XC_INVALID_KERNEL,
332 "ELF image has no section-header strings table (shstrtab).");
333 return -EINVAL;
334 }
335 shdr = (Elf_Shdr *)(image + ehdr->e_shoff +
336 (ehdr->e_shstrndx*ehdr->e_shentsize));
337 shstrtab = image + shdr->sh_offset;
339 dsi->__elfnote_section = NULL;
340 dsi->__xen_guest_string = NULL;
342 /* Look for .notes segment containing at least one Xen note */
343 for ( h = 0; h < ehdr->e_shnum; h++ )
344 {
345 shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
346 if ( !is_xen_elfnote_section(image, shdr) )
347 continue;
348 dsi->__elfnote_section = (void *)image + shdr->sh_offset;
349 dsi->__elfnote_section_end =
350 (void *)image + shdr->sh_offset + shdr->sh_size;
351 break;
352 }
354 /* Fall back to looking for the special '__xen_guest' section. */
355 if ( dsi->__elfnote_section == NULL )
356 {
357 for ( h = 0; h < ehdr->e_shnum; h++ )
358 {
359 shdr = (Elf_Shdr *)(image + ehdr->e_shoff + (h*ehdr->e_shentsize));
360 if ( is_xen_guest_section(shdr, shstrtab) )
361 {
362 dsi->__xen_guest_string = (char *)image + shdr->sh_offset;
363 break;
364 }
365 }
366 }
368 /* Check the contents of the Xen notes or guest string. */
369 if ( dsi->__elfnote_section || dsi->__xen_guest_string )
370 {
371 const char *loader = xen_elfnote_string(dsi, XEN_ELFNOTE_LOADER);
372 const char *guest_os = xen_elfnote_string(dsi, XEN_ELFNOTE_GUEST_OS);
373 const char *xen_version =
374 xen_elfnote_string(dsi, XEN_ELFNOTE_XEN_VERSION);
376 if ( ( loader == NULL || strncmp(loader, "generic", 7) ) &&
377 ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) )
378 {
379 xc_set_error(XC_INVALID_KERNEL,
380 "Will only load images built for the generic loader "
381 "or Linux images");
382 return -EINVAL;
383 }
385 if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) )
386 {
387 xc_set_error(XC_INVALID_KERNEL,
388 "Will only load images built for Xen v3.0");
389 return -EINVAL;
390 }
391 }
392 else
393 {
394 #if defined(__x86_64__) || defined(__i386__)
395 xc_set_error(XC_INVALID_KERNEL,
396 "Not a Xen-ELF image: "
397 "No ELF notes or '__xen_guest' section found.");
398 return -EINVAL;
399 #endif
400 }
402 /*
403 * If we have ELF notes then PAE=yes implies that we must support
404 * the extended cr3 syntax. Otherwise we need to find the
405 * [extended-cr3] syntax in the __xen_guest string.
406 */
407 dsi->pae_kernel = PAEKERN_no;
408 if ( dsi->__elfnote_section )
409 {
410 p = xen_elfnote_string(dsi, XEN_ELFNOTE_PAE_MODE);
411 if ( p != NULL && strncmp(p, "yes", 3) == 0 )
412 dsi->pae_kernel = PAEKERN_extended_cr3;
414 }
415 else
416 {
417 p = xen_guest_lookup(dsi, XEN_ELFNOTE_PAE_MODE);
418 if ( p != NULL && strncmp(p, "yes", 3) == 0 )
419 {
420 dsi->pae_kernel = PAEKERN_yes;
421 if ( !strncmp(p+3, "[extended-cr3]", 14) )
422 dsi->pae_kernel = PAEKERN_extended_cr3;
423 }
424 }
426 /* Initial guess for v_start is 0 if it is not explicitly defined. */
427 dsi->v_start =
428 xen_elfnote_numeric(dsi, XEN_ELFNOTE_VIRT_BASE, &virt_base_defined);
429 if ( !virt_base_defined )
430 dsi->v_start = 0;
432 /*
433 * If we are using the legacy __xen_guest section then elf_pa_off
434 * defaults to v_start in order to maintain compatibility with
435 * older hypervisors which set padd in the ELF header to
436 * virt_base.
437 *
438 * If we are using the modern ELF notes interface then the default
439 * is 0.
440 */
441 dsi->elf_paddr_offset =
442 xen_elfnote_numeric(dsi, XEN_ELFNOTE_PADDR_OFFSET, &elf_pa_off_defined);
443 if ( !elf_pa_off_defined )
444 {
445 if ( dsi->__elfnote_section )
446 dsi->elf_paddr_offset = 0;
447 else
448 dsi->elf_paddr_offset = dsi->v_start;
449 }
451 if ( elf_pa_off_defined && !virt_base_defined )
452 {
453 xc_set_error(XC_INVALID_KERNEL,
454 "Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF "
455 " notes or __xen_guest section.");
456 return -EINVAL;
457 }
459 for ( h = 0; h < ehdr->e_phnum; h++ )
460 {
461 phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
462 if ( !is_loadable_phdr(phdr) )
463 continue;
464 vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start;
465 if ( (vaddr + phdr->p_memsz) < vaddr )
466 {
467 xc_set_error(XC_INVALID_KERNEL,
468 "ELF program header %d is too large.", h);
469 return -EINVAL;
470 }
472 if ( vaddr < kernstart )
473 kernstart = vaddr;
474 if ( (vaddr + phdr->p_memsz) > kernend )
475 kernend = vaddr + phdr->p_memsz;
476 }
478 dsi->v_kernentry = ehdr->e_entry;
480 virt_entry =
481 xen_elfnote_numeric(dsi, XEN_ELFNOTE_ENTRY, &virt_entry_defined);
482 if ( virt_entry_defined )
483 dsi->v_kernentry = virt_entry;
485 if ( (kernstart > kernend) ||
486 (dsi->v_kernentry < kernstart) ||
487 (dsi->v_kernentry > kernend) ||
488 (dsi->v_start > kernstart) )
489 {
490 xc_set_error(XC_INVALID_KERNEL,
491 "ELF start or entries are out of bounds.");
492 return -EINVAL;
493 }
495 p = xen_elfnote_string(dsi, XEN_ELFNOTE_BSD_SYMTAB);
496 if ( p != NULL && strncmp(p, "yes", 3) == 0 )
497 dsi->load_symtab = 1;
499 dsi->v_kernstart = kernstart;
500 dsi->v_kernend = kernend;
501 dsi->v_end = dsi->v_kernend;
503 loadelfsymtab(image, 0, 0, NULL, dsi);
505 return 0;
506 }
508 static int
509 loadelfimage(
510 const char *image, unsigned long elfsize, int xch, uint32_t dom,
511 xen_pfn_t *parray, struct domain_setup_info *dsi)
512 {
513 Elf_Ehdr *ehdr = (Elf_Ehdr *)image;
514 Elf_Phdr *phdr;
515 int h;
517 char *va;
518 unsigned long pa, done, chunksz;
520 for ( h = 0; h < ehdr->e_phnum; h++ )
521 {
522 phdr = (Elf_Phdr *)(image + ehdr->e_phoff + (h*ehdr->e_phentsize));
523 if ( !is_loadable_phdr(phdr) )
524 continue;
526 for ( done = 0; done < phdr->p_filesz; done += chunksz )
527 {
528 pa = (phdr->p_paddr + done) - dsi->elf_paddr_offset;
529 va = xc_map_foreign_range(
530 xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
531 if ( va == NULL )
532 return -1;
533 chunksz = phdr->p_filesz - done;
534 if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
535 chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
536 memcpy(va + (pa & (PAGE_SIZE-1)),
537 image + phdr->p_offset + done, chunksz);
538 munmap(va, PAGE_SIZE);
539 }
541 for ( ; done < phdr->p_memsz; done += chunksz )
542 {
543 pa = (phdr->p_paddr + done) - dsi->elf_paddr_offset;
544 va = xc_map_foreign_range(
545 xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
546 if ( va == NULL )
547 return -1;
548 chunksz = phdr->p_memsz - done;
549 if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
550 chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
551 memset(va + (pa & (PAGE_SIZE-1)), 0, chunksz);
552 munmap(va, PAGE_SIZE);
553 }
554 }
556 loadelfsymtab(image, xch, dom, parray, dsi);
558 return 0;
559 }
561 #define ELFROUND (ELFSIZE / 8)
563 static int
564 loadelfsymtab(
565 const char *image, int xch, uint32_t dom, xen_pfn_t *parray,
566 struct domain_setup_info *dsi)
567 {
568 Elf_Ehdr *ehdr = (Elf_Ehdr *)image, *sym_ehdr;
569 Elf_Shdr *shdr;
570 unsigned long maxva, symva;
571 char *p;
572 int h, i;
574 if ( !dsi->load_symtab )
575 return 0;
577 p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
578 ehdr->e_shnum * sizeof(Elf_Shdr));
579 if (p == NULL)
580 return 0;
582 maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
583 symva = maxva;
584 maxva += sizeof(int);
585 dsi->symtab_addr = maxva;
586 dsi->symtab_len = 0;
587 maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
588 maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
590 shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
591 memcpy(shdr, image + ehdr->e_shoff, ehdr->e_shnum * sizeof(Elf_Shdr));
593 for ( h = 0; h < ehdr->e_shnum; h++ )
594 {
595 if ( shdr[h].sh_type == SHT_STRTAB )
596 {
597 /* Look for a strtab @i linked to symtab @h. */
598 for ( i = 0; i < ehdr->e_shnum; i++ )
599 if ( (shdr[i].sh_type == SHT_SYMTAB) &&
600 (shdr[i].sh_link == h) )
601 break;
602 /* Skip symtab @h if we found no corresponding strtab @i. */
603 if ( i == ehdr->e_shnum )
604 {
605 shdr[h].sh_offset = 0;
606 continue;
607 }
608 }
610 if ( (shdr[h].sh_type == SHT_STRTAB) ||
611 (shdr[h].sh_type == SHT_SYMTAB) )
612 {
613 if ( parray != NULL )
614 xc_map_memcpy(maxva, image + shdr[h].sh_offset,
615 shdr[h].sh_size,
616 xch, dom, parray, dsi->v_start);
618 /* Mangled to be based on ELF header location. */
619 shdr[h].sh_offset = maxva - dsi->symtab_addr;
621 dsi->symtab_len += shdr[h].sh_size;
622 maxva += shdr[h].sh_size;
623 maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
624 }
626 shdr[h].sh_name = 0; /* Name is NULL. */
627 }
629 if ( dsi->symtab_len == 0 )
630 {
631 dsi->symtab_addr = 0;
632 goto out;
633 }
635 if ( parray != NULL )
636 {
637 *(int *)p = maxva - dsi->symtab_addr;
638 sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
639 memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
640 sym_ehdr->e_phoff = 0;
641 sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
642 sym_ehdr->e_phentsize = 0;
643 sym_ehdr->e_phnum = 0;
644 sym_ehdr->e_shstrndx = SHN_UNDEF;
646 /* Copy total length, crafted ELF header and section header table */
647 xc_map_memcpy(symva, p, sizeof(int) + sizeof(Elf_Ehdr) +
648 ehdr->e_shnum * sizeof(Elf_Shdr), xch, dom, parray,
649 dsi->v_start);
650 }
652 dsi->symtab_len = maxva - dsi->symtab_addr;
653 dsi->v_end = round_pgup(maxva);
655 out:
656 free(p);
658 return 0;
659 }
661 /*
662 * Local variables:
663 * mode: C
664 * c-set-style: "BSD"
665 * c-basic-offset: 4
666 * tab-width: 4
667 * indent-tabs-mode: nil
668 * End:
669 */