Page 1 of 1

Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 5:49 am
by Fulgurance
Hello, i'm programming actually UEFI Bootloader. He work fine. I have followed this link to understand how the EFI protocol work: https://johv.dk/blog/bare-metal-assembly-tutorial.html

But i don't understand something about EFI_TABLE_HEADER size (into EFI_SYSTEM_TABLE structure.
This link say EFI_TABLE_HEADER (hdr) is 12 bytes and this is specified into p.p0 of UEFI specification.
But when i have looked this into UEFI specification, this isn't this size: you have 1 x UINT64 and 4 x UINT32 = 8 + ( 4*4) = 24 bytes ... I think i don't understand something

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 8:39 am
by iansjack
I make it 24 bytes for the header then 5 * 8 bytes for the remaining fields before *ConOut (allowing for each field being 8 byte aligned). So the offset 64 is correct, but the calculation is hokum.

Almost all tutorials contain errors - if you see a discrepancy between the documentation and a tutorial, always believe the former.

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 9:06 am
by Fulgurance
I have thinking it's an error, but his code works ...

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 9:54 am
by iansjack
Did you read what I said? :(

The calculation is rubbish, but it just happens to come up with the right answer. It's important to understand what you are doing rather than blindly following a tutorial.

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 10:08 am
by Fulgurance
Oh yes sorry, i'm french and sometimes if i read so fast, i miss some informations :|

I'm agree with you :)

If you can help me, i search to call function to change console text color, but i haven't any success...
Look:

Code: Select all

format pe64 efi
entry Main

section '.text' code readable executable

Main:

mov rcx,[rdx+64]
mov rax,[rcx+40]
mov rdx,0010000b+0x0
sub rsp,32
call rax
add rsp,32

mov rcx,[rdx+64]
mov rax,[rcx+8]
mov rdx,SystemMessage
sub rsp,32
call rax
add rsp,32

mov rcx,[rdx+64]
mov rax,[rcx+40]
mov rdx,1111000b+0x0
sub rsp,32
call rax
add rsp,32

mov rcx,[rdx+64]
mov rax,[rcx+8]
mov rdx,Message
sub rsp,32
call rax
add rsp,32


jmp $

section '.data' data readable writable

SystemMessage du '* EFI Boot: '
Message du 'Test OK',0xD,0xA,0x0

I know my argument are bad, but i don't understand what argument i need to pass

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 3:20 pm
by zaval
the header size is 24 bytes and not 12, every field needs NOT to be 8-byte aligned, only 8-byte ones do. the tutorial has some error statements, that don't help at least to say.

First, you need to learn alignment rules. they are tricky but 100% deterministic, there is no uncertainty, cannot be.
Second. for the sake of God, write for UEFI in C. it was made for this exactly way. you will waste a lot of time fooling around assembly there. later on, when having loaded your kernel, you can happily do as much assembly as you want.

Re: Question about EFI_TABLE_HEADER

Posted: Tue Apr 07, 2020 10:37 pm
by nullplan
zaval wrote:First, you need to learn alignment rules. they are tricky but 100% deterministic, there is no uncertainty, cannot be.
Not really all that hard.
  • Elementary types: Alignment == Size
  • Arrays: Alignment == Alignment of base type
  • Structs/Unions: Alignment == Largest alignment of the members
With only two caveats, really:
  1. 64-bit integers usually have alignment 8 on 64-bit architectures, but alignment 4 on 32-bit architectures. There, they behave like a struct of two 32-bit integers.
  2. Long double is weird. On x86, the usable size of long double is 10 bytes, which really doesn't fit most schemes, so in 32-bit mode it is typically given a size of 12 bytes,with an alignment of 4, but in 64-bit mode it has a size of 16 bytes and an alignment of 16 bytes.
Oh, and nonstandard types (e.g. vector types) are weird and usually, you don't need to care.

Re: Question about EFI_TABLE_HEADER

Posted: Wed Apr 08, 2020 4:30 am
by Fulgurance
zaval wrote:the header size is 24 bytes and not 12, every field needs NOT to be 8-byte aligned, only 8-byte ones do. the tutorial has some error statements, that don't help at least to say.
Okay, thanks you. But the problem isn't with fonction calling to show text, this part work. It's just for changing color. It's difficult to found good tutorial yes. Many times, i need to understand alone to programming intoassembly, many people say some bad explanations.
zaval wrote:First, you need to learn alignment rules. they are tricky but 100% deterministic, there is no uncertainty, cannot be
Yes it's true. I understand the purpose of alignement, but i have problem how alignement works. And where do you have found into UEFI documentation data need to be aligned ? I don't remember i have read that.
zaval wrote:Second. for the sake of God, write for UEFI in C. it was made for this exactly way. you will waste a lot of time fooling around assembly there. later on, when having loaded your kernel, you can happily do as much assembly as you want.
I know UEFI is writed for C, but in one word: NO. I have time and i would like to code with assembly sorry :) (i detest to program with high level language)
nullplan wrote:
zaval wrote:First, you need to learn alignment rules. they are tricky but 100% deterministic, there is no uncertainty, cannot be.
Not really all that hard.
  • Elementary types: Alignment == Size
  • Arrays: Alignment == Alignment of base type
  • Structs/Unions: Alignment == Largest alignment of the members
