OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 16, 2024 5:46 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 13 posts ] 
Author Message
 Post subject: UEFI PXE
PostPosted: Mon Mar 25, 2019 8:20 am 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
I'm a bit stuck as to how a UEFI OS loader is expected to consume PXE (using EDK2). My project currently boots from a FAT volume but I would like to add PXE support and load the OS files via TFTP.

When the loader is booted via PXE the EFI_PXE_BASE_CODE_PROTOCOL Mode->Started is true and I am able to obtain the StationIp. From here I need to obtain an option from DHCP that the OS uses to locate files and more importantly use TFTP to start pulling files off the "next-server". UEFI spec says the protocol supports the EFI_LOAD_FILE_PROTOCOL but implies this is meant for firmware developers, not OS loaders. Could someone give a push in the right direction? How is an OS loader expected to pull files off the PXE server?

I'm currently determining how the bootloader was loaded by seeing if PXE was started and if it wasn't assuming we booted from a disk volume, then searching each volume for a file in my OS. This is a bit crude and ideally I'd like to determine where the bootloader was loaded from (i.e. PXE or Volume Handle XXXX). A push in the right direction would be appreciated. :D

(the last time I posted on this forum I was told I had to post to a "license agreement thread" which is now locked, for what it's worth: I have taken notice of the CC0 license and its intended effects, and hereby declare that all my past and future contributions to the OSDev.org Wiki are licensed under the effect of the CC0.)


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Mon Mar 25, 2019 10:39 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
Hi,

ComradeSlice wrote:
How is an OS loader expected to pull files off the PXE server?
Using tftp commands via PxeBc->Mtftp. Check out this source:
https://github.com/tianocore/edk2/blob/master/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c#L451 - gets the filename and file size from a DHCP reply packet
https://github.com/tianocore/edk2/blob/master/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c#L1117 - reads a file from a tftp server into a memory buffer

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Mon Mar 25, 2019 4:48 pm 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
Thanks for responding. Those functions seem to rely on private structures. To clarify, I do know how to initiate TFTP but I am unsure how an OS loader specifically is supposed to do it. Documentation refers a lot to drivers and firmware developers. By the time firmware has passed control to the EFI image, DHCP has already been initiated, next-server has been read (if applicable), and a TFTP connection has been opened to the PXE server.

The PxeReply in EFI_PXE_BASE_CODE_MODE may be sufficient for getting the boot server IP address, but is this what an OS loader is meant to be doing?

What is the recommended way of determining *where* the EFI image was actually loaded from? When I boot from a disk volume (my EFI image supports booting from disk right now). The EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL always returns an ACPI device path for my EFI image. I need a push in the right direction for figuring out the best way to determine where the boot image was loaded from. Currently my logic is: if PXE is not started we must have booted from a volume. Then I search all volumes for certain files my OS contains. It would be ideal to simply know which volume the EFI image is located on.

For example:

Code:
UINT32 BootMode = GetBootSource(ImageHandle);
switch(BootMode)
{
    case VOLUME:
        Print(L"Booted from disk %u volume %u.\n", foo, bar);
        break;
    case PXE:
        Print(L"Booted from PXE.\n");
        break;
}


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Mon Mar 25, 2019 5:22 pm 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
I found the "next-server" address in EFI_PXE_BASE_CODE_MODE's DHCP Ack structure. This is required to call Mftp() but this does not feel right... What if there isn't a "next-server"? The firmware already knows where it got the NBP from, whether it was next-server or the DHCP server itself.


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Thu Mar 28, 2019 2:49 pm 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
ComradeSlice wrote:
Thanks for responding. Those functions seem to rely on private structures.
And? That's just a bunch of local variables grouped together in a struct defined here. You don't need to use that struct, you can pass the arguments to mtftp from other variables as well.

Quote:
To clarify, I do know how to initiate TFTP but I am unsure how an OS loader specifically is supposed to do it.
The example I linked does exatly that. It is an official OS loader example which uses TFTP to load the boot image.

Quote:
Documentation refers a lot to drivers and firmware developers. By the time firmware has passed control to the EFI image, DHCP has already been initiated, next-server has been read (if applicable), and a TFTP connection has been opened to the PXE server.
That should not concern you. You get the DHCP packet with an UEFI call, it really doesn't matter if it's queried right on the spot or the most recent packet served from a cache.
And there's no such thing, "opening a TFTP connection". TFTP uses state-less datagramms, there's no three-way-handshake or any TCP session involved, therefore you cannot "open" it.

Quote:
The PxeReply in EFI_PXE_BASE_CODE_MODE may be sufficient for getting the boot server IP address, but is this what an OS loader is meant to be doing?
Take a closer look on the example I've linked. PxeBcDhcp4BootInfo() function get's the last reply packet and copies the server's IP address out from that into the Private struct (line 494). It checks three different locations: boot server list, server address in header, option list entry 54 in order.

Quote:
What is the recommended way of determining *where* the EFI image was actually loaded from? When I boot from a disk volume (my EFI image supports booting from disk right now). The EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL always returns an ACPI device path for my EFI image. I need a push in the right direction for figuring out the best way to determine where the boot image was loaded from. Currently my logic is: if PXE is not started we must have booted from a volume. Then I search all volumes for certain files my OS contains. It would be ideal to simply know which volume the EFI image is located on.
I don't follow you here. You're implementing the OS loader, it's totally up to you, so you surely know. If you mean in the loaded EFI image, then the asnwer is again it's up to you how you pass variables to your kernel from your loader. In my loader I set up paging (because I'm using 64 bit kernel and long mode requires paging enabled), so I obviously map an info struct at an address where my kernel can find the info, and I don't care what the physical address is. If you don't use paging then you should figure out a way to pass a pointer as an argument when you transfer control to the kernel.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Thu Mar 28, 2019 5:27 pm 
Offline
Member
Member
User avatar

Joined: Sun Feb 20, 2011 2:01 pm
Posts: 110
ComradeSlice wrote:
Thanks for responding. Those functions seem to rely on private structures. To clarify, I do know how to initiate TFTP but I am unsure how an OS loader specifically is supposed to do it. Documentation refers a lot to drivers and firmware developers. By the time firmware has passed control to the EFI image, DHCP has already been initiated, next-server has been read (if applicable), and a TFTP connection has been opened to the PXE server.

The PxeReply in EFI_PXE_BASE_CODE_MODE may be sufficient for getting the boot server IP address, but is this what an OS loader is meant to be doing?

What is the recommended way of determining *where* the EFI image was actually loaded from? When I boot from a disk volume (my EFI image supports booting from disk right now). The EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL always returns an ACPI device path for my EFI image. I need a push in the right direction for figuring out the best way to determine where the boot image was loaded from. Currently my logic is: if PXE is not started we must have booted from a volume. Then I search all volumes for certain files my OS contains. It would be ideal to simply know which volume the EFI image is located on.

For example:

Code:
UINT32 BootMode = GetBootSource(ImageHandle);
switch(BootMode)
{
    case VOLUME:
        Print(L"Booted from disk %u volume %u.\n", foo, bar);
        break;
    case PXE:
        Print(L"Booted from PXE.\n");
        break;
}

The EFI loaded image protocol provides a DeviceHandle for the boot device. It's guaranteed to support either SIMPLE_FILE_SYSTEM or the other one (can't remember its name as I don't support it). That's just for the EFI System Partition.

My methodology so far is that the kernel resides on the system partition. I haven't had to go beyond that yet! However, a configuration file is an easy way to decide boot volume.

_________________
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Thu Mar 28, 2019 6:59 pm 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
I like your signature. I use EDK2 on Windows :wink:

What you said got me thinking and I think I found a good solution. The EFI Loaded Image Device Path Protocol seems to be sufficient to tell how the OS loader was booted.

Booting from disk gives a path like:
PciRoot(0x0)/Pci(0x1,0x1)/Ata(Primary,Master,0x0)/HD(1,GPT,4D3D26E9-77F8-41A5-89BB-754DFA7228F0,0x800,0x100000)/\.\EFI\BOOT\BOOTX64.EFI

Booting from PXE gives a path like:
PciRoot(0x0)/Pci(0x19,0x0)/MAC(112233445566,0x0)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)

I'm pretty sure UEFI can open devices via device paths. I'm going to see if I can use the disk device path to open the volume directly without having to scan for files. As far as PXE goes, I was hoping there would be a struct somewhere with a member that clearly means "this is the IP address we downloaded the NBP from" but it looks like I have to redo the work the firmware itself has already done to determine what the TFTP server is.


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Fri Mar 29, 2019 5:05 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
Hi,

ComradeSlice wrote:
I was hoping there would be a struct somewhere with a member that clearly means "this is the IP address we downloaded the NBP from"
From your EFI Loaded Image Device Path example:
ComradeSlice wrote:
Booting from PXE gives a path like:
PciRoot(0x0)/Pci(0x19,0x0)/MAC(112233445566,0x0)/IPv4(0.0.0.0,0x0,DHCP,0.0.0.0,0.0.0.0,0.0.0.0)
There's your server IP address used to download the NBP.

ComradeSlice wrote:
but it looks like I have to redo the work the firmware itself has already done to determine what the TFTP server is.
To repeat myself:
bzt wrote:
Take a closer look on the example I've linked. PxeBcDhcp4BootInfo() function get's the last reply packet and copies the server's IP address out from that into the Private struct (line 494). It checks three different locations: boot server list, server address in header, option list entry 54 in order.


Don't get me wrong, but if you can't find an IPv4 address in a Device Path and can't read an official C source, are you sure this is the propriate hobby for you? No critism, just a friendly advice.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Fri Mar 29, 2019 10:42 am 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
I did not put 0.0.0.0 in there to hide my IP address. That's how it was displayed in the device path.


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Fri Mar 29, 2019 6:53 pm 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
The EFI_PXE_BASE_CODE_PROTOCOL structure has a member "Mode" of type EFI_PXE_BASE_CODE_MODE, which is a structure that has a member "DhcpAck" of type EFI_PXE_BASE_CODE_PACKET, which is a union of a raw UINT8 buffer, EFI_PXE_BASE_CODE_DHCPV4_PACKET, and EFI_PXE_BASE_CODE_DHCPV6_PACKET structures. In the case of IPv4 when "next-server" is used the TFTP server address will be in the "BootpSiAddr" member. If not, the "DhcpOptions" member has to be parsed for option 54. I set up my DHCP server with and without next-server and I was able to determine the correct address for the TFTP server in both cases.

Code:
UINT8* TftpServer;

EFI_PXE_BASE_CODE_DHCPV4_PACKET* DhcpAck = &Mode->DhcpAck.Dhcpv4;
UINT8* NextAddr = DhcpAck->BootpSiAddr;
if (NextAddr[0] != 0 && NextAddr[1] != 0 && NextAddr[2] != 0 && NextAddr[3] != 0)
{
   TftpServer = NextAddr;
}
else
{
   for (UINT32 i = 0; i < 56; i++)
   {
      if (DhcpAck->DhcpOptions[i] != 54)
      {
         continue;
      }

      UINT8* Option54 = DhcpAck->DhcpOptions + i;
      // Length should be four bytes, as it's an IPv4 address.
      if (Option54[1] == 4)
      {
         TftpServer = Option54 + 2;
      }
   }
}


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Sat Mar 30, 2019 4:32 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
ComradeSlice wrote:
I was able to determine the correct address for the TFTP server in both cases.
Well done! Now you just have to pass that value to your kernel, and you'll be able to determine the place of origin of your loaded image.
ComradSlice wrote:
I did not put 0.0.0.0 in there to hide my IP address.
I have to say if you haven't replaced the IP address and the MAC address to 112233445566 in the example then you have a buggy EFI firmware which is unlikely. The returned Device Path should be correct. Are you sure you queried the path from an initialized instance? Looks like an uninitialized path to me.
Btw as a last resort you could create a Device Path manually (not relying EFI) from the detected TFTP server address, after all it's nothing more than a bunch of bytes in memory. Or patch the IP address in the Device Path with the correct value using CopyMem at the right offset. (But again, this should not needed, EFI should return a correct Device Path in the first place).

About your code, wouldn't be my first choice how to write it, but should work as far as I can tell without testing. I suggest to define DhcpOptions as EFI_DHCP4_PACKET_OPTION, would make your code much more readable.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Mon Apr 01, 2019 7:57 am 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
I changed the MAC address but not the IP address. I thought maybe it was using MTFTP (and thus wouldn't be using a unicast address) but I captured the packets and this is not the case.

Quote:
Mar 29 23:58:28 nydc1 tftpd[46701]: Filename: 'MyLoaderX64.efi'
Mar 29 23:58:28 nydc1 tftpd[46701]: Mode: 'octet'
Mar 29 23:58:28 nydc1 tftpd[46701]: 172.16.1.143: read request for //MyLoaderX64.efi: success


Attachments:
DHCP TFTP.png
DHCP TFTP.png [ 34.14 KiB | Viewed 2065 times ]
Top
 Profile  
 
 Post subject: Re: UEFI PXE
PostPosted: Mon Apr 01, 2019 8:37 am 
Offline

Joined: Sun Apr 10, 2011 8:29 am
Posts: 8
Here is the TFTP device path hex dump. There is no address and it's not using MTFTP (see previous post attachment).


Attachments:
TFTP Device Path.jpg
TFTP Device Path.jpg [ 119.8 KiB | Viewed 2056 times ]
Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 13 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], DotBot [Bot], Google [Bot] and 752 guests


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 post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group