1/* BFD back-end for PPCbug boot records.
2   Copyright (C) 1996-2017 Free Software Foundation, Inc.
3   Written by Michael Meissner, Cygnus Support, <meissner@cygnus.com>
4
5   This file is part of BFD, the Binary File Descriptor library.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20   MA 02110-1301, USA.  */
21
22
23/* This is a BFD backend which may be used to write PowerPCBug boot objects.
24   It may only be used for output, not input.  The intention is that this may
25   be used as an output format for objcopy in order to generate raw binary
26   data.
27
28   This is very simple.  The only complication is that the real data
29   will start at some address X, and in some cases we will not want to
30   include X zeroes just to get to that point.  Since the start
31   address is not meaningful for this object file format, we use it
32   instead to indicate the number of zeroes to skip at the start of
33   the file.  objcopy cooperates by specially setting the start
34   address to zero by default.  */
35
36#include "sysdep.h"
37#include "safe-ctype.h"
38#include "bfd.h"
39#include "libbfd.h"
40
41/* PPCbug location structure */
42typedef struct ppcboot_location
43{
44  bfd_byte	ind;
45  bfd_byte	head;
46  bfd_byte	sector;
47  bfd_byte	cylinder;
48} ppcboot_location_t;
49
50/* PPCbug partition table layout */
51typedef struct ppcboot_partition
52{
53  ppcboot_location_t	partition_begin;	/* partition begin */
54  ppcboot_location_t	partition_end;		/* partition end */
55  bfd_byte		sector_begin[4];	/* 32-bit start RBA (zero-based), little endian */
56  bfd_byte		sector_length[4];	/* 32-bit RBA count (one-based), little endian */
57} ppcboot_partition_t;
58
59/* PPCbug boot layout.  */
60typedef struct ppcboot_hdr
61{
62  bfd_byte		pc_compatibility[446];	/* x86 instruction field */
63  ppcboot_partition_t	partition[4];		/* partition information */
64  bfd_byte		signature[2];		/* 0x55 and 0xaa */
65  bfd_byte		entry_offset[4];	/* entry point offset, little endian */
66  bfd_byte		length[4];		/* load image length, little endian */
67  bfd_byte		flags;			/* flag field */
68  bfd_byte		os_id;			/* OS_ID */
69  char			partition_name[32];	/* partition name */
70  bfd_byte		reserved1[470];		/* reserved */
71}
72#ifdef __GNUC__
73  __attribute__ ((packed))
74#endif
75ppcboot_hdr_t;
76
77/* Signature bytes for last 2 bytes of the 512 byte record */
78#define SIGNATURE0 0x55
79#define SIGNATURE1 0xaa
80
81/* PowerPC boot type */
82#define PPC_IND 0x41
83
84/* Information needed for ppcboot header */
85typedef struct ppcboot_data
86{
87  ppcboot_hdr_t	header;				/* raw header */
88  asection *sec;				/* single section */
89} ppcboot_data_t;
90
91/* Any bfd we create by reading a ppcboot file has three symbols:
92   a start symbol, an end symbol, and an absolute length symbol.  */
93#define PPCBOOT_SYMS 3
94
95#define ppcboot_set_tdata(abfd, ptr) ((abfd)->tdata.any = (ptr))
96#define ppcboot_get_tdata(abfd) ((ppcboot_data_t *) ((abfd)->tdata.any))
97
98/* Create a ppcboot object.  Invoked via bfd_set_format.  */
99
100static bfd_boolean
101ppcboot_mkobject (bfd *abfd)
102{
103  if (!ppcboot_get_tdata (abfd))
104    {
105      bfd_size_type amt = sizeof (ppcboot_data_t);
106      ppcboot_set_tdata (abfd, bfd_zalloc (abfd, amt));
107    }
108
109  return TRUE;
110}
111
112
113/* Set the architecture to PowerPC */
114static bfd_boolean
115ppcboot_set_arch_mach (bfd *abfd,
116		       enum bfd_architecture arch,
117		       unsigned long machine)
118{
119  if (arch == bfd_arch_unknown)
120    arch = bfd_arch_powerpc;
121
122  else if (arch != bfd_arch_powerpc)
123    return FALSE;
124
125  return bfd_default_set_arch_mach (abfd, arch, machine);
126}
127
128
129/* Any file may be considered to be a ppcboot file, provided the target
130   was not defaulted.  That is, it must be explicitly specified as
131   being ppcboot.  */
132
133static const bfd_target *
134ppcboot_object_p (bfd *abfd)
135{
136  struct stat statbuf;
137  asection *sec;
138  ppcboot_hdr_t hdr;
139  size_t i;
140  ppcboot_data_t *tdata;
141  flagword flags;
142
143  BFD_ASSERT (sizeof (ppcboot_hdr_t) == 1024);
144
145  if (abfd->target_defaulted)
146    {
147      bfd_set_error (bfd_error_wrong_format);
148      return NULL;
149    }
150
151  /* Find the file size.  */
152  if (bfd_stat (abfd, &statbuf) < 0)
153    {
154      bfd_set_error (bfd_error_system_call);
155      return NULL;
156    }
157
158  if ((size_t) statbuf.st_size < sizeof (ppcboot_hdr_t))
159    {
160      bfd_set_error (bfd_error_wrong_format);
161      return NULL;
162    }
163
164  if (bfd_bread (&hdr, (bfd_size_type) sizeof (hdr), abfd)
165      != sizeof (hdr))
166    {
167      if (bfd_get_error () != bfd_error_system_call)
168	bfd_set_error (bfd_error_wrong_format);
169
170      return NULL;
171    }
172
173  /* Now do some basic checks.  */
174  for (i = 0; i < sizeof (hdr.pc_compatibility); i++)
175    if (hdr.pc_compatibility[i])
176      {
177	bfd_set_error (bfd_error_wrong_format);
178	return NULL;
179      }
180
181  if (hdr.signature[0] != SIGNATURE0 || hdr.signature[1] != SIGNATURE1)
182    {
183      bfd_set_error (bfd_error_wrong_format);
184      return NULL;
185    }
186
187  if (hdr.partition[0].partition_end.ind != PPC_IND)
188    {
189      bfd_set_error (bfd_error_wrong_format);
190      return NULL;
191    }
192
193  abfd->symcount = PPCBOOT_SYMS;
194
195  /* One data section.  */
196  flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_CODE | SEC_HAS_CONTENTS;
197  sec = bfd_make_section_with_flags (abfd, ".data", flags);
198  if (sec == NULL)
199    return NULL;
200  sec->vma = 0;
201  sec->size = statbuf.st_size - sizeof (ppcboot_hdr_t);
202  sec->filepos = sizeof (ppcboot_hdr_t);
203
204  ppcboot_mkobject (abfd);
205  tdata = ppcboot_get_tdata (abfd);
206  tdata->sec = sec;
207  memcpy (&tdata->header, &hdr, sizeof (ppcboot_hdr_t));
208
209  ppcboot_set_arch_mach (abfd, bfd_arch_powerpc, 0L);
210  return abfd->xvec;
211}
212
213#define ppcboot_close_and_cleanup _bfd_generic_close_and_cleanup
214#define ppcboot_bfd_free_cached_info _bfd_generic_bfd_free_cached_info
215#define ppcboot_new_section_hook _bfd_generic_new_section_hook
216
217
218/* Get contents of the only section.  */
219
220static bfd_boolean
221ppcboot_get_section_contents (bfd *abfd,
222			      asection *section ATTRIBUTE_UNUSED,
223			      void * location,
224			      file_ptr offset,
225			      bfd_size_type count)
226{
227  if (bfd_seek (abfd, offset + (file_ptr) sizeof (ppcboot_hdr_t), SEEK_SET) != 0
228      || bfd_bread (location, count, abfd) != count)
229    return FALSE;
230  return TRUE;
231}
232
233
234/* Return the amount of memory needed to read the symbol table.  */
235
236static long
237ppcboot_get_symtab_upper_bound (bfd *abfd ATTRIBUTE_UNUSED)
238{
239  return (PPCBOOT_SYMS + 1) * sizeof (asymbol *);
240}
241
242
243/* Create a symbol name based on the bfd's filename.  */
244
245static char *
246mangle_name (bfd *abfd, char *suffix)
247{
248  bfd_size_type size;
249  char *buf;
250  char *p;
251
252  size = (strlen (bfd_get_filename (abfd))
253	  + strlen (suffix)
254	  + sizeof "_ppcboot__");
255
256  buf = (char *) bfd_alloc (abfd, size);
257  if (buf == NULL)
258    return "";
259
260  sprintf (buf, "_ppcboot_%s_%s", bfd_get_filename (abfd), suffix);
261
262  /* Change any non-alphanumeric characters to underscores.  */
263  for (p = buf; *p; p++)
264    if (! ISALNUM (*p))
265      *p = '_';
266
267  return buf;
268}
269
270
271/* Return the symbol table.  */
272
273static long
274ppcboot_canonicalize_symtab (bfd *abfd, asymbol **alocation)
275{
276  asection *sec = ppcboot_get_tdata (abfd)->sec;
277  asymbol *syms;
278  unsigned int i;
279  bfd_size_type amt = PPCBOOT_SYMS * sizeof (asymbol);
280
281  syms = (asymbol *) bfd_alloc (abfd, amt);
282  if (syms == NULL)
283    return FALSE;
284
285  /* Start symbol.  */
286  syms[0].the_bfd = abfd;
287  syms[0].name = mangle_name (abfd, "start");
288  syms[0].value = 0;
289  syms[0].flags = BSF_GLOBAL;
290  syms[0].section = sec;
291  syms[0].udata.p = NULL;
292
293  /* End symbol.  */
294  syms[1].the_bfd = abfd;
295  syms[1].name = mangle_name (abfd, "end");
296  syms[1].value = sec->size;
297  syms[1].flags = BSF_GLOBAL;
298  syms[1].section = sec;
299  syms[1].udata.p = NULL;
300
301  /* Size symbol.  */
302  syms[2].the_bfd = abfd;
303  syms[2].name = mangle_name (abfd, "size");
304  syms[2].value = sec->size;
305  syms[2].flags = BSF_GLOBAL;
306  syms[2].section = bfd_abs_section_ptr;
307  syms[2].udata.p = NULL;
308
309  for (i = 0; i < PPCBOOT_SYMS; i++)
310    *alocation++ = syms++;
311  *alocation = NULL;
312
313  return PPCBOOT_SYMS;
314}
315
316#define ppcboot_make_empty_symbol _bfd_generic_make_empty_symbol
317#define ppcboot_print_symbol _bfd_nosymbols_print_symbol
318
319/* Get information about a symbol.  */
320
321static void
322ppcboot_get_symbol_info (bfd *ignore_abfd ATTRIBUTE_UNUSED,
323			 asymbol *symbol,
324			 symbol_info *ret)
325{
326  bfd_symbol_info (symbol, ret);
327}
328
329#define ppcboot_get_symbol_version_string \
330  _bfd_nosymbols_get_symbol_version_string
331#define ppcboot_bfd_is_target_special_symbol \
332  ((bfd_boolean (*) (bfd *, asymbol *)) bfd_false)
333#define ppcboot_bfd_is_local_label_name bfd_generic_is_local_label_name
334#define ppcboot_get_lineno _bfd_nosymbols_get_lineno
335#define ppcboot_find_nearest_line _bfd_nosymbols_find_nearest_line
336#define ppcboot_find_line _bfd_nosymbols_find_line
337#define ppcboot_find_inliner_info _bfd_nosymbols_find_inliner_info
338#define ppcboot_bfd_make_debug_symbol _bfd_nosymbols_bfd_make_debug_symbol
339#define ppcboot_read_minisymbols _bfd_generic_read_minisymbols
340#define ppcboot_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
341
342/* Write section contents of a ppcboot file.  */
343
344static bfd_boolean
345ppcboot_set_section_contents (bfd *abfd,
346			      asection *sec,
347			      const void * data,
348			      file_ptr offset,
349			      bfd_size_type size)
350{
351  if (! abfd->output_has_begun)
352    {
353      bfd_vma low;
354      asection *s;
355
356      /* The lowest section VMA sets the virtual address of the start
357         of the file.  We use the set the file position of all the
358         sections.  */
359      low = abfd->sections->vma;
360      for (s = abfd->sections->next; s != NULL; s = s->next)
361	if (s->vma < low)
362	  low = s->vma;
363
364      for (s = abfd->sections; s != NULL; s = s->next)
365	s->filepos = s->vma - low;
366
367      abfd->output_has_begun = TRUE;
368    }
369
370  return _bfd_generic_set_section_contents (abfd, sec, data, offset, size);
371}
372
373
374static int
375ppcboot_sizeof_headers (bfd *abfd ATTRIBUTE_UNUSED,
376			struct bfd_link_info *info ATTRIBUTE_UNUSED)
377{
378  return sizeof (ppcboot_hdr_t);
379}
380
381
382/* Print out the program headers.  */
383
384static bfd_boolean
385ppcboot_bfd_print_private_bfd_data (bfd *abfd, void * farg)
386{
387  FILE *f = (FILE *)farg;
388  ppcboot_data_t *tdata = ppcboot_get_tdata (abfd);
389  long entry_offset = bfd_getl_signed_32 (tdata->header.entry_offset);
390  long length = bfd_getl_signed_32 (tdata->header.length);
391  int i;
392
393  fprintf (f, _("\nppcboot header:\n"));
394  fprintf (f, _("Entry offset        = 0x%.8lx (%ld)\n"),
395	   (unsigned long) entry_offset, entry_offset);
396  fprintf (f, _("Length              = 0x%.8lx (%ld)\n"),
397	   (unsigned long) length, length);
398
399  if (tdata->header.flags)
400    fprintf (f, _("Flag field          = 0x%.2x\n"), tdata->header.flags);
401
402  if (tdata->header.os_id)
403    fprintf (f, "OS_ID               = 0x%.2x\n", tdata->header.os_id);
404
405  if (tdata->header.partition_name[0])
406    fprintf (f, _("Partition name      = \"%s\"\n"), tdata->header.partition_name);
407
408  for (i = 0; i < 4; i++)
409    {
410      long sector_begin  = bfd_getl_signed_32 (tdata->header.partition[i].sector_begin);
411      long sector_length = bfd_getl_signed_32 (tdata->header.partition[i].sector_length);
412
413      /* Skip all 0 entries */
414      if (!tdata->header.partition[i].partition_begin.ind
415	  && !tdata->header.partition[i].partition_begin.head
416	  && !tdata->header.partition[i].partition_begin.sector
417	  && !tdata->header.partition[i].partition_begin.cylinder
418	  && !tdata->header.partition[i].partition_end.ind
419	  && !tdata->header.partition[i].partition_end.head
420	  && !tdata->header.partition[i].partition_end.sector
421	  && !tdata->header.partition[i].partition_end.cylinder
422	  && !sector_begin && !sector_length)
423	continue;
424
425      /* xgettext:c-format */
426      fprintf (f, _("\nPartition[%d] start  = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i,
427	       tdata->header.partition[i].partition_begin.ind,
428	       tdata->header.partition[i].partition_begin.head,
429	       tdata->header.partition[i].partition_begin.sector,
430	       tdata->header.partition[i].partition_begin.cylinder);
431
432      /* xgettext:c-format */
433      fprintf (f, _("Partition[%d] end    = { 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x }\n"), i,
434	       tdata->header.partition[i].partition_end.ind,
435	       tdata->header.partition[i].partition_end.head,
436	       tdata->header.partition[i].partition_end.sector,
437	       tdata->header.partition[i].partition_end.cylinder);
438
439      /* xgettext:c-format */
440      fprintf (f, _("Partition[%d] sector = 0x%.8lx (%ld)\n"),
441	       i, (unsigned long) sector_begin, sector_begin);
442
443      /* xgettext:c-format */
444      fprintf (f, _("Partition[%d] length = 0x%.8lx (%ld)\n"),
445	       i, (unsigned long) sector_length, sector_length);
446    }
447
448  fprintf (f, "\n");
449  return TRUE;
450}
451
452
453#define ppcboot_bfd_get_relocated_section_contents \
454  bfd_generic_get_relocated_section_contents
455#define ppcboot_bfd_relax_section bfd_generic_relax_section
456#define ppcboot_bfd_gc_sections bfd_generic_gc_sections
457#define ppcboot_bfd_lookup_section_flags bfd_generic_lookup_section_flags
458#define ppcboot_bfd_merge_sections bfd_generic_merge_sections
459#define ppcboot_bfd_is_group_section bfd_generic_is_group_section
460#define ppcboot_bfd_discard_group bfd_generic_discard_group
461#define ppcboot_section_already_linked \
462  _bfd_generic_section_already_linked
463#define ppcboot_bfd_define_common_symbol bfd_generic_define_common_symbol
464#define ppcboot_bfd_link_hash_table_create _bfd_generic_link_hash_table_create
465#define ppcboot_bfd_link_add_symbols _bfd_generic_link_add_symbols
466#define ppcboot_bfd_link_just_syms _bfd_generic_link_just_syms
467#define ppcboot_bfd_copy_link_hash_symbol_type \
468  _bfd_generic_copy_link_hash_symbol_type
469#define ppcboot_bfd_final_link _bfd_generic_final_link
470#define ppcboot_bfd_link_split_section _bfd_generic_link_split_section
471#define ppcboot_get_section_contents_in_window \
472  _bfd_generic_get_section_contents_in_window
473#define ppcboot_bfd_link_check_relocs _bfd_generic_link_check_relocs
474
475#define ppcboot_bfd_copy_private_bfd_data _bfd_generic_bfd_copy_private_bfd_data
476#define ppcboot_bfd_merge_private_bfd_data _bfd_generic_bfd_merge_private_bfd_data
477#define ppcboot_bfd_copy_private_section_data _bfd_generic_bfd_copy_private_section_data
478#define ppcboot_bfd_copy_private_symbol_data _bfd_generic_bfd_copy_private_symbol_data
479#define ppcboot_bfd_copy_private_header_data _bfd_generic_bfd_copy_private_header_data
480#define ppcboot_bfd_set_private_flags _bfd_generic_bfd_set_private_flags
481#define ppcboot_bfd_print_private_bfd_dat ppcboot_bfd_print_private_bfd_data
482
483const bfd_target powerpc_boot_vec =
484{
485  "ppcboot",			/* name */
486  bfd_target_unknown_flavour,	/* flavour */
487  BFD_ENDIAN_BIG,		/* byteorder is big endian for code */
488  BFD_ENDIAN_LITTLE,		/* header_byteorder */
489  EXEC_P,			/* object_flags */
490  (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_DATA
491   | SEC_ROM | SEC_HAS_CONTENTS), /* section_flags */
492  0,				/* symbol_leading_char */
493  ' ',				/* ar_pad_char */
494  16,				/* ar_max_namelen */
495  0,				/* match priority.  */
496  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
497  bfd_getb32, bfd_getb_signed_32, bfd_putb32,
498  bfd_getb16, bfd_getb_signed_16, bfd_putb16,	/* data */
499  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
500  bfd_getl32, bfd_getl_signed_32, bfd_putl32,
501  bfd_getl16, bfd_getl_signed_16, bfd_putl16,	/* hdrs */
502  {				/* bfd_check_format */
503    _bfd_dummy_target,
504    ppcboot_object_p,		/* bfd_check_format */
505    _bfd_dummy_target,
506    _bfd_dummy_target,
507  },
508  {				/* bfd_set_format */
509    bfd_false,
510    ppcboot_mkobject,
511    bfd_false,
512    bfd_false,
513  },
514  {				/* bfd_write_contents */
515    bfd_false,
516    bfd_true,
517    bfd_false,
518    bfd_false,
519  },
520
521  BFD_JUMP_TABLE_GENERIC (ppcboot),
522  BFD_JUMP_TABLE_COPY (ppcboot),
523  BFD_JUMP_TABLE_CORE (_bfd_nocore),
524  BFD_JUMP_TABLE_ARCHIVE (_bfd_noarchive),
525  BFD_JUMP_TABLE_SYMBOLS (ppcboot),
526  BFD_JUMP_TABLE_RELOCS (_bfd_norelocs),
527  BFD_JUMP_TABLE_WRITE (ppcboot),
528  BFD_JUMP_TABLE_LINK (ppcboot),
529  BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
530
531  NULL,
532
533  NULL
534};
535