With only two caveats, really:
  1. 64-bit integers usually have alignment 8 on 64-bit architectures, but alignment 4 on 32-bit architectures. There, they behave like a struct of two 32-bit integers.
  2. Long double is weird. On x86, the usable size of long double is 10 bytes, which really doesn't fit most schemes, so in 32-bit mode it is typically given a size of 12 bytes,with an alignment of 4, but in 64-bit mode it has a size of 16 bytes and an alignment of 16 bytes.
Oh, and nonstandard types (e.g. vector types) are weird and usually, you don't need to care.
I programming only for Intel 64 bits processor at the moment.

Re: Question about EFI_TABLE_HEADER

Posted: Wed Apr 08, 2020 4:36 am
by iansjack
Fulgurance wrote:I know UEFI is writed for C, but in one word: NO. I have time and i would like to code with assembly sorry :) (i detest to program with high level language)
Obviously it's entirely your choice, but if you insist on not taking advantage of the benefits of high-level languages then you should be prepared to do most of the work for yourself. Most example code is in C and it is the language that the majority of programmers in this field use. So, really, it's not entirely fair to ask for help to solve difficulties that are of your own making.

Re: Question about EFI_TABLE_HEADER

Posted: Wed Apr 08, 2020 2:25 pm
by zaval
With only two caveats, really:
64-bit integers usually have alignment 8 on 64-bit architectures, but alignment 4 on 32-bit architectures. There, they behave like a struct of two 32-bit integers.
just a clear proof it's tricky. because you are wrong here. ironically, just a few days ago, I stumbled upon this question, on the example of one UEFI structure and was wondering - really, would a 64 bit field in a structure require 8-byte alignment on a 32 bit architecture? after all, the native word is 32 bit and that field would be accessed as 2 32 bit words. the structure in question:

Code: Select all

typedef	struct _EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE{
	UINT32					MaxMode;
	UINT32					Mode;
	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION	*Info;
	UINTN					SizeOfInfo;
	EFI_PHYSICAL_ADDRESS			FrameBufferBase;
	UINTN					FrameBufferSize;
}EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE, *PEFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;
this is why I said it's tricky. this struct is properly layed out for 64 bit arch and at the first glance, looks such on 32 bit. but, EFI_PHYSICAL_ADDRESS type is always 64 bit and despite it still resides 8-byte aligned if the whole structure is put 8-byte aligned, it won't be if it's an array of these structures. :) because UINTN behind it resolves into UINT32 on 32 bits, and, as a result, the second element of the array gets its FramebufferBase resided only 4-byte aligned, even if the whole array has been put 8-byte aligned. Now yet imagine nested structures, what if struct A has only 32 bit words, but also has structure B, holding a 64 bit word. the structure A needs thus be 8-byte multiple and aligned, but looking at it, one may easily overlook this. this all is not a concern for a C coder, since compilers take care of this burden, but our OP wants be all assembly. hehe. :mrgreen: back to the above structure. I went and checked it by compiling a simple program, since I am not a guru of the C standard subtleties. guess what? the struture got its 4-byte trailing padding. its size is 32 byte instead of 28. then I added the padding manually and got the same size and field offsets. :)


Yes it's true. I understand the purpose of alignement, but i have problem how alignement works. And where do you have found into UEFI documentation data need to be aligned ? I don't remember i have read that.
In the UEFI data structures, there is a lot of places, where dudes, devising them, introduced misaligned layout, see the example above. this especially important for you, - one, that wants to program in assembly, since you need to calculate offsets to the fields manually. unlike C programmers. Got it? if the structure were layed out without the need for the compiler to add padding, it would be easier - you would look at it and straight calculate offsets adding sizes of the previous elements. now, you need to thouroughly analyze and see where the compiler has put padding and take that into account. let me show you one the most f&cked up structures in the UEFI regarding this, yet it's a very used one by OS loaders:

Code: Select all

struct _EFI_LOADED_IMAGE_PROTOCOL{
	UINT32				Revision;
	EFI_HANDLE			ParentHandle;
	EFI_SYSTEM_TABLE		*SystemTable;

	/* Source location of the image */
	EFI_HANDLE			DeviceHandle;
	EFI_DEVICE_PATH_PROTOCOL	*FilePath;
	VOID				*Reserved;

	/* Image’s load options */
	UINT32				LoadOptionsSize;
	VOID				*LoadOptions;

