aboutsummaryrefslogtreecommitdiffstats
path: root/Lessons/Lesson_13/README.md
blob: ffa7a40c1b2030ff984346a2dd7c48c194a21dfb (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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]
  UefiLib
  ShellCEntryLib
```
Main changes:
- `ENTRY_POINT = ShellCEntryLib` is added to the `[Defines]` section
- `ShellCEntryLib` is added to the `[LibraryClasses]` section instead of `UefiApplicationEntryPoint`

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
  );
```
You can look at the source code of `ShellAppMain` at the https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.c

If you look at the source you could see that istead of a `HandleProtocol` API that we've used:
```
Status = gBS->HandleProtocol(
  ImageHandle,
  &gEfiShellParametersProtocolGuid,
  (VOID **) &ShellParameters
);
```
it uses `OpenProtocol` API: (I've modified a code a little bit to make it comparable to our version)
```
Status = gBS->OpenProtocol(
  ImageHandle,
  &gEfiShellParametersProtocolGuid,
  (VOID **)&ShellParameters,
  ImageHandle,
  NULL,
  EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
```
According to the UEFI spec `HandleProtocol` API is outdated and `OpenProtocol` should be used instead.
`OpenProtocol` API is a more general call that can cover more cases. It all would matter when you start develop UEFI drivers. You can read UEFI spec for more information. Right now just accept a fact that for the UEFI app these two calls are the same.

Let's go back to our code. 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
```