flat assembler
Message board for the users of flat assembler.

Index > Main > UEFI questions

Goto page 1, 2, 3  Next
Author
Thread Post new topic Reply to topic
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 17 Aug 2016, 20:38
Hello, I've been trying to figure out how to start coding with UEFI in assembler.
I found a few examples of very basic programs from osdev in fasm, tried them and yay, they worked.

Since I'm new to UEFI in asm I have a couple of questions. The one to start off with:

1. Why do we have to save ebp on entry and restore it before exit?

Code:
include 'efi.inc'

main:
        push ebp; !!!!!!!!!!!!!!!!!!!!!!!! ---- Saving
        mov ebp, esp

        ...
        blah blah blah
        ...

        mov ecx, [edx + 4]
        mov eax, EFI_SYSTEM_TABLE_SIGNATURE2
        cmp eax, ecx
        jne error

        ...
        blah blah blah
        ...

success:
        mov eax, EFI_SUCCESS
        pop ebp ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- Restoring
        retn

error:
        mov eax, 1
        pop ebp ; !!!!!!!!!!!!!!!!!!!!!!!!!!! --- And once again
        retn
    
Post 17 Aug 2016, 20:38
View user's profile Send private message Reply with quote
Trinitek



Joined: 06 Nov 2011
Posts: 257
Trinitek 17 Aug 2016, 22:51
UEFI on x86-64 platforms uses Microsoft's x64 calling convention. You would see the same saving and restoring procedure when entering and leaving a 64-bit function in userspace code on Windows as well.
Post 17 Aug 2016, 22:51
View user's profile Send private message Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 18 Aug 2016, 06:56
Thank you Trinitec.
I looked that up, so very interesting thing to read.
Post 18 Aug 2016, 06:56
View user's profile Send private message Reply with quote
Trinitek



Joined: 06 Nov 2011
Posts: 257
Trinitek 18 Aug 2016, 12:46
Actually, since that's 32-bit code, that would be the cdecl calling convention, but the saving/restoring procedure is the same. Not sure why I didn't catch that.
Post 18 Aug 2016, 12:46
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 18 Aug 2016, 13:20
ebp is used for comfortable access to the arguments and local variables, both located on the stack.
Of course, you can access these variables using esp directly, but if you push/pop some values in the stack, esp will change and the offset to the variables will change as well. This will make the code much less readable and the risk of bugs increases. In the same time, once, esp is copied to ebp, you can use ebp with constant offset to every local variable (function argument).

So, this way, If you don't use local variables or procedure arguments and don't change ebp, there is no need to save it.
Post 18 Aug 2016, 13:20
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 19 Aug 2016, 06:34
Umm I've read there about preserving registers, calling conventions, shadow space in the stack... Do I have to adhere to all that within my code as well or does it only refer to external calls and rets (apart from readability issues)?
Post 19 Aug 2016, 06:34
View user's profile Send private message Reply with quote
Trinitek



Joined: 06 Nov 2011
Posts: 257
Trinitek 19 Aug 2016, 06:55
MrFox wrote:
Umm I've read there about preserving registers, calling conventions, shadow space in the stack... Do I have to adhere to all that within my code as well or does it only refer to external calls and rets (apart from readability issues)?
It's absolutely required that you adhere to the convention when calling external functions. You can do whatever you want with internal functions, however. I think that you should be aware that there are macros available that allow you to define and call functions of a particular convention, so you do not have to write the entry and exit procedures yourself.
Post 19 Aug 2016, 06:55
View user's profile Send private message Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 24 Aug 2016, 18:02
Thanks, Trinitec, that's what I needed to know.
AFAIK, there are not only macros but even special x86 assembly instructions created especially to do such things.
Enter and Leave: http://stackoverflow.com/questions/5858996/enter-and-leave-in-assembly
does FASM support them?

Question #2
I'm wracking my brains around the http://wiki.osdev.org/Uefi.inc and can't figure out why on earth the guy needed all those bells and whistles with redefining data types and structures (see efi.inc titled "The include file" at the middle of the page).

Namely, I can't get why he needed all those:
Code:
...

struc int64 {
  align 8
  . dq ?
}

...

macro struct name
{
  virtual at 0
    name name
  end virtual
}

...