	/* Location where image was loaded */
	VOID				*ImageBase;
	UINT64				ImageSize;
	EFI_MEMORY_TYPE			ImageCodeType;
	EFI_MEMORY_TYPE			ImageDataType;
	EFI_IMAGE_UNLOAD		Unload;
};
this structure is so wrecked, that it's hard to make it worse. it needs two paddings for 32 bits and two paddings for 64 bits all in different places! :D now imagine, writing in assembly, you would need to get the offsets right, by yourself! here, the structure with the paddings added explicitly:

Code: Select all

struct _EFI_LOADED_IMAGE_PROTOCOL{
	UINT32				Revision;
#if TARGET_BITNESS == 0x40
	UINT32				_Pad_64_0;
#endif
	EFI_HANDLE			ParentHandle;
	EFI_SYSTEM_TABLE		*SystemTable;

	/* Source location of the image */
	EFI_HANDLE			DeviceHandle;
	EFI_DEVICE_PATH_PROTOCOL	*FilePath;
	VOID				*Reserved;

	/* Image’s load options */
	UINT32				LoadOptionsSize;
#if TARGET_BITNESS == 0x40
	UINT32				_Pad_64_1;
#endif
	VOID				*LoadOptions;

	/* Location where image was loaded */
	VOID				*ImageBase;
#if TARGET_BITNESS == 0x20
	UINT32				_Pad_32_0;
#endif
	UINT64				ImageSize;
	EFI_MEMORY_TYPE			ImageCodeType;
	EFI_MEMORY_TYPE			ImageDataType;
	EFI_IMAGE_UNLOAD		Unload;

#if TARGET_BITNESS == 0x20
	UINT32				_Pad_32_1;
#endif
};
I mean, you need to take into account every possibility, - aligned inside, aligned being in the array, plus, EFI_MEMORY_TYPE is enumeration, :D it's 4 byte always. EFI_IMAGE_UNLOAD is a function pointer, whereas staring at it, you may not guess this instantly.

PS. Funny, they were greedy to make Revision and LoadOptionSize UINTN, that would instantly eliminate both misalignment on 64 bits, but made ImageSize always 64 bit, what introduced 2 misalignments on 32 bits. did they think that one day a UEFI executable won't fit 4GB? well, maybe if made as the modern "web technology", then it's not that infeasible. after all, Chrome now eats up 100MB of RAM for every web page, even containing just a moderate amount of text and a few of tiny icons.

Re: Question about EFI_TABLE_HEADER

Posted: Wed Apr 08, 2020 11:09 pm
by nullplan
zaval wrote:just a clear proof it's tricky. because you are wrong here. ironically, just a few days ago, I stumbled upon this question, on the example of one UEFI structure and was wondering - really, would a 64 bit field in a structure require 8-byte alignment on a 32 bit architecture? after all, the native word is 32 bit and that field would be accessed as 2 32 bit words.
OK, obviously I should have included a statement to the effect of: 64-bit integers on 32-bit architectures can get tricky, so look it up in the relevant ABI. The only 32-bit architecture I normally work with is PPC32, and there my statement is correct.

Also of note: Double precision floating point numbers generally have alignment 8 even on 32-bit architectures. And some PPC implementations will trap when accessing misaligned floating point numbers, even if they ordinarily would not when accessing integers.
zaval wrote:this all is not a concern for a C coder, since compilers take care of this burden, but our OP wants be all assembly. hehe.
Yeah, assembly is difficult. Specifically, interfacing assembly and C requires knowledge of the ABI so you don't make mistakes. At the end of the day, the compiler does a hard job, and unlike people it does not get tired and makes mistakes due to fatigue.

I went through a phase where I was writing Win32 GUI applications in assembly. But that was totally pointless, since that was mostly about calling DLL functions, so the programs were always just push-push-push-push-call. And with Win32's tendency to long parameter lists, messing up and pushing the wrong number of things, or pushing things in the wrong order, was a constant threat. I gave up on it after spending some hours debugging a crash, finally asking for help, and having it revealed that I called a function wrong. Which a compiler would have told me in less than one second.

Re: Question about EFI_TABLE_HEADER

Posted: Thu Apr 09, 2020 4:22 am
by bzt
Fulgurance wrote:I know UEFI is writed for C, but in one word: NO. I have time and i would like to code with assembly sorry :) (i detest to program with high level language)
In this case I'd recommend the following sources:
  • Uefi.inc - an UEFI ABI wrapper. Provides a "uefi_call_wrapper" macro for fasm (works just like in GNUEFI), it's a bit easier to use and makes your code more readable
  • x86asm.net on UEFI - great source with lots of explanation
Both links use fasm, and mainly focus on 64 bit. Our wiki page has some usage examples as well (GOP, BLOCK_IO etc.) in Assembly, but unfortunately not very well commented yet. You can find the source code for the aforementioned macro on the wiki page. It makes heavy use of fasm's macro capability (counting arguments, automatically align them properly etc.).

Cheers,
bzt