aboutsummaryrefslogtreecommitdiffstats
path: root/Lesson_13/README.md
blob: 5ba6fb3f51b4283724e6dd848551817b46b3c202 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
In previous lesson we've used `EFI_SHELL_PARAMETERS_PROTOCOL` to get command line parameters to our app.
It is a valid method but there is a simpler way to do it for the shell apps.

To ease things we can use the entry point in this format:
https://github.com/tianocore/edk2/blob/master/ShellPkg/Include/Library/ShellCEntryLib.h
```
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  );
```
This way `Argc`/`Argv` are passed directly to the app entry point like it is usually happening in C programming.

Let's create a `SimpleShellApp` based on our `HelloWorld` app.

With the necessary modifications the INF file would look like this:
```
[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = SimpleShellApp
  FILE_GUID                      = 2afd1202-545e-4f8d-b8fb-bc179e84ddc8
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = ShellCEntryLib

[Sources]
  SimpleShellApp.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  ShellCEntryLib
```
Main changes:
- `ENTRY_POINT = ShellCEntryLib` is added to the `[Defines]` section
- `ShellCEntryLib` is added to the `[LibraryClasses]` section

In the end it works this way. Shell C library is the main UEFI app with the standard entry point:
```
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
```
It parses incoming parameters with the `EFI_SHELL_PARAMETERS_PROTOCOL` like we did it and then calls our app entry point passing all of the parsed parameteres with the call:
```
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  );
```


To find the necessary `ShellCEntryLib` library class search as usual:
```
$ grep ShellCEntryLib -r ./ --include=*.inf | grep LIBRARY_CLASS
./ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf:  LIBRARY_CLASS                  = ShellCEntryLib|UEFI_APPLICATION UEFI_DRIVER
```

Add this library class to our `UefiLessonsPkg/UefiLessonsPkg.dsc`:
```
ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
```
Don't forget also to add our new app under the `[Components]` section:
```
UefiLessonsPkg/SimpleShellApp/SimpleShellApp.inf
```


Now let's look at the `*.c` file.

We can't use our first print method as `SystemTable` now is unavailable, but we can simply use `SystemTable` with the help of `gST` pointer:
```
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
{
  Print(L"Argc=%d\n", Argc);
//  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
  gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
  Print(L"Bye!\n");
  return 0;
}
```

Let's add some parameters handling code:
```
for (UINTN i=Argc; i>0; i--) {
  Print(L"Arg[%d]=%s\n", Argc-i, Argv[Argc-i]);
}
```

If we test our app under OVMF we would get:
```
FS0:\> SimpleShellApp.efi kkk ggg
Hello again!
Bye!
Arg[0]=FS0:\SimpleShellApp.efi
Arg[1]=kkk
Arg[2]=ggg
FS0:\> SimpleShellApp.efi
Hello again!
Bye!
Arg[0]=FS0:\SimpleShellApp.efi
```