/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
    included in this distribution in the file called "COPYING". If not,
    see <http://www.gnu.org/licenses/>.

   Contact Information
   OpenAirInterface Admin: openair_admin@eurecom.fr
   OpenAirInterface Tech : openair_tech@eurecom.fr
   OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr

   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/

/*
    C Header file <updatefw.h> for updatefw tool.

    K. Khalfallah, Aug, 2007
    kkhalfallah@free.fr
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include "openair_device.h"

#include "elf.h"
#include "elftypes.h"
#define EM_SPARC    2
#define MAX_SIZE_STRING_TABLE    1024
#define MAX_SIZE_SECTION_NAME    32

#include "updatefw.h"

/* Interface with OpenAirInterface driver */
#define DEVICE_NAME "/dev/openair0"
#define ACCESS_MODE O_RDONLY

#define SWITCH_IS_OFF        0
#define SWITCH_IS_ON         1
static unsigned int helpflag = SWITCH_IS_OFF;
static unsigned int pflag = SWITCH_IS_OFF;
static unsigned int noexecflag = SWITCH_IS_OFF;
static unsigned int fflag = SWITCH_IS_OFF;
static unsigned int sflag = SWITCH_IS_OFF;
static unsigned int rebootflag = SWITCH_IS_OFF;
static FILE* p_elfimage;
static Elf32_Ehdr elf_Ehdr;
static Elf32_Shdr elf_Shdr;
long Shdr_pos, StringSec_pos, StringSec_size;
//char section_name[MAX_SIZE_SECTION_NAME];
char SecNameStnTable[MAX_SIZE_STRING_TABLE];
unsigned int ioctl_params[4];

#define LONGOPTIONS_NB 6
struct option updatefw_longopts[LONGOPTIONS_NB+1] = {
  {  .name = "help",        .has_arg = no_argument,       .flag = &helpflag,      .val = SWITCH_IS_ON  },
  {  .name = "firmware",    .has_arg = required_argument, .flag = NULL,           .val = 'f'           },
  {  .name = "pretend",     .has_arg = no_argument,       .flag = &pflag,         .val = SWITCH_IS_ON  },
  {  .name = "noexec",      .has_arg = no_argument,       .flag = &noexecflag,    .val = SWITCH_IS_ON  },
  {  .name = "stackptr",    .has_arg = required_argument, .flag = NULL,           .val = 's'           },
  {  .name = "forcereboot", .has_arg = no_argument,       .flag = &rebootflag,    .val = SWITCH_IS_ON  },
  {0, 0, 0, 0}
};
#define TRUE  1
#define FALSE 0

void show_usage(char* pgname) {
  unsigned int i;
  fprintf(stderr, "  %s : Tool to update firmware of Cardbus-MIMO-1/Leon3 card through the PCI interface,\n", pgname);
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, " that is, even without using the Xilinx parallel cable. The tool performs sequentially\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, " the 5 following operations:\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "       1) Ask card driver to reboot (optionally)\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "       2) Ask card driver to transfer .text section\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "       3) Ask card driver to transfer .data section\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "       4) Ask card driver to clear .bss section\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "       5) Ask card driver to have Leon processor set stack-pointer\n");
  fprintf(stderr, "    ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "          and jump to firmware entry.\n");
  fprintf(stderr, "Usage:\n");
  fprintf(stderr, "  %s [-f|--firmware 'filename'] [-s|--stackptr value] [-b|--forcereboot]\n",pgname);
  fprintf(stderr, "   ");

  for (i=0; i<strlen(pgname); i++) fprintf(stderr, " ");

  fprintf(stderr, "[-v|-vv|-vvv|-vvvv] [-h|--help] [-p|--pretend]\n\n");
  fprintf(stderr, "  [-f|--firmware ]     Mandatory option, to set the name of the executable file\n");
  fprintf(stderr, "                       of the firmware to update the remote card with.\n");
  fprintf(stderr, "                       The file should be an executable file in the Sparc32 ELF binary\n");
  fprintf(stderr, "                       format, containing at least the 3 mandatory sections .text, .data & .bss\n");
  fprintf(stderr, "  [-s|--stackptr]      Mandatory option, to set the value of the stack pointer before\n");
  fprintf(stderr, "                       giving hand to the firmware. The specified value must be an integer\n");
  fprintf(stderr, "                       number of 8 hexadecimal digits preceded by token '0x'.\n");
  fprintf(stderr, "  [-b|--forcereboot]   Force reboot of Leon processor before updating the firmware.\n");
  fprintf(stderr, "  [-p|--pretend]       Just pretend to transfer commands (ioctls) to driver, don't do it\n");
  fprintf(stderr, "                       actually. Useful together with verbose -vv switch.\n");
  fprintf(stderr, "  [-n|--noexec]        Transfer all data and text segments, but don't do actually start the code on Leon (no START_EXEC).\n");
  fprintf(stderr, "  [-v|-vv|-vvv|-vvvv]  Verbose modes.\n");
  fprintf(stderr, "  [-h|--help]          Displays this help.\n");
}

