Change the startup code to fix a memory leak and to allow us to

accept load options (=command line options).

The call graph changes from *entry*->efi_main->efi_init, where
efi_main is the EFI equivalent of main to *entry*->efi_main->main,
where main is what you'd expect. efi_main now is what efi_init was.
The prototype of main follows that of C. The first argument is argc
and the second is argv. There is no third argument.
Allocation of heap pages is now handled by the EFI library and it
now deallocates the pages when main() returns or when exit() is
called. This allows us to safely return to the boot manager (or
EFI shell) without leaks. EFI applications are responsible to free
all memory themselves.

Handling of the load options is a bit tricky. There are either no
load options, load options in ASCII or load options in Unicode.
The EFI library will translate the ASCII options to Unicode options
as to simplify user code. Since the load options are passed as a
single string (if present) and main() accepts argc and argv, the
startup code also has to split the string into words and build the
argv vector. Here the trickiness starts. When the loader is started
from the EFI shell, argv[0] will automaticly load the program name.
In all other cases (ie through the boot manager), this is not the
case. Unfortunately, there's no trivial way to check. Hence, a
set of conditions is checked to determine if we need to fill in
argv[0] ourselves or not. This checking is not perfect. There are
known cases where it fails to do the right thing. The logic works
for most expected cases, though. This includes the case where no
options are given.

Approved by: re (blanket)
This commit is contained in:
Marcel Moolenaar 2002-12-10 06:22:25 +00:00
parent 4ee2f7cb16
commit 155dbcacfb
5 changed files with 142 additions and 42 deletions

View File