struc EFI_TABLE_HEADER {
 .Signature             int64
 .Revision              int32
 .HeaderSize            int32
 .CRC32                 int32
 .Reserved              int32
}
struct EFI_TABLE_HEADER

struc EFI_SYSTEM_TABLE {
 .Hdr                   EFI_TABLE_HEADER
 .FirmwareVendor        dptr
 .FirmwareRevision      int32
 .ConsoleInHandle       dptr
 .ConIn                 dptr
 .ConsoleOutHandle      dptr
 .ConOut                dptr
 .StandardErrorHandle   dptr
 .StdErr                dptr
 .RuntimeServices       dptr
 .BootServices          dptr
 .NumberOfTableEntries  intn
 .ConfigurationTable    dptr
}
struct EFI_SYSTEM_TABLE
...    


why don't we just use:

Code:
struc EFI_TABLE_HEADER
{virtual at 0
;----------------------
 .Signature             dq ?
 .Revision              dd ?
 .HeaderSize            dd ?
 .CRC32                 dd ?
 .Reserved              dd ?
;----------------------
 end virtual}    
Post 24 Aug 2016, 18:02
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20462
Location: In your JS exploiting you and your system
revolution 24 Aug 2016, 18:14
When you put "virtual at 0" inside the struc definition then you won't be able to instantiate the structure into any memory address other than 0. So probably not a good idea unless you know that you never need anything else.

For "enter" and "leave", yes fasm supports them. But most people don't use them because of concerns about performance. But you are free to use them if you wish to.
Post 24 Aug 2016, 18:14
View user's profile Send private message Visit poster's website Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 24 Aug 2016, 18:28
Oh, thanks. What do you mean by performance? Any issues using them?

If I understood that code correctly, that guy uses the structures to only provide offsets, like this:
Code:
mov edx, dword [rax+EFI_BLOCK_IO_MEDIA.MediaId]    

or this:
Code:
clear:
        mov eax, EFI_SYSTEM_TABLE.ConOut
        mov ecx, [SystemTable]
        add ecx, eax
        mov ecx, [ecx]

        mov edx, ecx
        mov eax, SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen
        add edx, eax

        push ecx                                ; [arg] SystemTable.ConOut

        mov edx, [edx]
        call edx
        add esp, 4    

I guess my interpretation of his code will work exactly the same way, won't it?
Post 24 Aug 2016, 18:28
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20462
Location: In your JS exploiting you and your system
revolution 24 Aug 2016, 18:38
By performance, this is how other people view them. Other people think that enter/leave are slow so they avoid them. Sometimes this makes sense, sometimes it doesn't matter. It depends upon what you are doing and where you use them.

If you only ever reference the struc in the way you show then it makes no difference. But you still need to initialise the values by instantiating the struc somewhere, you can't escape this requirement. Merely declaring the struc will not define the value of EFI_TABLE_HEADER.Signature. So you lose some flexibility without any gain in simplicity IMO.
Post 24 Aug 2016, 18:38
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 24 Aug 2016, 19:01
revolution wrote:
If you only ever reference the struc in the way you show then it makes no difference. But you still need to initialise the values by instantiating the struc somewhere, you can't escape this requirement. Merely declaring the struc will not define the value of EFI_TABLE_HEADER.Signature. So you lose some flexibility without any gain in simplicity IMO.
It could be a little bit simpler if "struc" was not used at all:
Code:
virtual at 0 
 EFI_TABLE_HEADER:
 .Signature             dq ? 
 .Revision              dd ? 
 .HeaderSize            dd ? 
 .CRC32                 dd ? 
 .Reserved              dd ? 
end virtual    
This is how structure offsets used to be defined before fasm had "struc" implemented.
Post 24 Aug 2016, 19:01
View user's profile Send private message Visit poster's website Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 24 Aug 2016, 19:07
Ok, I got it. Thanks.
The thing is, we do not initialize any of these tables or handles. They all already reside in memory when our UEFI app (they call it 'Image') starts and we only need these structures to address offsets within those tables. The app gains System_Table pointer and Loaded_Image handle pointer through stack when it's loaded and all the other references are being made relative to these pointers, 'browsing' the existing tree of structs and handles. That's why I think (as of now, at least) I'll never have to initialize those structures, only to map them on existing addresses. I may be mistaken though as I'm a lil noobie. Smile

