PXE - TFTP timeout

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

PXE - TFTP timeout

Post by shikhin »

Hi,

I have been recently developing on my PXE Stage 1 boot loader, and being the inexperienced man I am, I have encountered yet another problem. :)

I have managed to get as far as writing the complete OpenFile, CloseFile and ReadFile (from a offset) wrappers (which would then file a ReadCompFile function). These wrappers even work in QEMU, on which I can successfully load and execute the common BIOS Stage. However, recently, when I was testing the OpenFile wrapper on my PIII, I encountered a problem.

On further debugging, I found that PXE was leaving the error code 0x35 for me, which translated to "PXENV_STATUS_TFTP_READ_TIMEOUT" - as I found out after referring to the specifications. After a little googling, and hours spent on IRC, the two suggestions I was given were:
  • Try with a Linux distribution, and check whether your TFTP server is good enough. I did try that, and found out that my TFTP server is intact (which was already proven by the fact that iPXE was able to load my boot file).
  • Try disabling your firewall, such that nothing blocks the port. This one seemed valuable advice, but unfortunately, this didn't solve the problem, as I didn't have any firewall enabled for a start.
Here is my ugly looking code:

Code: Select all

%define PACKET_SIZE 512               ; Keep the size to the minimum and most supported 512.
%define UDP_PORT    (69 << 8)         ; The port is 69, or 69 << 8 in big endian.

PXENV_TFTP_OPEN:
    .Status       dw 0
    .SIP          dd 0
    .GIP          dd 0
    .Filename     times 128 db 0
    .Port         dw 0
    .PacketSize   dw 0

; Save PacketSize here, for future reference by code.
PacketSize:       dw 0

; Opens a file from the TFTP server, so that it can be read from.
; @si             Should point to the asciiz filename.
; @ecx            Should contain the length of the filename (< 128).
OpenFile:
    pushad
    
    ; Zero out the PXENV_TFTP_OPEN structure.
    push ecx                          ; Save the size of the filename.
    mov di, PXENV_TFTP_OPEN 
    mov ecx, 71                       ; Store 71-words.
    xor eax, eax                      ; We need to zero out the structure.

    rep stosw                         ; Clear out the entire structure.

    pop ecx                           ; Restore the size of the filename.
 
    ; Put some default values in there.
    mov word [PXENV_TFTP_OPEN.Port], UDP_PORT
    mov word [PXENV_TFTP_OPEN.PacketSize], PACKET_SIZE
    mov eax, [SIP]
    bswap eax                         ; PXE expects SIP to be in big endian format - BSWAP!
    mov [PXENV_TFTP_OPEN.SIP], eax 

    mov eax, [GIP]
    bswap eax
    mov [PXENV_TFTP_OPEN.GIP], eax

    ; Store the "filename" at PXENV_TFTP_OPEN.Filename
    mov di, PXENV_TFTP_OPEN.Filename
    rep movsb

    ; Store the address of the input buffer, and the opcode at BX.
    mov di, PXENV_TFTP_OPEN
    mov bx, TFTP_OPEN
    call UsePXEAPI

    or ax, [PXENV_TFTP_OPEN]
    test ax, ax
    jnz .Error                        ; Test if any error occured. If it did, abort boot!

    ; Store PacketSize for future reference.
    mov eax, [PXENV_TFTP_OPEN.PacketSize]
    mov [PacketSize], eax

.Return:
    popad
    ret

.Error:
    xor ax, ax
    mov si, PXEAPIError
    call AbortBoot
:mrgreen:

NOTE: I am using iPXE on my PIII, since the network card doesn't support PXE.

I've already tried printing out and verifying the entire PXENV_TFTP_OPEN structure. The structure, as I expected was correct. If anyone could be of any help, I'd be great.

Regards,
Shikhin 8)
http://shikhin.in/

Current status: Gandr.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: PXE - TFTP timeout

Post by Brendan »

Hi,

Code: Select all

%define PACKET_SIZE 512               ; Keep the size to the minimum and most supported 512.
%define UDP_PORT    (69 << 8)         ; The port is 69, or 69 << 8 in big endian.

PXENV_TFTP_OPEN:
    .Status       dw 0
    .SIP          dd 0
    .GIP          dd 0
    .Filename     times 128 db 0
    .Port         dw 0
    .PacketSize   dw 0

; Save PacketSize here, for future reference by code.
PacketSize:       dw 0