@ -31,4 +31,7 @@ extern EFI_SYSTEM_TABLE *ST;
extern EFI_BOOT_SERVICES *BS;
extern EFI_RUNTIME_SERVICES *RS;
void efi_init(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
void efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
EFI_STATUS main(int argc, CHAR16 *argv[]);
void exit(EFI_STATUS status);

View File

@ -5,7 +5,7 @@
LIB= efi
INTERNALLIB= true
SRCS= libefi.c efi_console.c time.c copy.c devicename.c module.c exit.c
SRCS= libefi.c efi_console.c time.c copy.c devicename.c module.c
SRCS+= delay.c efifs.c efinet.c elf_freebsd.c bootinfo.c pal.s
.if ${MACHINE_ARCH} == "ia64"

View File

@ -31,17 +31,146 @@ static const char rcsid[] =
#include <efi.h>
#include <efilib.h>
#include <stand.h>
EFI_HANDLE IH;
EFI_SYSTEM_TABLE *ST;
EFI_BOOT_SERVICES *BS;
EFI_RUNTIME_SERVICES *RS;
void
efi_init(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
static EFI_PHYSICAL_ADDRESS heap;
static UINTN heapsize;
static CHAR16 *
arg_skipsep(CHAR16 *argp)
{
while (*argp == ' ' || *argp == '\t')
argp++;
return (argp);
}
static CHAR16 *
arg_skipword(CHAR16 *argp)
{
while (*argp && *argp != ' ' && *argp != '\t')
argp++;
return (argp);
}
void exit(EFI_STATUS exit_code)
{
BS->FreePages(heap, EFI_SIZE_TO_PAGES(heapsize));
BS->Exit(IH, exit_code, 0, NULL);
}
void
efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
{
static EFI_GUID image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_LOADED_IMAGE *img;
CHAR16 *argp, *args, **argv;
EFI_STATUS status;
int argc, addprog;
IH = image_handle;
ST = system_table;
BS = ST->BootServices;
RS = ST->RuntimeServices;
heapsize = 512*1024;
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
EFI_SIZE_TO_PAGES(heapsize), &heap);
if (status != EFI_SUCCESS)
BS->Exit(IH, status, 0, NULL);
setheap((void *)heap, (void *)(heap + heapsize));
/* Use exit() from here on... */
status = BS->HandleProtocol(IH, &image_protocol, (VOID**)&img);
if (status != EFI_SUCCESS)
exit(status);
/*
* Pre-process the (optional) load options. If the option string
* is given as an ASCII string, we use a poor man's ASCII to
* Unicode-16 translation. The size of the option string as given
* to us includes the terminating null character. We assume the
* string is an ASCII string if strlen() plus the terminating
* '\0' is less than LoadOptionsSize. Even if all Unicode-16
* characters have the upper 8 bits non-zero, the terminating
* null character will cause a one-off.
* If the string is already in Unicode-16, we make a copy so that
* we know we can always modify the string.
*/
if (img->LoadOptionsSize) {
if (img->LoadOptionsSize == strlen(img->LoadOptions) + 1) {
args = malloc(img->LoadOptionsSize << 1);
for (argc = 0; argc < img->LoadOptionsSize; argc++)
args[argc] = ((char*)img->LoadOptions)[argc];
} else {
args = malloc(img->LoadOptionsSize);
memcpy(args, img->LoadOptions, img->LoadOptionsSize);
}
} else
args = NULL;
/*
* Use a quick and dirty algorithm to build the argv vector. We
* first count the number of words. Then, after allocating the
* vector, we split the string up. We don't deal with quotes or
* other more advanced shell features.
* The EFI shell will pas the name of the image as the first
* word in the argument list. This does not happen if we're
* loaded by the boot manager. This is not so easy to figure
* out though. The ParentHandle is not always NULL, because
* there can be a function (=image) that will perform the task
* for the boot manager.
*/
/* Part 1: Figure out if we need to add our program name. */
addprog = (args == NULL || img->ParentHandle == NULL ||
img->FilePath == NULL) ? 1 : 0;
if (!addprog) {
addprog =
(DevicePathType(img->FilePath) != MEDIA_DEVICE_PATH ||
DevicePathSubType(img->FilePath) != MEDIA_FILEPATH_DP ||
DevicePathNodeLength(img->FilePath) <=
sizeof(FILEPATH_DEVICE_PATH)) ? 1 : 0;
if (!addprog) {
/* XXX todo. */
}
}
/* Part 2: count words. */
argc = (addprog) ? 1 : 0;
argp = args;
while (argp != NULL && *argp != 0) {
argp = arg_skipsep(argp);
if (*argp == 0)
break;
argc++;
argp = arg_skipword(argp);
}
/* Part 3: build vector. */
argv = malloc((argc + 1) * sizeof(CHAR16*));
argc = 0;
if (addprog)
argv[argc++] = L"loader.efi";
argp = args;
while (argp != NULL && *argp != 0) {
argp = arg_skipsep(argp);
if (*argp == 0)
break;
argv[argc++] = argp;
argp = arg_skipword(argp);
/* Terminate the words. */
if (*argp != 0)
*argp++ = 0;
}
argv[argc] = NULL;
status = main(argc, argv);
exit(status);
}

View File

@ -104,27 +104,13 @@ find_pal_proc(void)
}
EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
main(int argc, CHAR16 *argv[])
{
EFI_PHYSICAL_ADDRESS mem;
EFI_LOADED_IMAGE *img;
EFI_SIMPLE_NETWORK *net;
EFI_STATUS status;
struct ia64_pal_result res;
char buf[32];
int i;
efi_init(image_handle, system_table);
/*
* Initialise the heap as early as possible. Once this is done,
* alloc() is usable. The stack is buried inside us, so this is
* safe.
*/
BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
512*1024/4096, &mem);
setheap((void *)mem, (void *)(mem + 512*1024));
/*
* XXX Chicken-and-egg problem; we want to have console output
* early, but some console attributes may depend on reading from
@ -148,7 +134,7 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
(devsw[i]->dv_init)();
efinet_init_driver();
printf("\n");
printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
printf("(%s, %s)\n", bootprog_maker, bootprog_date);
@ -156,14 +142,13 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
printf("Memory: %ld k\n", memsize() / 1024);
#endif
/*
* XXX quick and dirty check to see if we're loaded from the
* network. If so, we set the default device to 'net'. In all
* other cases we set the default device to 'disk'. We presume
* fixed positions in devsw for both net and disk.
*/
BS->HandleProtocol(image_handle, &imgid, (VOID**)&img);
BS->HandleProtocol(IH, &imgid, (VOID**)&img);
status = BS->HandleProtocol(img->DeviceHandle, &netid, (VOID**)&net);
if (status == EFI_SUCCESS && net != NULL) {
@ -177,7 +162,6 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
/* default to 'a' */
currdev.d_kind.efidisk.partition = 0;
}
currdev.d_type = currdev.d_dev->dv_type;
/*

View File

@ -104,27 +104,13 @@ find_pal_proc(void)
}
EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
main(int argc, CHAR16 *argv[])
{
EFI_PHYSICAL_ADDRESS mem;
EFI_LOADED_IMAGE *img;
EFI_SIMPLE_NETWORK *net;
EFI_STATUS status;
struct ia64_pal_result res;
char buf[32];
int i;
efi_init(image_handle, system_table);
/*
* Initialise the heap as early as possible. Once this is done,
* alloc() is usable. The stack is buried inside us, so this is
* safe.
*/
BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
512*1024/4096, &mem);
setheap((void *)mem, (void *)(mem + 512*1024));
/*
* XXX Chicken-and-egg problem; we want to have console output
* early, but some console attributes may depend on reading from
@ -148,7 +134,7 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
(devsw[i]->dv_init)();
efinet_init_driver();
printf("\n");
printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
printf("(%s, %s)\n", bootprog_maker, bootprog_date);
@ -156,14 +142,13 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
printf("Memory: %ld k\n", memsize() / 1024);
#endif
/*
* XXX quick and dirty check to see if we're loaded from the
* network. If so, we set the default device to 'net'. In all
* other cases we set the default device to 'disk'. We presume
* fixed positions in devsw for both net and disk.
*/
BS->HandleProtocol(image_handle, &imgid, (VOID**)&img);
BS->HandleProtocol(IH, &imgid, (VOID**)&img);
status = BS->HandleProtocol(img->DeviceHandle, &netid, (VOID**)&net);
if (status == EFI_SUCCESS && net != NULL) {
@ -177,7 +162,6 @@ efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table)
/* default to 'a' */
currdev.d_kind.efidisk.partition = 0;
}
currdev.d_type = currdev.d_dev->dv_type;
/*