1/** @file
2  Establish the program environment and the "main" entry point.
3
4  All of the global data in the gMD structure is initialized to 0, NULL, or
5  SIG_DFL; as appropriate.
6
7  Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
8  This program and the accompanying materials are licensed and made available under
9  the terms and conditions of the BSD License that accompanies this distribution.
10  The full text of the license may be found at
11  http://opensource.org/licenses/bsd-license.
12
13  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15**/
16#include  <Uefi.h>
17#include  <Library/UefiLib.h>
18#include  <Library/DebugLib.h>
19
20#include  <Library/ShellCEntryLib.h>
21#include  <Library/MemoryAllocationLib.h>
22#include  <Library/TimerLib.h>
23
24#include  <LibConfig.h>
25
26#include  <errno.h>
27#include  <stdio.h>
28#include  <stdlib.h>
29#include  <string.h>
30#include  <time.h>
31#include  <MainData.h>
32#include  <unistd.h>
33
34extern int main( int, char**);
35extern int __sse2_available;
36
37struct  __MainData  *gMD;
38
39/* Worker function to keep GCC happy. */
40void __main()
41{
42  ;
43}
44
45/** Clean up data as required by the exit() function.
46
47**/
48void
49exitCleanup(INTN ExitVal)
50{
51  void (*CleanUp)(void);   // Pointer to Cleanup Function
52  int i;
53
54  if(gMD != NULL) {
55    gMD->ExitValue = (int)ExitVal;
56    CleanUp = gMD->cleanup; // Preserve the pointer to the Cleanup Function
57
58    // Call all registered atexit functions in reverse order
59    i = gMD->num_atexit;
60    if( i > 0) {
61      do {
62        (gMD->atexit_handler[--i])();
63      } while( i > 0);
64  }
65
66    if (CleanUp != NULL) {
67      CleanUp();
68    }
69  }
70}
71
72/* Create mbcs versions of the Argv strings. */
73static
74char **
75ArgvConvert(UINTN Argc, CHAR16 **Argv)
76{
77  ssize_t  AVsz;       /* Size of a single nArgv string, or -1 */
78  UINTN   count;
79  char  **nArgv;
80  char   *string;
81  INTN    nArgvSize;  /* Cumulative size of narrow Argv[i] */
82
83DEBUG_CODE_BEGIN();
84  DEBUG((DEBUG_INIT, "ArgvConvert called with %d arguments.\n", Argc));
85  for(count = 0; count < ((Argc > 5)? 5: Argc); ++count) {
86    DEBUG((DEBUG_INIT, "Argument[%d] = \"%s\".\n", count, Argv[count]));
87  }
88DEBUG_CODE_END();
89
90  nArgvSize = Argc;
91  /* Determine space needed for narrow Argv strings. */
92  for(count = 0; count < Argc; ++count) {
93    AVsz = (ssize_t)wcstombs(NULL, Argv[count], ARG_MAX);
94    if(AVsz < 0) {
95      DEBUG((DEBUG_ERROR, "ABORTING: Argv[%d] contains an unconvertable character.\n", count));
96      exit(EXIT_FAILURE);
97      /* Not Reached */
98    }
99    nArgvSize += AVsz;
100  }
101
102  /* Reserve space for the converted strings. */
103  gMD->NCmdLine = (char *)AllocateZeroPool(nArgvSize+1);
104  if(gMD->NCmdLine == NULL) {
105    DEBUG((DEBUG_ERROR, "ABORTING: Insufficient memory.\n"));
106    exit(EXIT_FAILURE);
107    /* Not Reached */
108  }
109
110  /* Convert Argument Strings. */
111  nArgv   = gMD->NArgV;
112  string  = gMD->NCmdLine;
113  for(count = 0; count < Argc; ++count) {
114    nArgv[count] = string;
115    AVsz = wcstombs(string, Argv[count], nArgvSize) + 1;
116    DEBUG((DEBUG_INFO, "Cvt[%d] %d \"%s\" --> \"%a\"\n", (INT32)count, (INT32)AVsz, Argv[count], nArgv[count]));
117    string += AVsz;
118    nArgvSize -= AVsz;
119    if(nArgvSize < 0) {
120      DEBUG((DEBUG_ERROR, "ABORTING: Internal Argv[%d] conversion error.\n", count));
121      exit(EXIT_FAILURE);
122      /* Not Reached */
123    }
124  }
125  return gMD->NArgV;
126}
127
128INTN
129EFIAPI
130ShellAppMain (
131  IN UINTN Argc,
132  IN CHAR16 **Argv
133  )
134{
135  struct __filedes   *mfd;
136  char              **nArgv;
137  INTN   ExitVal;
138  int                 i;
139
140  ExitVal = (INTN)RETURN_SUCCESS;
141  gMD = AllocateZeroPool(sizeof(struct __MainData));
142  if( gMD == NULL ) {
143    ExitVal = (INTN)RETURN_OUT_OF_RESOURCES;
144  }
145  else {
146    /* Initialize data */
147    __sse2_available      = 0;
148    _fltused              = 1;
149    errno                 = 0;
150    EFIerrno              = 0;
151
152    gMD->ClocksPerSecond  = 1;
153    gMD->AppStartTime     = (clock_t)((UINT32)time(NULL));
154
155    // Initialize file descriptors
156    mfd = gMD->fdarray;
157    for(i = 0; i < (FOPEN_MAX); ++i) {
158      mfd[i].MyFD = (UINT16)i;
159    }
160
161    DEBUG((DEBUG_INIT, "StdLib: Open Standard IO.\n"));
162    i = open("stdin:", (O_RDONLY | O_TTY_INIT), 0444);
163    if(i == 0) {
164      i = open("stdout:", (O_WRONLY | O_TTY_INIT), 0222);
165      if(i == 1) {
166        i = open("stderr:", O_WRONLY, 0222);
167      }
168    }
169    if(i != 2) {
170      Print(L"ERROR Initializing Standard IO: %a.\n    %r\n",
171            strerror(errno), EFIerrno);
172    }
173
174    /* Create mbcs versions of the Argv strings. */
175    nArgv = ArgvConvert(Argc, Argv);
176    if(nArgv == NULL) {
177      ExitVal = (INTN)RETURN_INVALID_PARAMETER;
178    }
179    else {
180      if( setjmp(gMD->MainExit) == 0) {
181        errno   = 0;    // Clean up any "scratch" values from startup.
182        ExitVal = (INTN)main( (int)Argc, gMD->NArgV);
183        exitCleanup(ExitVal);
184      }
185      /* You reach here if:
186          * normal return from main()
187          * call to _Exit(), either directly or through exit().
188      */
189      ExitVal = (INTN)gMD->ExitValue;
190    }
191
192    if( ExitVal == EXIT_FAILURE) {
193      ExitVal = RETURN_ABORTED;
194    }
195
196    /* Close any open files */
197    for(i = OPEN_MAX - 1; i >= 0; --i) {
198      (void)close(i);   // Close properly handles closing a closed file.
199    }
200
201    /* Free the global MainData structure */
202    if(gMD != NULL) {
203      if(gMD->NCmdLine != NULL) {
204        FreePool( gMD->NCmdLine );
205      }
206      FreePool( gMD );
207  }
208  }
209  return ExitVal;
210}
211