int get_elf_header(Elf32_Ehdr* p_Elf32_hdr, FILE* p_file) {
  int nbread;
  nbread = fread(p_Elf32_hdr, sizeof(elf_Ehdr), 1, p_file);

  if (nbread != 1) {
    fprintf(stderr, "Error : while reading ELF header (fread() returned %d)\n", nbread);
    exit(-1);
  }

  invert2(p_Elf32_hdr->e_type);            /* $$$$KMK: bad code !!! Endianess dependant ! */
  invert2(p_Elf32_hdr->e_machine);
  invert4(p_Elf32_hdr->e_version);
  invert4(p_Elf32_hdr->e_entry);
  invert4(p_Elf32_hdr->e_phoff);
  invert4(p_Elf32_hdr->e_shoff);
  invert4(p_Elf32_hdr->e_flags);
  invert2(p_Elf32_hdr->e_ehsize);
  invert2(p_Elf32_hdr->e_phentsize);
  invert2(p_Elf32_hdr->e_phnum);
  invert2(p_Elf32_hdr->e_shentsize);
  invert2(p_Elf32_hdr->e_shnum);
  invert2(p_Elf32_hdr->e_shstrndx);
  return (p_Elf32_hdr->e_type == ET_EXEC)
         && (p_Elf32_hdr->e_machine == EM_SPARC)
         && (p_Elf32_hdr->e_version == EV_CURRENT)
         && (p_Elf32_hdr->e_ident[EI_CLASS] == ELFCLASS32)
         && (p_Elf32_hdr->e_ident[EI_DATA] == ELFDATA2MSB)
         && (p_Elf32_hdr->e_ident[EI_VERSION] == EV_CURRENT);
}

int get_elf_section_header(Elf32_Shdr* p_Elf32_Shdr, FILE* p_file, unsigned int section_ndx) {
  int nbread;
  /* retrieve the position of the section header table */
  fseek(p_file, Shdr_pos + (section_ndx * elf_Ehdr.e_shentsize), 0);
  /* set file pointer to the position of the section header section_ndx */
  // seekrst = fseek(p_file, (long), 0);
  nbread = fread(p_Elf32_Shdr, sizeof(elf_Shdr), 1, p_file);

  if (nbread != 1) {
    fprintf(stderr, "Error : while reading elf section header (fread() returned %d)\n", nbread);
    exit(-1);
  }

  invert4(p_Elf32_Shdr->sh_name);            /* $$$$KMK: bad code !!! Endianess dependant ! */
  invert4(p_Elf32_Shdr->sh_type);
  invert4(p_Elf32_Shdr->sh_flags);
  invert4(p_Elf32_Shdr->sh_addr);
  invert4(p_Elf32_Shdr->sh_offset);
  invert4(p_Elf32_Shdr->sh_size);
  invert4(p_Elf32_Shdr->sh_link);
  invert4(p_Elf32_Shdr->sh_info);
  invert4(p_Elf32_Shdr->sh_addralign);
  invert4(p_Elf32_Shdr->sh_entsize);
  //fseek(p_file, StringSec_pos + p_Elf32_Shdr->sh_name, 0);
  //fread();
  //strcpy(sname, );
  return nbread;
}

