/** @file
  This program generates a hex array to be manually coppied into
  OvmfXen.fdf.

  The purpose is for the flash device image to be recognize as an ELF.

  Copyright (c) 2019, Citrix Systems, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "elf.h"
#include "fcntl.h"
#include "stdbool.h"
#include "stddef.h"
#include "stdio.h"
#include "stdlib.h"

void
print_hdr (
  FILE    *file,
  void    *s,
  size_t  size,
  bool    end_delimiter
  )
{
  char  *c = s;

  fprintf (file, "  ");
  while (size-- > 1) {
    fprintf (file, "0x%02hhx, ", *(c++));
  }

  if (end_delimiter) {
    fprintf (file, "0x%02hhx,", *c);
  } else {
    fprintf (file, "0x%02hhx", *c);
  }
}

/* Format for the XEN_ELFNOTE_PHYS32_ENTRY program segment */
#define XEN_ELFNOTE_PHYS32_ENTRY  18
typedef struct {
  uint32_t    name_size;
  uint32_t    desc_size;
  uint32_t    type;
  char        name[4];
  uint32_t    desc;
} xen_elfnote_phys32_entry;

#define LICENSE_HDR  "\
## @file\r\n\
#  FDF include file that defines a PVH ELF header.\r\n\
#\r\n\
#  Copyright (c) 2022, Intel Corporation. All rights reserved.\r\n\
#\r\n\
#  SPDX-License-Identifier: BSD-2-Clause-Patent\r\n\
#\r\n\
##\r\n\
\r\n\
"

int
main (
  int   argc,
  char  *argv[]
  )
{
  /* FW_SIZE */
  size_t  ovmf_blob_size = 0x00200000;
  /* Load OVMF at 1MB when running as PVH guest */
  uint32_t  ovmf_base_address = 0x00100000;
  uint32_t  ovmfxen_pvh_entry_point;
  size_t    offset_into_file = 0;
  char      *endptr, *str;
  long      param;
  FILE      *file = stdout;

  /* Parse the size parameter */
  if (argc > 1) {
    str   = argv[1];
    param = strtol (str, &endptr, 10);
    if (endptr != str) {
      ovmf_blob_size = (size_t)param;
    }
  }

  /* Parse the filepath parameter */
  if (argc > 2) {
    file = fopen (argv[2], "w");
    fprintf (file, LICENSE_HDR);
  }

  /* Xen PVH entry point */
  ovmfxen_pvh_entry_point = ovmf_base_address + ovmf_blob_size - 0x30;

  /* ELF file header */
 #ifdef PVH64
  Elf64_Ehdr  hdr = {
 #else
  Elf32_Ehdr  hdr = {
 #endif
    .e_ident   = ELFMAG,
    .e_type    = ET_EXEC,
    .e_machine = EM_386,
    .e_version = EV_CURRENT,
    .e_entry   = ovmfxen_pvh_entry_point,
    .e_flags   = R_386_NONE,
    .e_ehsize  = sizeof (hdr),
 #ifdef PVH64
    .e_phentsize = sizeof (Elf64_Phdr),
 #else
    .e_phentsize = sizeof (Elf32_Phdr),
 #endif
  };

  offset_into_file += sizeof (hdr);

 #ifdef PVH64
  hdr.e_ident[EI_CLASS] = ELFCLASS64;
 #else
  hdr.e_ident[EI_CLASS] = ELFCLASS32;
 #endif
  hdr.e_ident[EI_DATA]    = ELFDATA2LSB;
  hdr.e_ident[EI_VERSION] = EV_CURRENT;
  hdr.e_ident[EI_OSABI]   = ELFOSABI_LINUX;
  /* Placing program headers just after hdr */
  hdr.e_phoff = sizeof (hdr);

  /* program header */
 #ifdef PVH64
  Elf64_Phdr  phdr_load = {
 #else
  Elf32_Phdr  phdr_load = {
 #endif
    .p_type   = PT_LOAD,
    .p_offset = 0, /* load everything */
    .p_paddr  = ovmf_base_address,
    .p_filesz = ovmf_blob_size,
    .p_memsz  = ovmf_blob_size,
    .p_flags  = PF_X | PF_W | PF_R,
 #ifdef PVH64
    .p_align  = 4,
 #else
    .p_align  = 0,
 #endif
  };

  phdr_load.p_vaddr = phdr_load.p_paddr;
  hdr.e_phnum      += 1;
  offset_into_file += sizeof (phdr_load);

  /* Xen ELF Note. */

  xen_elfnote_phys32_entry  xen_elf_note = {
    .type      = XEN_ELFNOTE_PHYS32_ENTRY,
    .name      = "Xen",
    .desc      = ovmfxen_pvh_entry_point,
    .name_size =
      offsetof (xen_elfnote_phys32_entry, desc) -
      offsetof (xen_elfnote_phys32_entry, name),
    .desc_size =
      sizeof (xen_elfnote_phys32_entry) -
      offsetof (xen_elfnote_phys32_entry, desc),
  };
 #ifdef PVH64
  Elf64_Phdr  phdr_note = {
 #else
  Elf32_Phdr  phdr_note = {
 #endif
    .p_type   = PT_NOTE,
    .p_filesz = sizeof (xen_elf_note),
    .p_memsz  = sizeof (xen_elf_note),
    .p_flags  = PF_R,
 #ifdef PVH64
    .p_align  = 4,
 #else
    .p_align  = 0,
 #endif
  };

  hdr.e_phnum       += 1;
  offset_into_file  += sizeof (phdr_note);
  phdr_note.p_offset = offset_into_file;
  phdr_note.p_paddr  = ovmf_base_address + phdr_note.p_offset;
  phdr_note.p_vaddr  = phdr_note.p_paddr;

  /*
   * print elf header
   */

  size_t  i;
  size_t  hdr_size  = sizeof (hdr);
  size_t  entry_off = offsetof (typeof(hdr), e_entry);

  fprintf (file, "DATA = {\r\n");

  fprintf (file, "  # ELF file header\r\n");
  print_hdr (file, &hdr, entry_off, true);
  fprintf (file, "\r\n");
  print_hdr (file, &hdr.e_entry, sizeof (hdr.e_entry), true);
  fprintf (file, " # hdr.e_entry\r\n");
  print_hdr (file, &hdr.e_entry + 1, hdr_size - entry_off - sizeof (hdr.e_entry), true);

  fprintf (file, "\r\n\r\n  # ELF Program segment headers\r\n");
  fprintf (file, "  # - Load segment\r\n");
  for (i = 0; i < sizeof (phdr_load); i += 4) {
    print_hdr (file, ((char *)&phdr_load) + i, 4, true);
    fprintf (file, "\r\n");
  }

  fprintf (file, "  # - ELFNOTE segment\r\n");
  for (i = 0; i < sizeof (phdr_note); i += 4) {
    print_hdr (file, ((char *)&phdr_note) + i, 4, true);
    fprintf (file, "\r\n");
  }

  fprintf (file, "\r\n  # XEN_ELFNOTE_PHYS32_ENTRY\r\n");
  for (i = 0; i < sizeof (xen_elf_note); i += 4) {
    print_hdr (file, ((char *)&xen_elf_note) + i, 4, (sizeof (xen_elf_note) - i) > 4);
    fprintf (file, "\r\n");
  }

  fprintf (file, "}\r\n");

  return 0;
}