Would you recommend that I stick to his way of declaration when introducing other structures from UEFI spec (there are tons of them) into that file?

---------------

Added: Thanks, Tomasz!!! Simplicity is what I need as these are my 'first steps' at fasm.
Post 24 Aug 2016, 19:07
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20462
Location: In your JS exploiting you and your system
revolution 24 Aug 2016, 19:13
MrFox wrote:
Would you recommend that I stick to his way of declaration when introducing other structures from UEFI spec (there are tons of them) into that file?
It is just personal preference. I would suggest that you do what makes sense to you, not necessarily what gives the most flexibility, or whatever other metric, if it confuses you. But it is nice to be able to understand the various methods and know that you can use them when you might need the extra flexibility in the future.
Post 24 Aug 2016, 19:13
View user's profile Send private message Visit poster's website Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 24 Aug 2016, 19:24
Okay, as of now, I think I'll do what suits me best, i.e. put it as Tomasz suggested, because I hate it when there are pieces in MY code that I don't 100% understand (even if they are technically more correct).

And of course, I'll be 'reading the source' in order to improve my personal understanding of fasm syntax and working principles to get that extra flexibility in the future.

Thanks, guys, you rock!
Post 24 Aug 2016, 19:24
View user's profile Send private message Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 25 Aug 2016, 05:53
Code:
virtual at 0 
 EFI_TABLE_HEADER:
 .Signature             dq ? 
 .Revision              dd ? 
 .HeaderSize            dd ? 
 .CRC32                 dd ? 
 .Reserved              dd ? 
end virtual    

How can I implement nested structures?

I need something like this:
Code:
struc EFI_SYSTEM_TABLE {
 .Hdr                           EFI_TABLE_HEADER
 .FirmwareVendor                dptr
 .FirmwareRevision              int32
 .ConsoleInHandle               dptr
 .ConIn                         dptr
 .ConsoleOutHandle              dptr
 .ConOut                        dptr
 .StandardErrorHandle           dptr
 .StdErr                        dptr
 .RuntimeServices               dptr
 .BootServices                  dptr
 .NumberOfTableEntries          intn
 .ConfigurationTable            dptr
}    
Post 25 Aug 2016, 05:53
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 25 Aug 2016, 07:08
For this you need "struc", because you need to instantiate the same structure in different places. Simplified variant is not your friend in such case.
Post 25 Aug 2016, 07:08
View user's profile Send private message Visit poster's website Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 25 Aug 2016, 11:16
Thanks!

I have a question regarding programming technique.

The task is to browse through the system table and its subtables. They contain values and pointers. This man uses different registers to move along the table and jump to pointers:

Code:
clear:
        mov eax, EFI_SYSTEM_TABLE.ConOut
        mov ecx, [SystemTable]
        add ecx, eax
        mov ecx, [ecx]

        mov edx, ecx
        mov eax, SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen
        add edx, eax

        push ecx                                ; [arg] SystemTable.ConOut

        mov edx, [edx]
        call edx
        add esp, 4
        ret
    


Why don't we just use one register for all that? Would it be faster or slower?

Code:
clear:
        mov eax, [SystemTable]
        add eax, EFI_SYSTEM_TABLE.ConOut
        mov eax, [eax]
        push eax                                ; [arg] SystemTable.ConOut

        add eax, SIMPLE_TEXT_OUTPUT_INTERFACE.ClearScreen
        mov eax, [eax]
        call eax
        add esp, 4

        ret    
Post 25 Aug 2016, 11:16
View user's profile Send private message Reply with quote
MrFox



Joined: 17 Aug 2016
Posts: 52
Location: Russia
MrFox 25 Aug 2016, 11:32
Afterthought: maybe that's done this way to prevent local overheating EAX cpu cirquitry?
Post 25 Aug 2016, 11:32
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20462
Location: In your JS exploiting you and your system
revolution 26 Aug 2016, 07:51
Don't worry about "overheating" any particular part of the CPU. Use whatever registers you want.

And besides, modern CPUs use register renaming so such a thing isn't possible even if you tried to do it deliberately.
Post 26 Aug 2016, 07:51
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2, 3  Next

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.