void find_and_transfer_section(char* section_name, unsigned int verboselevel) {
  /* Interface with driver */
  int ioctlretval;
  int ifile;
  char* section_content;
  int nbread;
  unsigned int secnb = 0;

  for (secnb = 0 ; secnb < elf_Ehdr.e_shnum; secnb++) {
    get_elf_section_header(&elf_Shdr, p_elfimage, secnb);

    if (!strcmp(SecNameStnTable + elf_Shdr.sh_name, section_name)) {
      if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
        printf("Info: ok, found section %s (as section nb. %d)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);

      /* Check that section size is a multiple of 4 bytes. */
      if (elf_Shdr.sh_size % 4) {
        fprintf(stderr, "Error: section %s has a non-multiple-of-4-bytes size (%d).\n",
                SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
        fclose(p_elfimage);
        exit(-1);
      } else if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS) {
        printf("Info: ok, section %s has size %d bytes (multiple of 4 bytes).\n",
               SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
      }

      /* Dynamically allocate a chunk of memory to store the section into. */
      section_content = (char*)malloc(elf_Shdr.sh_size);

      if (!section_content) {
        fprintf(stderr, "Error: could not dynamically allocate %d bytes for section %s.\n",
                elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
        fclose(p_elfimage);
        exit(-1);
      } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
        printf("Info: ok, dynamically allocated a %d bytes buffer for section %s.\n",
               elf_Shdr.sh_size, SecNameStnTable + elf_Shdr.sh_name);
      }

      /* Position the file cursor at the begining of proper section. */
      fseek(p_elfimage, (long)(elf_Shdr.sh_offset), 0);
      /* Copy the section's content into this temporary buffer. */
      nbread = fread(section_content, elf_Shdr.sh_size, 1, p_elfimage);

      if (nbread != 1) {
        fprintf(stderr, "Error: could not read %d bytes from ELF file into dynamic buffer.\n", elf_Shdr.sh_size);
        free(section_content);
        fclose(p_elfimage);
        exit(-1);
      } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
        printf("Info: ok, copied content of section %s into dynamic buffer (%d bytes copied).\n",
               SecNameStnTable + elf_Shdr.sh_name, elf_Shdr.sh_size);
      }

      /* Open the special device file. */
      if (!pflag) {
        ifile = open(DEVICE_NAME, ACCESS_MODE, 0);

        if (ifile<0) {
          fprintf(stderr, "Error: could not open %s (open() returned %d, errno=%u)\n", DEVICE_NAME, ifile, errno);
          free(section_content);
          fclose(p_elfimage);
          exit(-1);
        } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
          printf("Info: ok, successfully opened %s.\n", DEVICE_NAME);
        }

        /* Collect control data for ioctl. */
        ioctl_params[0] = UPDATE_FIRMWARE_TRANSFER_BLOCK;
        ioctl_params[1] = elf_Shdr.sh_addr;
        ioctl_params[2] = elf_Shdr.sh_size / 4;
        ioctl_params[3] = (unsigned int)((unsigned int*)section_content);
        //invert4(ioctl_params[1]);
        //invert4(ioctl_params[2]);
        /* Call ioctl driver */
        ioctlretval = ioctl(ifile, openair_UPDATE_FIRMWARE, ioctl_params);

        if (ioctlretval) {
          fprintf(stderr, "Error: ioctl on %s failed.\n", DEVICE_NAME);
          close(ifile);
          free(section_content);
          fclose(p_elfimage);
          exit(-1);
        } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
          printf("Info: ok, successful ioctl on %s for section %s.\n",
                 DEVICE_NAME, SecNameStnTable + elf_Shdr.sh_name);
        }

        close(ifile);
      } /* pflag */

      free(section_content);
    } /* section_name */
  } /* for secnb */
}

