On Jun 16, 2022, at 1:17 PM, Ayush Singh <ayushdevel1325@gmail.com> wrote:

Thanks for the great answer. After some discussion in the zulip [1], I
have some approaches that I will try first.

As for calling C, EFIABI from Rust, yes, it is quite well supported.
Rust also has a specific EFIABI now, since some EFI platforms don't
use C calling conventions.

As for the entry point, llvm has an `-entry:efi_main` that can be used
to define the entry function for UEFI targets. I just was not sure I
could integrate it with the actual `main()` function.


For the C builds the tools_def.txt file uses  $(IMAGE_ENTRY_POINT). The build maps that over to the _ModelEntryPoint label I mentioned. It would probably be good to sue the same symbol. 

The C we have is free standing so there is nothing that is setup for the C language, other that libs the user asked for.

In normal Rust, the control flow is somewhat like this: crt0 -> libc
main -> rust lang_start -> rust lang_start_internal -> rust main

In UEFI, I would have to do something like this: efi_main -> rust
lang_start -> rust lang_start_internal -> rust main


The edk2 way to do this seems to me is to create an edk2 RustEntryPoint lib that models the edk2 *EntryPointLib [1]. The entry point would be  _ModelEntryPoint.

I’m not 100% clear on all the dependencies but the big picture is for C edk2 injects code between the entry point and the main function. I think you will want that in your Rust world.

The other thing you need to manage is the entry point hands-off  the only way to bind to all the EFI Services so that needs to make it into your Rust world. 

EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
The Apps need access to SystemTable to do just about anything. The ImageHandle lets them get access to args and info on how the driver/app was loaded. 

We have different flavors of these entry point libs as the handoff, and sometimes entry exit behavior are different:
$ git grep 'EntryPoint|' -- \*.inf | grep LIBRARY_CLASS
MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf:18:  LIBRARY_CLASS                  = DxeCoreEntryPoint|DXE_CORE
MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf:18:  LIBRARY_CLASS                  = PeiCoreEntryPoint|PEI_CORE
MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf:18:  LIBRARY_CLASS                  = PeimEntryPoint|PEIM
MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf:21:  LIBRARY_CLASS                  = StandaloneMmDriverEntryPoint|MM_STANDALONE
MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf:18:  LIBRARY_CLASS                  = UefiApplicationEntryPoint|UEFI_APPLICATION
MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf:18:  LIBRARY_CLASS                  = UefiDriverEntryPoint|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_DRIVER SMM_CORE DXE_SMM_DRIVER
StandaloneMmPkg/Library/StandaloneMmCoreEntryPoint/StandaloneMmCoreEntryPoint.inf:18:  LIBRARY_CLASS                  = StandaloneMmCoreEntryPoint|MM_CORE_STANDALONE

I think what this means from a practical point after reading your Rust thread is:
1) Have some custom code, per driver type, to maybe convert the EFI/PEI/edk2 define entry point arguments maybe into standard Rust args. 
  argc = 2
  argv[0] = ImageHandle
  argv[1] =  *SystemTable

2) Then you can call the common Rust init flow from your link. 

3) rt_entry() is custom for edk2. It would basically do the same things as the edk2 C *EntryPoint libs _ModuleEntryPoint() functions. Call the auto generated lib constructor functions, call the auto generated entry point function that calls the function in the users Rust code. Call the lib destructor. Also provide support for the Exit function. 

You can kind of hard code bits to get started, but If you think about it this way I think it will be easier to layer in edk2 like build features as we grow the Rust support. 

You could get really fancy and pass the mode BASE/PEIM/DXE in argv[0], and the args in args[1], …. By doing that you might need small stubs that are mode specific to capture the different entry point APIs, but all the other lib infrastructure could be common. The little tiny entry stub libs could depend on the common lib so the INF files would only need to specify the entry point stub lib.

[1] https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiApplicationEntryPoint/ApplicationEntryPoint.c

Thanks,

Andrew Fish

The problem was that I couldn't find a way to go from `efi_main ->
rust lang_start` earlier. After all, it is such a low-level detail
that there is almost no documentation for anything that happens before
`rust lang_start`. Still, I do have some idea now, so will see how it
goes.

Ayush Singh

[1]: (https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Run.20a.20function.20before.20.60lang_start.60/near/286376012)

On Fri, Jun 17, 2022 at 12:06 AM Andrew Fish <afish@apple.com> wrote:



On Jun 16, 2022, at 7:20 AM, Ayush Singh <ayushdevel1325@gmail.com> wrote:

Hello everyone, I wanted to ask if the edk2 build system also links to
crt0-efi, like GNU-EFI?

If yes, I would also like to see how that is actually implemented. If
not, how does edk2 support custom entry functions? It is possible with
llvm backend but I am not sure how it is done in GCC and am curious,


The general answer for edk2 is and library that a `CONSTRUCTOR =` statement in its INF that lib constructor will get called when the Driver/App is started.

The actually entry point for a Driver/App is _ModuleEntryPoint(). The typical way it is done is this is implemented in a phase specific library[1]. This phase specific basically calls 3 C functions that got generated by the build: ProcessLibraryConstructorList(), ProcessModuleEntryPointList(), and ProcessLibraryDestructorList(). The library constructor/destructors functions call the lib  constructor/destructors function based in the sequence of the dependency graph of the libraries that get pulled in.

Currently, rust does not support the custom implementation of
`lang_start` (which is started by crt0 in most platforms), so I was
trying to find ways to be able to use custom crt0 which sets up
`SystemTable` and `SystemHandler` and start the `lang_start` from it.
This way, the user will be able to call the normal `main` function
rather than using the `no_main` feature.


If you look in the build output of an edk2 C driver/app you will see an AutoGen.h and AutoGen.c file. This was the C code the build system autogenerated to glue everything together. It manages gluing in the libs, abstracting the PCD implementation, and adding C constants for EFI_GUID values.

Sorry I don’t know Rust yet. Is it possible to call C, EFIABI, from Rust code? If yes maybe what you need is a Rust ModuleEntryPoint that can call the C library constructor and a C EFIABI entry function for your Rust. I’m not sure how you manage dealing with C includes in Rust? With C++ you can just decorate them.

I guess the other 100% Rust option is to know the INF is building Rust (INF has Code.rs files) and build Rust (or Rust compatible) AutoGen

My blog post [1] shows how we currently use the `efi_main` function.

Yours sincerely,
Ayush Singh

[1]: (https://www.programmershideaway.xyz/post5/)



[1] https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiApplicationEntryPoint/ApplicationEntryPoint.c

Thanks,

Andrew Fish