]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/boot/efi/pxe.c
kernel: implement portable userinit() and simplify process creation
[plan9front.git] / sys / src / boot / efi / pxe.c
index 87d7a7e2b761c174aabda00250947dbf0486227d..c7953b5c35dc23c016132d9db7c1920a25aefdd8 100644 (file)
@@ -118,8 +118,9 @@ EFI_GUID EFI_PXE_BASE_CODE_PROTOCOL_GUID = {
 static
 EFI_PXE_BASE_CODE_PROTOCOL *pxe;
 
-static
-EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp;
+static uchar mymac[6];
+static uchar myip[16];
+static uchar serverip[16];
 
 typedef struct Tftp Tftp;
 struct Tftp
@@ -306,28 +307,113 @@ tftpopen(Tftp *t, char *path)
 static void*
 pxeopen(char *name)
 {
-       static Tftp t[1];
+       static uchar buf[sizeof(Tftp)+8];
+       Tftp *t = (Tftp*)((uintptr)(buf+7)&~7);
 
        memset(t, 0, sizeof(Tftp));
-
-       memmove(&t->dip, dhcp->BootpSiAddr, 4);
-       memmove(&t->sip, pxe->Mode->StationIp, 4);
-
+       memmove(&t->sip, myip, sizeof(myip));
+       memmove(&t->dip, serverip, sizeof(serverip));
        if(tftpopen(t, name))
                return nil;
        return t;
 }
 
+static int
+parseipv6(uchar to[16], char *from)
+{
+       int i, dig, elipsis;
+       char *p;
+
+       elipsis = 0;
+       memset(to, 0, 16);
+       for(i = 0; i < 16; i += 2){
+               dig = 0;
+               for(p = from;; p++){
+                       if(*p >= '0' && *p <= '9')
+                               dig = (dig << 4) | (*p - '0');
+                       else if(*p >= 'a' && *p <= 'f')
+                               dig = (dig << 4) | (*p - 'a'+10);
+                       else if(*p >= 'A' && *p <= 'F')
+                               dig = (dig << 4) | (*p - 'A'+10);
+                       else
+                               break;
+                       if(dig > 0xFFFF)
+                               return -1;
+               }
+               to[i]   = dig>>8;
+               to[i+1] = dig;
+               if(*p == ':'){
+                       if(*++p == ':'){        /* :: is elided zero short(s) */
+                               if (elipsis)
+                                       return -1;      /* second :: */
+                               elipsis = i+2;
+                               p++;
+                       }
+               } else if (p == from)
+                       break;
+               from = p;               
+       }
+       if(i < 16){
+               memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
+               memset(&to[elipsis], 0, 16-i);
+       }
+       return 0;
+}
+
+static void
+parsedhcp(EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp)
+{
+       uchar *p, *e;
+       char *x;
+       int opt;
+       int len;
+
+       memset(mymac, 0, sizeof(mymac));
+       memset(serverip, 0, sizeof(serverip));
+
+       /* DHCPv4 */
+       if(pxe->Mode->UsingIpv6 == 0){
+               memmove(mymac, dhcp->BootpHwAddr, 6);
+               memmove(serverip, dhcp->BootpSiAddr, 4);
+               return;
+       }
+
+       /* DHCPv6 */
+       e = (uchar*)dhcp + sizeof(*dhcp);
+       p = (uchar*)dhcp + 4;
+       while(p+4 <= e){
+               opt = p[0]<<8 | p[1];
+               len = p[2]<<8 | p[3];
+               p += 4;
+               if(p + len > e)
+                       break;
+               switch(opt){
+               case 1: /* Client DUID */
+                       memmove(mymac, p+len-6, 6);
+                       break;
+               case 59: /* Boot File URL */
+                       for(x = (char*)p; x < (char*)p+len; x++){
+                               if(*x == '['){
+                                       parseipv6(serverip, x+1);
+                                       break;
+                               }
+                       }
+                       break;
+               }
+               p += len;
+       }
+}
+
 int
 pxeinit(void **pf)
 {
+       EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp;
        EFI_PXE_BASE_CODE_MODE *mode;
        EFI_HANDLE *Handles;
        UINTN Count;
        int i;
 
        pxe = nil;
-       dhcp = nil;
        Count = 0;
        Handles = nil;
        if(eficall(ST->BootServices->LocateHandleBuffer,
@@ -340,7 +426,7 @@ pxeinit(void **pf)
                        Handles[i], &EFI_PXE_BASE_CODE_PROTOCOL_GUID, &pxe))
                        continue;
                mode = pxe->Mode;
-               if(mode == nil || mode->UsingIpv6 || mode->Started == 0)
+               if(mode == nil || mode->Started == 0)
                        continue;
                if(mode->DhcpAckReceived){
                        dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)mode->DhcpAck;
@@ -354,19 +440,20 @@ pxeinit(void **pf)
        return -1;
 
 Found:
+       parsedhcp(dhcp);
+       memmove(myip, mode->StationIp, 16);
+
        open = pxeopen;
        read = pxeread;
        close = pxeclose;
 
        if(pf != nil){
                char ini[24];
-               uchar *mac;
 
-               mac = dhcp->BootpHwAddr;
                memmove(ini, "/cfg/pxe/", 9);
                for(i=0; i<6; i++){
-                       ini[9+i*2+0] = hex[mac[i] >> 4];
-                       ini[9+i*2+1] = hex[mac[i] & 0xF];
+                       ini[9+i*2+0] = hex[mymac[i] >> 4];
+                       ini[9+i*2+1] = hex[mymac[i] & 0xF];
                }
                ini[9+12] = '\0';
                if((*pf = pxeopen(ini)) == nil)