void find_and_clear_section_bss(unsigned int verboselevel) {
  /* Interface with driver */
  int ioctlretval;
  int ifile;
  unsigned int secnb = 0;

  for (secnb = 0 ; secnb < elf_Ehdr.e_shnum; secnb++) {
    get_elf_section_header(&elf_Shdr, p_elfimage, secnb);

    if (!strcmp(SecNameStnTable + elf_Shdr.sh_name, ".bss")) {
      if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
        printf("Info: ok, found section %s (as section nb. %d)\n", SecNameStnTable + elf_Shdr.sh_name, secnb);

      /* Open the special device file. */
      if (!pflag) {
        ifile = open(DEVICE_NAME, ACCESS_MODE, 0);

        if (ifile<0) {
          fprintf(stderr, "Error: could not open %s (open() returned %d, errno=%u)\n", DEVICE_NAME, ifile, errno);
          fclose(p_elfimage);
          exit(-1);
        } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
          printf("Info: ok, successfully opened %s.\n", DEVICE_NAME);
        }

        /* Collect control data for ioctl. */
        ioctl_params[0] = UPDATE_FIRMWARE_CLEAR_BSS;
        ioctl_params[1] = elf_Shdr.sh_addr;
        ioctl_params[2] = elf_Shdr.sh_size;
        //invert4(ioctl_params[1]);
        //invert4(ioctl_params[2]);
        /* Call ioctl driver */
        ioctlretval = ioctl(ifile, openair_UPDATE_FIRMWARE, ioctl_params);

        if (ioctlretval) {
          fprintf(stderr, "Error: ioctl on %s failed.\n", DEVICE_NAME);
          close(ifile);
          fclose(p_elfimage);
          exit(-1);
        } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
          printf("Info: ok, successful ioctl on %s for section %s.\n",
                 DEVICE_NAME, SecNameStnTable + elf_Shdr.sh_name);
        }

        close(ifile);
      } /* pflag */
    } /* section_name */
  } /* for secnb */
}