; Opens a file from the TFTP server, so that it can be read from.
; @si             Should point to the asciiz filename.
; @ecx            Should contain the length of the filename (< 128).
OpenFile:
    pushad
    
    ; Zero out the PXENV_TFTP_OPEN structure.
    push ecx                          ; Save the size of the filename.
    mov di, PXENV_TFTP_OPEN 
    mov ecx, 71                       ; Store 71-words.
    xor eax, eax                      ; We need to zero out the structure.

    rep stosw                         ; Clear out the entire structure.

    pop ecx                           ; Restore the size of the filename.
 
    ; Put some default values in there.
    mov word [PXENV_TFTP_OPEN.Port], UDP_PORT
    mov word [PXENV_TFTP_OPEN.PacketSize], PACKET_SIZE
You could probably avoid half of this by not zeroing out parts of the structure that get overwritten anyway (e.g. the file name field) and treating some of it as "never modified" (e.g. when the structure is defined you could do ".Port dw UDP_PORT" and avoid the need for "mov word [PXENV_TFTP_OPEN.Port], UDP_PORT").

I'd also be tempted to put a "CLD" somewhere in case the caller left it set. This might not be necessary though (e.g. if you use a rule like "all code always makes sure the direction flag is clear before doing a RET or CALL").

Code: Select all

    mov eax, [SIP]
    bswap eax                         ; PXE expects SIP to be in big endian format - BSWAP!
    mov [PXENV_TFTP_OPEN.SIP], eax 

    mov eax, [GIP]
    bswap eax
    mov [PXENV_TFTP_OPEN.GIP], eax
I'm not sure where you're getting SIP and GIP. You should be getting them from the DHCP server's "DHCPACK packet", which you get from the PXE API's "GET CACHED INFO" function. In that case, SIP and GIP would have been in big-endan format already (no need for bswap).

The rest of it looks fine to me (although I'd assume "call AbortBoot" at the end should be "jmp AbortBoot" - a CALL followed by nothing always looks like a bug to me).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: PXE - TFTP timeout

Post by shikhin »

Brendan wrote:Hi,

Code: Select all

snip - my code here.
You could probably avoid half of this by not zeroing out parts of the structure that get overwritten anyway (e.g. the file name field) and treating some of it as "never modified" (e.g. when the structure is defined you could do ".Port dw UDP_PORT" and avoid the need for "mov word [PXENV_TFTP_OPEN.Port], UDP_PORT").
Of course, perhaps I was being a bit too paranoid. I'll change that as soon as this works.
Brendan wrote:
I'd also be tempted to put a "CLD" somewhere in case the caller left it set. This might not be necessary though (e.g. if you use a rule like "all code always makes sure the direction flag is clear before doing a RET or CALL").
Fortunately, until now, my code follows the rule of: "making sure not to set the direction flag anytime", so that's no problem.
Brendan wrote:

Code: Select all

   
snip - my code here.
I'm not sure where you're getting SIP and GIP. You should be getting them from the DHCP server's "DHCPACK packet", which you get from the PXE API's "GET CACHED INFO" function. In that case, SIP and GIP would have been in big-endan format already (no need for bswap).

The rest of it looks fine to me (although I'd assume "call AbortBoot" at the end should be "jmp AbortBoot" - a CALL followed by nothing always looks like a bug to me).
Yes, I'm getting it from the GET_CACHED_INFO function. I fixed that bit (surprised that QEMU doesn't use SIP at all), and now I get a PXENV_STATUS_OUT_OF_RESOURCES error (no timeouts - no delays though). Anyone has any idea why that may be occurring.

Regards,
Shikhin :)
http://shikhin.in/

Current status: Gandr.
User avatar
Love4Boobies
Member
Member
Posts: 2111
Joined: Fri Mar 07, 2008 5:36 pm
Location: Bucharest, Romania

Re: PXE - TFTP timeout

Post by Love4Boobies »

Shikhin wrote:Fortunately, until now, my code follows the rule of: "making sure not to set the direction flag anytime", so that's no problem.
Not only is that a maintainability problem (you might later forget about it), but the firmware doesn't make any guarantees so be sure to at least initialize DF.
"Computers in the future may weigh no more than 1.5 tons.", Popular Mechanics (1949)
[ Project UDI ]
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: PXE - TFTP timeout

Post by shikhin »

Hi,
Love4Boobies wrote:
Shikhin wrote:Fortunately, until now, my code follows the rule of: "making sure not to set the direction flag anytime", so that's no problem.
Not only is that a maintainability problem (you might later forget about it), but the firmware doesn't make any guarantees so be sure to at least initialize DF.
Yeah, I guess I'll need to be careful for interrupt handlers too. Anyway, I do initialize DF.

On another note: I did fix that problem. Trying to run the code on various other machines led to the fact that it only failed on iPXE. I tried with older versions of iPXE (Etherboot) and it worked. Seems like a iPXE bug...

Regards,
Shikhin 8)
http://shikhin.in/

Current status: Gandr.
Post Reply