int main(int argc, char** argv) {
  /* Interface with driver */
  int ioctlretval;
  int ifile;
  /* Interface with getopt_long() libC function */
  int getoptret;
  int indexptr;
  int erroroption = FALSE;
  char* p_str_fwn = NULL;
  char* p_str_stkptr = NULL;
  char* filename;
  unsigned int verboselevel = 0;
  unsigned int i;
  char* c;
  unsigned int stackpointer = 0;
  unsigned int ioctl_test_gok = 0;
  unsigned int do_UPDATE_FIRMWARE_START_EXECUTION = 1;

  /*****************
   * Parse options *
   *****************/
  while ((getoptret = getopt_long_only (argc, argv, "f:vhpns:b", updatefw_longopts, &indexptr)) != -1)
    switch (getoptret) {
      /* Without-argument options */
    case 0: /* means that the option just sets a flag. Nothing has to be done,
                 since getopt_long already sets the flag. */
      break;

      /* With-argument options & equivalent short options */
    case 'v':
      verboselevel++;
      break;

    case 'h': /* short switch -h was used */
      helpflag = SWITCH_IS_ON;
      break;

    case 'p': /* short switch -p was used */
      pflag = SWITCH_IS_ON;
      break;

    case 'n': /* short switch -n was used */
      noexecflag = SWITCH_IS_ON;
      break;

    case 'f': /* short switch -f was used */
      fflag = SWITCH_IS_ON;
      p_str_fwn = optarg;
      break;

    case 's': /* short switch -s was used */
      sflag = SWITCH_IS_ON;
      p_str_stkptr = optarg;
      break;

    case 'b': /* short switch -b was used */
      rebootflag = SWITCH_IS_ON;
      break;

    default:
      erroroption = TRUE;
      break;
    };

  /* End of while */

  /********************************
   * Check consistency of options *
   ********************************/
  /* First, any irregularity in the use of the options ? Leave. */
  if (erroroption == TRUE) {
    fprintf(stderr, "%s: Misuse (--help to show usage)\n", argv[0]);
    exit(-1);
  }

  /* Let print the help if it has been explicitly asked for */
  if (helpflag == SWITCH_IS_ON) {
    show_usage(argv[0]);
    exit(0);
  }

  /* Check that mandatory firmware option was specified */
  if (fflag != SWITCH_IS_ON) {
    fprintf(stderr, "%s: missing mandatory [-f|--firmware] option (--help to show usage).\n", argv[0]);
    exit(-1);
  }

  /* Check that mandatory stack pointer option was specified */
  if (sflag != SWITCH_IS_ON) {
    fprintf(stderr, "%s: missing mandatory [-s|--stackptr] option (--help to show usage).\n", argv[0]);
    exit(-1);
  }

  /* Check consistency of stack pointer value. */
  /* 1/2 - The address must start with the 2 characters '0x' and be exactly 10 characters long. */
  if ((strncmp(p_str_stkptr, "0x", 2)) || (strlen(p_str_stkptr) != 10)) { /* 10 is for 0x + 8 hex digits. */
    fprintf(stderr, "%s: Invalid value to [s|--stackptr] option (--help to show usage).\n", argv[0]);
    exit(-1);
  }

  /* 2/2 - The address must only be made of hexadecimal digits. */
  for (i=0,c=p_str_stkptr+2; i < 8 ; i++,c++) {
    if (!
        (   ((*c >= '0') && (*c <= '9'))
            || ((*c >= 'a') && (*c <= 'f'))
            || ((*c >= 'A') && (*c <= 'F'))
        )
       ) {
      fprintf(stderr, "%s: Invalid value to [s|--stackptr] option (--help to show usage).\n", argv[0]);
      exit(-1);
    }

    /* Transcode string hexadecinmal value into binary value */
    if ((*c <= '9') && (*c >= '0'))
      stackpointer |= (*c - '0') << 28-(i*4);
    else if ((*c <= 'F') && (*c >= 'A'))
      stackpointer |= ((*c - 'A') + 10) << 28-(i*4);
    else if ((*c <= 'f') && (*c >= 'a'))
      stackpointer |= ((*c - 'a') + 10) << 28-(i*4);
  }

  /* Open firmware file in READ_ONLY mode. */
  filename = p_str_fwn;
  p_elfimage = fopen(filename, READ_FILE_MODE);

  if (p_elfimage == NULL) {
    fprintf(stderr, "Error : could not open file <%s> in %s mode.\n", filename, READ_FILE_MODE);
    exit(-1);
  }

  if (verboselevel >= VERBOSE_LEVEL_ACCESS_FILES)
    printf("Info: using file <%s> (successfully opened in %s mode).\n", filename, READ_FILE_MODE);

  /* Read elf binary image file */
  /* Get informations from header file */
  if (!(get_elf_header(&elf_Ehdr, p_elfimage))) {
    fprintf(stderr, "Error : file doesn't match expected format.\n");
    exit(-1);
  }

  if (verboselevel >= VERBOSE_LEVEL_ACCESS_FILES)
    printf("Info: ok, file matches expected format.\n");

  /* record the position of the section header table in the file */
  fseek(p_elfimage, (long)(elf_Ehdr.e_shoff), 0);
  Shdr_pos = ftell(p_elfimage);
  /* record the position of the section name string table */
  get_elf_section_header(&elf_Shdr, p_elfimage, elf_Ehdr.e_shstrndx);
  fseek(p_elfimage, (long)(elf_Shdr.sh_offset), 0);
  StringSec_pos = ftell(p_elfimage);
  StringSec_size = elf_Shdr.sh_size;

  /* copy the string table into global variable */
  if (StringSec_size > MAX_SIZE_STRING_TABLE) {
    fprintf(stderr, "Error: section name string table too big (%d > %d);"
            " increase max. allowed value in source code and recompile\n",
            StringSec_size, MAX_SIZE_STRING_TABLE);
    exit(-1);
  }

  fread(SecNameStnTable, 1, StringSec_size, p_elfimage);

  /* Action 1: force reboot (if option was specified) */
  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) {
    if (rebootflag == SWITCH_IS_ON)
      printf("Info: entering action #1 (forcing reboot of Leon).\n");
    else
      printf("Info: squeezed action #1 (forcing reboot of Leon).\n");
  }

  if ((rebootflag == SWITCH_IS_ON) && (!pflag)) {
    ifile = open(DEVICE_NAME, ACCESS_MODE, 0);

    if (ifile<0) {
      fprintf(stderr, "Error: could not open %s (open() returned %d, errno=%u).\n", DEVICE_NAME, ifile, errno);
      fclose(p_elfimage);
      exit(-1);
    } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
      printf("Info: ok, successfully opened %s.\n", DEVICE_NAME);
    }

    ioctl_params[0] = UPDATE_FIRMWARE_FORCE_REBOOT;
    /* Call ioctl driver */
    ioctlretval = ioctl(ifile, openair_UPDATE_FIRMWARE, ioctl_params);

    if (ioctlretval) {
      fprintf(stderr, "Error: ioctl on %s failed.\n", DEVICE_NAME);
      close(ifile);
      fclose(p_elfimage);
      exit(-1);
    } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
      printf("Info: ok, successful ioctl on %s to force reboot.\n", DEVICE_NAME);
    }

    /* If the card received the boot order, then
         1) The firmware who handled the order must have cleared the BOOT_GOK bit
            before rebooting ;
         2) Boot-strap should once again set it back upon its init operations.
       So we wait for this bit to be set again before issuing the rest of the
       update-firmware operations (transfer loadable sections and so on...).
       Note: putting the polling loop here is better than to have it in the
       openair driver, because it avoids hanging the kernel if the card was
       ever not to reboot cleanly. If the loop was to not end, user can simply
       gives up the update-firmware operation by issuing a Ctrl-C. */
    ioctl_params[0] = UPDATE_FIRMWARE_TEST_GOK;

    /* Call ioctl driver */
    if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS)
      printf("Info: waiting for GOK bit (as acknowledge of reboot command) - May take a few seconds...\n");

    ioctl_test_gok = 0;

    do {
      ioctlretval = ioctl(ifile, openair_UPDATE_FIRMWARE, ioctl_params);
      ioctl_test_gok++;
      sleep(1); /* sleep 1 second */

      if (!(ioctl_test_gok % 10))
        printf("Warning: card does not seem to have rebooted cleanly (no GOK bit). Still trying ([Ctrl-C] to abort).\n");
    } while (ioctlretval);

    if (verboselevel >= VERBOSE_LEVEL_IOCTL)
      printf("Info: ok, card set again GOK bit (after %u seconds).\n", ioctl_test_gok);

    close(ifile);
  } /* rebootflag && pflag */

  if ((verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) && (rebootflag == SWITCH_IS_ON))
    printf("Info: action #1 done.\n");

  /* Action 2: Find the .text section */
  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: entering action #2 (Transfer .text section).\n");

  find_and_transfer_section(".text", verboselevel);
  sleep(1);

  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: action #2 done.\n");

  /* Action 3 : Find the .data section */
  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: entering action #3 (Transfer .data section).\n");

  find_and_transfer_section(".data", verboselevel);
  sleep(1);

  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: action #3 done.\n");

  /* Action 4 : Find the .bss  section */
  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: entering action #4 (Clear .bss section).\n");

  find_and_clear_section_bss(verboselevel);
  sleep(1);

  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: action #4 done.\n");

  /* Action 5 : Find the entry address */
  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: entering action #5 (Jump to firmware/set stack-pointer).\n");

  if (verboselevel >= VERBOSE_LEVEL_SECTION_DETAILS)
    printf("Info: Firmware entry point = 0x%08x, setting stack pointer = 0x%08x.\n", elf_Ehdr.e_entry, stackpointer);

  /* Open the special device file. */
  if (!pflag) {
    ifile = open(DEVICE_NAME, ACCESS_MODE, 0);

    if (ifile<0) {
      fprintf(stderr, "Error: could not open %s (open() returned %d, errno=%u).\n", DEVICE_NAME, ifile, errno);
      fclose(p_elfimage);
      exit(-1);
    } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
      printf("Info: ok, successfully opened %s.\n", DEVICE_NAME);
    }

    /* Collect control data for ioctl. */
    ioctl_params[0] = UPDATE_FIRMWARE_START_EXECUTION;
    ioctl_params[1] = elf_Ehdr.e_entry;
    ioctl_params[2] = stackpointer;

    //invert4(ioctl_params[1]);
    //invert4(ioctl_params[2]);
    /* Call ioctl driver */
    if ( noexecflag == SWITCH_IS_OFF ) {
      ioctlretval = ioctl(ifile, openair_UPDATE_FIRMWARE, ioctl_params);

      if (ioctlretval) {
        fprintf(stderr, "Error: ioctl on %s failed.\n", DEVICE_NAME);
        close(ifile);
        fclose(p_elfimage);
        exit(-1);
      } else if (verboselevel >= VERBOSE_LEVEL_IOCTL) {
        printf("Info: ok, successful ioctl on %s for stack-pointer setting/firmware-execution.\n", DEVICE_NAME);
      }
    }

    close(ifile);
  } /* pflag */

  if (verboselevel >= VERBOSE_LEVEL_MAIN_STEPS) printf("Info: action #5 done.\n");

  fclose(p_elfimage);

  /* Did we pretend ? */
  if (pflag) printf("Nothing done.\n");

  exit(0);
}
