From 8f83a69d4c9fe4d506f6168b3b91a25495629bab Mon Sep 17 00:00:00 2001 From: kevans Date: Sat, 10 Feb 2018 04:56:07 +0000 Subject: [PATCH] MFC Loader Fixes 2017q2: r316437,r316577,r316578,r316585,r316590,r316612, r316625,r316628,r316654,r316682,r316704,r316771,r317092,r317097,r317099, r317652,r317785,r317886,r317887,r318142,r318320,r318356,r318678,r318754, r318982,r318986,r318987,r318988,r318989,r318990,r318991,r318992,r318993, r318994,r318999,r319083,r319084,r319085,r320011,r320234,r320288,r320304, r320467,r320482 r316437: Small cleanup to make i386/loader match efi/loader boot environment code r316577: loader: part.c cstyle cleanup r316578: loader: want mechanism to avoid RA with bcache r316585: loader: zfs reader should check all labels r316590: libstand/dosfs: cache FAT32 in 128 Kb blocks to save loader memory r316612: In r298230 the value of HEAP_MIN was changed from 3MB to 64MB. Correct a comment. r316625: Do not use -msoft-float with intention of disabling FP on aarch64 r316628: Silence GCC warning by initializing the local variable. r316654: loader: r316585 did miss userboot update r316682: loader: r316585 did miss sparc/ofw r316704: loader.efi: only fetch zfs pool guid for the actual boot device r316771: loader: Avoid possible overflow via environment variable r317092: loader: zfs reader vdev_probe should check for minimum device size r317097: loader: F_READ/F_WRITE should be checked against masked flag r317099: loader: uboot disk ioctl should call disk_ioctl r317652: loader.efi: ResetSystem does not use data with EFI_SUCCESS r317785: zfsboot: drvsize() may be unusable on some systems r317886: distinguish NFS versus TFTP boot by rootpath r317887: loader: network read rework r318142: libstand: NULL pointer dereference in rarp r318320: loader: add ip layer code into libstand r318356: libstand: increase nfs max read size to 16k r318678: Replacing iterating over rootpath by strsep(3). r318754: Pass -N directly to ld via -Wl rather than passing it to the compiler driver. r318982: Pass a "FREEBSD" user-class in PXE dhcp request r318986: add a comment on vendor index 19 and 20 to avoid confusion r318987: Support URI scheme for root-path in netbooting r318988: Always build tftpfs support along with nfs for pxeboot r318989: Always issue the pxe request r318990: Partially revert r314948 r318991: Document recent changes on pxeboot r318992: Capitalize DHCP r318993: Use the usual FreeBSD spelling for the DHCP user class r318994: Catch with the change in the user class r318999: Update the comments concerning net_parse_rootpath to reflect what it is now r319083: Followup on the user-class changes r319084: Small cleanup in dev_net.c r319085: use the same option list for dhcp discovery and request r320011: Add chain loader support for loader r320234: Make structure padding explicit in EFI_MEMORY_DESCRIPTOR r320288: Allow Clang's integrated assembler to assemble boot0 r320304: loader.efi: Disable smbios for arm r320467: loader: chain load relocate data declaration is bad r320482: As with arm64 mark the EFI PE header as allocated on arm. PR: 218473 --- lib/libstand/Makefile | 2 +- lib/libstand/arp.c | 54 +- lib/libstand/bootp.c | 147 ++--- lib/libstand/bootp.h | 12 +- lib/libstand/bootparam.c | 101 ++-- lib/libstand/dosfs.c | 210 +++---- lib/libstand/dosfs.h | 2 + lib/libstand/ether.c | 39 +- lib/libstand/globals.c | 1 - lib/libstand/ip.c | 422 ++++++++++++++ lib/libstand/net.c | 10 +- lib/libstand/net.h | 16 +- lib/libstand/netif.c | 57 +- lib/libstand/netif.h | 8 +- lib/libstand/nfs.c | 149 ++--- lib/libstand/rarp.c | 43 +- lib/libstand/rpc.c | 81 ++- lib/libstand/rpc.h | 2 +- lib/libstand/stand.h | 3 + lib/libstand/tftp.c | 84 +-- lib/libstand/udp.c | 158 ++---- share/mk/bsd.stand.mk | 5 +- sys/boot/common/bcache.c | 12 +- sys/boot/common/commands.c | 2 +- sys/boot/common/dev_net.c | 88 ++- sys/boot/common/disk.c | 7 +- sys/boot/common/md.c | 2 +- sys/boot/common/part.c | 44 +- sys/boot/efi/Makefile.inc | 6 +- sys/boot/efi/boot1/Makefile | 2 +- sys/boot/efi/boot1/boot1.c | 1 + sys/boot/efi/boot1/zfs_module.c | 9 + sys/boot/efi/fdt/Makefile | 4 +- sys/boot/efi/include/efidef.h | 1 + sys/boot/efi/libefi/Makefile | 2 +- sys/boot/efi/libefi/efinet.c | 89 +-- sys/boot/efi/libefi/efipart.c | 2 +- sys/boot/efi/libefi/time.c | 2 +- sys/boot/efi/loader/Makefile | 5 + sys/boot/efi/loader/arch/arm/start.S | 2 +- sys/boot/efi/loader/arch/arm64/Makefile.inc | 2 +- sys/boot/efi/loader/main.c | 142 ++++- sys/boot/forth/menu.rc | 15 + sys/boot/i386/boot0/Makefile | 3 - sys/boot/i386/common/drv.h | 2 +- sys/boot/i386/libi386/Makefile | 2 +- sys/boot/i386/libi386/bioscd.c | 2 +- sys/boot/i386/libi386/biosdisk.c | 2 +- sys/boot/i386/libi386/biosmem.c | 2 +- sys/boot/i386/libi386/libi386.h | 32 ++ sys/boot/i386/libi386/pxe.c | 548 +++++++------------ sys/boot/i386/libi386/pxe.h | 2 +- sys/boot/i386/libi386/relocater_tramp.S | 358 ++++++++++++ sys/boot/i386/loader/Makefile | 13 +- sys/boot/i386/loader/chain.c | 134 +++++ sys/boot/i386/loader/help.i386 | 9 + sys/boot/i386/loader/main.c | 13 +- sys/boot/i386/pxeldr/pxeboot.8 | 33 +- sys/boot/i386/zfsboot/zfsboot.c | 77 ++- sys/boot/mips/beri/boot2/Makefile | 2 +- sys/boot/mips/beri/loader/beri_disk_cfi.c | 1 + sys/boot/mips/beri/loader/beri_disk_sdcard.c | 1 + sys/boot/ofw/libofw/ofw_disk.c | 24 +- sys/boot/ofw/libofw/ofw_net.c | 47 +- sys/boot/ofw/libofw/openfirm.c | 47 ++ sys/boot/ofw/libofw/openfirm.h | 2 + sys/boot/powerpc/ps3/ps3cdrom.c | 1 + sys/boot/powerpc/ps3/ps3disk.c | 1 + sys/boot/sparc64/loader/main.c | 28 +- sys/boot/uboot/lib/disk.c | 6 + sys/boot/uboot/lib/net.c | 32 +- sys/boot/usb/storage/umass_loader.c | 1 + sys/boot/userboot/userboot/main.c | 11 + sys/boot/userboot/userboot/userboot_disk.c | 10 +- sys/boot/zfs/libzfs.h | 1 + sys/boot/zfs/zfsimpl.c | 231 ++++---- 76 files changed, 2460 insertions(+), 1263 deletions(-) create mode 100644 lib/libstand/ip.c create mode 100644 sys/boot/i386/libi386/relocater_tramp.S create mode 100644 sys/boot/i386/loader/chain.c diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile index ce40621c461..a35912b2f4e 100644 --- a/lib/libstand/Makefile +++ b/lib/libstand/Makefile @@ -141,7 +141,7 @@ SRCS+= closeall.c dev.c ioctl.c nullfs.c stat.c \ fstat.c close.c lseek.c open.c read.c write.c readdir.c # network routines -SRCS+= arp.c ether.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c +SRCS+= arp.c ether.c ip.c inet_ntoa.c in_cksum.c net.c udp.c netif.c rpc.c # network info services: SRCS+= bootp.c rarp.c bootparam.c diff --git a/lib/libstand/arp.c b/lib/libstand/arp.c index e6175325f31..61b5f1fc270 100644 --- a/lib/libstand/arp.c +++ b/lib/libstand/arp.c @@ -65,17 +65,16 @@ int arp_num = 1; /* Local forwards */ static ssize_t arpsend(struct iodesc *, void *, size_t); -static ssize_t arprecv(struct iodesc *, void *, size_t, time_t); +static ssize_t arprecv(struct iodesc *, void **, void **, time_t); /* Broadcast an ARP packet, asking who has addr on interface d */ u_char * -arpwhohas(d, addr) - struct iodesc *d; - struct in_addr addr; +arpwhohas(struct iodesc *d, struct in_addr addr) { int i; struct ether_arp *ah; struct arp_list *al; + void *pkt; struct { struct ether_header eh; struct { @@ -83,13 +82,6 @@ arpwhohas(d, addr) u_char pad[18]; /* 60 - sizeof(...) */ } data; } wbuf; - struct { - struct ether_header eh; - struct { - struct ether_arp arp; - u_char pad[24]; /* extra space */ - } data; - } rbuf; /* Try for cached answer first */ for (i = 0, al = arp_list; i < arp_num; ++i, ++al) @@ -122,20 +114,24 @@ arpwhohas(d, addr) /* Store ip address in cache (incomplete entry). */ al->addr = addr; + pkt = NULL; + ah = NULL; i = sendrecv(d, arpsend, &wbuf.data, sizeof(wbuf.data), - arprecv, &rbuf.data, sizeof(rbuf.data)); + arprecv, &pkt, (void **)&ah); if (i == -1) { panic("arp: no response for %s\n", inet_ntoa(addr)); } /* Store ethernet address in cache */ - ah = &rbuf.data.arp; #ifdef ARP_DEBUG if (debug) { + struct ether_header *eh; + + eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN); printf("arp: response from %s\n", - ether_sprintf(rbuf.eh.ether_shost)); + ether_sprintf(eh->ether_shost)); printf("arp: cacheing %s --> %s\n", inet_ntoa(addr), ether_sprintf(ah->arp_sha)); } @@ -143,14 +139,12 @@ arpwhohas(d, addr) MACPY(ah->arp_sha, al->ea); ++arp_num; + free(pkt); return (al->ea); } static ssize_t -arpsend(d, pkt, len) - struct iodesc *d; - void *pkt; - size_t len; +arpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef ARP_DEBUG @@ -166,28 +160,27 @@ arpsend(d, pkt, len) * else -1 (and errno == 0) */ static ssize_t -arprecv(d, pkt, len, tleft) - struct iodesc *d; - void *pkt; - size_t len; - time_t tleft; +arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; struct ether_arp *ah; u_int16_t etype; /* host order */ + void *ptr; #ifdef ARP_DEBUG if (debug) printf("arprecv: "); #endif - n = readether(d, pkt, len, tleft, &etype); + ptr = NULL; + n = readether(d, &ptr, (void **)&ah, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef ARP_DEBUG if (debug) printf("bad len=%d\n", n); #endif + free(ptr); return (-1); } @@ -196,12 +189,11 @@ arprecv(d, pkt, len, tleft) if (debug) printf("not arp type=%d\n", etype); #endif + free(ptr); return (-1); } /* Ethernet address now checked in readether() */ - - ah = (struct ether_arp *)pkt; if (ah->arp_hrd != htons(ARPHRD_ETHER) || ah->arp_pro != htons(ETHERTYPE_IP) || ah->arp_hln != sizeof(ah->arp_sha) || @@ -211,6 +203,7 @@ arprecv(d, pkt, len, tleft) if (debug) printf("bad hrd/pro/hln/pln\n"); #endif + free(ptr); return (-1); } @@ -220,6 +213,7 @@ arprecv(d, pkt, len, tleft) printf("is request\n"); #endif arp_reply(d, ah); + free(ptr); return (-1); } @@ -228,6 +222,7 @@ arprecv(d, pkt, len, tleft) if (debug) printf("not ARP reply\n"); #endif + free(ptr); return (-1); } @@ -239,6 +234,7 @@ arprecv(d, pkt, len, tleft) if (debug) printf("unwanted address\n"); #endif + free(ptr); return (-1); } /* We don't care who the reply was sent to. */ @@ -248,6 +244,8 @@ arprecv(d, pkt, len, tleft) if (debug) printf("got it\n"); #endif + *pkt = ptr; + *payload = ah; return (n); } @@ -256,9 +254,7 @@ arprecv(d, pkt, len, tleft) * Notes: Re-uses buffer. Pad to length = 46. */ void -arp_reply(d, pkt) - struct iodesc *d; - void *pkt; /* the request */ +arp_reply(struct iodesc *d, void *pkt) { struct ether_arp *arp = pkt; diff --git a/lib/libstand/bootp.c b/lib/libstand/bootp.c index 32dd937d8cb..9f08fafe2e8 100644 --- a/lib/libstand/bootp.c +++ b/lib/libstand/bootp.c @@ -38,6 +38,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include #include @@ -72,7 +73,7 @@ static char vm_cmu[4] = VM_CMU; /* Local forwards */ static ssize_t bootpsend(struct iodesc *, void *, size_t); -static ssize_t bootprecv(struct iodesc *, void *, size_t, time_t); +static ssize_t bootprecv(struct iodesc *, void **, void **, time_t); static int vend_rfc1048(u_char *, u_int); #ifdef BOOTP_VEND_CMU static void vend_cmu(u_char *); @@ -89,23 +90,50 @@ static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts); static char expected_dhcpmsgtype = -1, dhcp_ok; struct in_addr dhcp_serverip; #endif +struct bootp *bootp_response; +size_t bootp_response_size; + +static void +bootp_fill_request(unsigned char *bp_vend) +{ + /* + * We are booting from PXE, we want to send the string + * 'PXEClient' to the DHCP server so you have the option of + * only responding to PXE aware dhcp requests. + */ + bp_vend[0] = TAG_CLASSID; + bp_vend[1] = 9; + bcopy("PXEClient", &bp_vend[2], 9); + bp_vend[11] = TAG_USER_CLASS; + /* len of each user class + number of user class */ + bp_vend[12] = 8; + /* len of the first user class */ + bp_vend[13] = 7; + bcopy("FreeBSD", &bp_vend[14], 7); + bp_vend[21] = TAG_PARAM_REQ; + bp_vend[22] = 7; + bp_vend[23] = TAG_ROOTPATH; + bp_vend[24] = TAG_HOSTNAME; + bp_vend[25] = TAG_SWAPSERVER; + bp_vend[26] = TAG_GATEWAY; + bp_vend[27] = TAG_SUBNET_MASK; + bp_vend[28] = TAG_INTF_MTU; + bp_vend[29] = TAG_SERVERID; + bp_vend[30] = TAG_END; +} /* Fetch required bootp infomation */ void -bootp(sock, flag) - int sock; - int flag; +bootp(int sock) { + void *pkt; struct iodesc *d; struct bootp *bp; struct { u_char header[HEADER_SIZE]; struct bootp wbootp; } wbuf; - struct { - u_char header[HEADER_SIZE]; - struct bootp rbootp; - } rbuf; + struct bootp *rbootp; #ifdef BOOTP_DEBUG if (debug) @@ -137,29 +165,8 @@ bootp(sock, flag) bp->bp_vend[4] = TAG_DHCP_MSGTYPE; bp->bp_vend[5] = 1; bp->bp_vend[6] = DHCPDISCOVER; + bootp_fill_request(&bp->bp_vend[7]); - /* - * If we are booting from PXE, we want to send the string - * 'PXEClient' to the DHCP server so you have the option of - * only responding to PXE aware dhcp requests. - */ - if (flag & BOOTP_PXE) { - bp->bp_vend[7] = TAG_CLASSID; - bp->bp_vend[8] = 9; - bcopy("PXEClient", &bp->bp_vend[9], 9); - bp->bp_vend[18] = TAG_PARAM_REQ; - bp->bp_vend[19] = 8; - bp->bp_vend[20] = TAG_ROOTPATH; - bp->bp_vend[21] = TAG_TFTP_SERVER; - bp->bp_vend[22] = TAG_HOSTNAME; - bp->bp_vend[23] = TAG_SWAPSERVER; - bp->bp_vend[24] = TAG_GATEWAY; - bp->bp_vend[25] = TAG_SUBNET_MASK; - bp->bp_vend[26] = TAG_INTF_MTU; - bp->bp_vend[27] = TAG_SERVERID; - bp->bp_vend[28] = TAG_END; - } else - bp->bp_vend[7] = TAG_END; #else bp->bp_vend[4] = TAG_END; #endif @@ -176,8 +183,7 @@ bootp(sock, flag) if(sendrecv(d, bootpsend, bp, sizeof(*bp), - bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp)) - == -1) { + bootprecv, &pkt, (void **)&rbootp) == -1) { printf("bootp: no reply\n"); return; } @@ -188,7 +194,7 @@ bootp(sock, flag) bp->bp_vend[6] = DHCPREQUEST; bp->bp_vend[7] = TAG_REQ_ADDR; bp->bp_vend[8] = 4; - bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4); + bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4); bp->bp_vend[13] = TAG_SERVERID; bp->bp_vend[14] = 4; bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4); @@ -196,30 +202,25 @@ bootp(sock, flag) bp->bp_vend[20] = 4; leasetime = htonl(300); bcopy(&leasetime, &bp->bp_vend[21], 4); - if (flag & BOOTP_PXE) { - bp->bp_vend[25] = TAG_CLASSID; - bp->bp_vend[26] = 9; - bcopy("PXEClient", &bp->bp_vend[27], 9); - bp->bp_vend[36] = TAG_END; - } else - bp->bp_vend[25] = TAG_END; + bootp_fill_request(&bp->bp_vend[25]); expected_dhcpmsgtype = DHCPACK; + free(pkt); if(sendrecv(d, bootpsend, bp, sizeof(*bp), - bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp)) - == -1) { + bootprecv, &pkt, (void **)&rbootp) == -1) { printf("DHCPREQUEST failed\n"); return; } } #endif - myip = d->myip = rbuf.rbootp.bp_yiaddr; - servip = rbuf.rbootp.bp_siaddr; - if(rootip.s_addr == INADDR_ANY) rootip = servip; - bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile)); + myip = d->myip = rbootp->bp_yiaddr; + servip = rbootp->bp_siaddr; + if (rootip.s_addr == INADDR_ANY) + rootip = servip; + bcopy(rbootp->bp_file, bootfile, sizeof(bootfile)); bootfile[sizeof(bootfile) - 1] = '\0'; if (!netmask) { @@ -259,14 +260,12 @@ bootp(sock, flag) /* Bump xid so next request will be unique. */ ++d->xid; + free(pkt); } /* Transmit a bootp request */ static ssize_t -bootpsend(d, pkt, len) - struct iodesc *d; - void *pkt; - size_t len; +bootpsend(struct iodesc *d, void *pkt, size_t len) { struct bootp *bp; @@ -287,30 +286,25 @@ bootpsend(d, pkt, len) } static ssize_t -bootprecv(d, pkt, len, tleft) -struct iodesc *d; -void *pkt; -size_t len; -time_t tleft; +bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; struct bootp *bp; + void *ptr; -#ifdef BOOTP_DEBUGx +#ifdef BOOTP_DEBUG if (debug) printf("bootp_recvoffer: called\n"); #endif - n = readudp(d, pkt, len, tleft); + ptr = NULL; + n = readudp(d, &ptr, (void **)&bp, tleft); if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE) goto bad; - bp = (struct bootp *)pkt; - #ifdef BOOTP_DEBUG if (debug) - printf("bootprecv: checked. bp = 0x%lx, n = %d\n", - (long)bp, (int)n); + printf("bootprecv: checked. bp = %p, n = %zd\n", bp, n); #endif if (bp->bp_xid != htonl(d->xid)) { #ifdef BOOTP_DEBUG @@ -329,8 +323,21 @@ time_t tleft; /* Suck out vendor info */ if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) { - if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0) + int vsize = n - offsetof(struct bootp, bp_vend); + if (vend_rfc1048(bp->bp_vend, vsize) != 0) goto bad; + + /* Save copy of bootp reply or DHCP ACK message */ + if (bp->bp_op == BOOTREPLY && + ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) || + dhcp_ok == 0)) { + free(bootp_response); + bootp_response = malloc(n); + if (bootp_response != NULL) { + bootp_response_size = n; + bcopy(bp, bootp_response, bootp_response_size); + } + } } #ifdef BOOTP_VEND_CMU else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0) @@ -339,8 +346,11 @@ time_t tleft; else printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend); - return(n); + *pkt = ptr; + *payload = bp; + return (n); bad: + free(ptr); errno = 0; return (-1); } @@ -357,9 +367,7 @@ dhcp_try_rfc1048(u_char *cp, u_int len) } static int -vend_rfc1048(cp, len) - u_char *cp; - u_int len; +vend_rfc1048(u_char *cp, u_int len) { u_char *ep; int size; @@ -438,10 +446,6 @@ vend_rfc1048(cp, len) bcopy(cp, &dhcp_serverip.s_addr, sizeof(dhcp_serverip.s_addr)); } - if (tag == TAG_TFTP_SERVER) { - bcopy(cp, &tftpip.s_addr, - sizeof(tftpip.s_addr)); - } #endif cp += size; } @@ -450,8 +454,7 @@ vend_rfc1048(cp, len) #ifdef BOOTP_VEND_CMU static void -vend_cmu(cp) - u_char *cp; +vend_cmu(u_char *cp) { struct cmu_vend *vp; diff --git a/lib/libstand/bootp.h b/lib/libstand/bootp.h index 4a57df26e1b..2e7049b872a 100644 --- a/lib/libstand/bootp.h +++ b/lib/libstand/bootp.h @@ -108,7 +108,7 @@ struct bootp { #define TAG_T2 ((unsigned char) 59) #define TAG_CLASSID ((unsigned char) 60) #define TAG_CLIENTID ((unsigned char) 61) -#define TAG_TFTP_SERVER ((unsigned char) 150) +#define TAG_USER_CLASS ((unsigned char) 77) #endif #define TAG_END ((unsigned char) 255) @@ -123,12 +123,6 @@ struct bootp { #define DHCPRELEASE 7 #endif -/* - * bootp flags - */ -#define BOOTP_NONE 0x0000 /* No flags */ -#define BOOTP_PXE 0x0001 /* Booting from PXE. */ - /* * "vendor" data permitted for CMU bootp clients. */ @@ -148,6 +142,10 @@ struct cmu_vend { /* v_flags values */ #define VF_SMASK 1 /* Subnet mask field contains valid data */ +/* cached bootp response/dhcp ack */ +extern struct bootp *bootp_response; +extern size_t bootp_response_size; + int dhcp_try_rfc1048(u_char *cp, u_int len); #endif /* _BOOTP_H_ */ diff --git a/lib/libstand/bootparam.c b/lib/libstand/bootparam.c index e4d2f1022c9..1de2d53d77f 100644 --- a/lib/libstand/bootparam.c +++ b/lib/libstand/bootparam.c @@ -104,8 +104,7 @@ int xdr_string_decode(char **p, char *str, int *len_p); * know about us (don't want to broadcast a getport call). */ int -bp_whoami(sockfd) - int sockfd; +bp_whoami(int sockfd) { /* RPC structures for PMAPPROC_CALLIT */ struct args { @@ -126,22 +125,19 @@ bp_whoami(sockfd) n_long h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - n_long h[RPC_HEADER_WORDS]; - struct repl d; - } rdata; char *send_tail, *recv_head; struct iodesc *d; - int len, x; + void *pkt; + int len, x, rc; RPC_PRINTF(("bp_whoami: myip=%s\n", inet_ntoa(myip))); + rc = -1; if (!(d = socktodesc(sockfd))) { RPC_PRINTF(("bp_whoami: bad socket. %d\n", sockfd)); - return (-1); + return (rc); } args = &sdata.d; - repl = &rdata.d; /* * Build request args for PMAPPROC_CALLIT. @@ -156,19 +152,19 @@ bp_whoami(sockfd) * append encapsulated data (client IP address) */ if (xdr_inaddr_encode(&send_tail, myip)) - return (-1); + return (rc); /* RPC: portmap/callit */ d->myport = htons(--rpc_port); d->destip.s_addr = INADDR_BROADCAST; /* XXX: subnet bcast? */ /* rpc_call will set d->destport */ + pkt = NULL; len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT, - args, send_tail - (char*)args, - repl, sizeof(*repl)); + args, send_tail - (char*)args, (void **)&repl, &pkt); if (len < 8) { printf("bootparamd: 'whoami' call failed\n"); - return (-1); + goto done; } /* Save bootparam server address (from IP header). */ @@ -196,7 +192,7 @@ bp_whoami(sockfd) x = ntohl(repl->encap_len); if (len < x) { printf("bp_whoami: short reply, %d < %d\n", len, x); - return (-1); + goto done; } recv_head = (char*) repl->capsule; @@ -204,24 +200,27 @@ bp_whoami(sockfd) hostnamelen = MAXHOSTNAMELEN-1; if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) { RPC_PRINTF(("bp_whoami: bad hostname\n")); - return (-1); + goto done; } /* domain name */ domainnamelen = MAXHOSTNAMELEN-1; if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) { RPC_PRINTF(("bp_whoami: bad domainname\n")); - return (-1); + goto done; } /* gateway address */ if (xdr_inaddr_decode(&recv_head, &gateip)) { RPC_PRINTF(("bp_whoami: bad gateway\n")); - return (-1); + goto done; } /* success */ - return(0); + rc = 0; +done: + free(pkt); + return (rc); } @@ -233,25 +232,18 @@ bp_whoami(sockfd) * server pathname */ int -bp_getfile(sockfd, key, serv_addr, pathname) - int sockfd; - char *key; - char *pathname; - struct in_addr *serv_addr; +bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname) { struct { n_long h[RPC_HEADER_WORDS]; n_long d[64]; } sdata; - struct { - n_long h[RPC_HEADER_WORDS]; - n_long d[128]; - } rdata; + void *pkt; char serv_name[FNAME_SIZE]; - char *send_tail, *recv_head; + char *rdata, *send_tail; /* misc... */ struct iodesc *d; - int sn_len, path_len, rlen; + int rc = -1, sn_len, path_len, rlen; if (!(d = socktodesc(sockfd))) { RPC_PRINTF(("bp_getfile: bad socket. %d\n", sockfd)); @@ -259,7 +251,6 @@ bp_getfile(sockfd, key, serv_addr, pathname) } send_tail = (char*) sdata.d; - recv_head = (char*) rdata.d; /* * Build request message. @@ -281,17 +272,16 @@ bp_getfile(sockfd, key, serv_addr, pathname) d->myport = htons(--rpc_port); d->destip = bp_server_addr; /* rpc_call will set d->destport */ - + pkt = NULL; rlen = rpc_call(d, BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE, sdata.d, send_tail - (char*)sdata.d, - rdata.d, sizeof(rdata.d)); + (void **)&rdata, &pkt); if (rlen < 4) { RPC_PRINTF(("bp_getfile: short reply\n")); errno = EBADRPC; - return (-1); + goto done; } - recv_head = (char*) rdata.d; /* * Parse result message. @@ -299,26 +289,29 @@ bp_getfile(sockfd, key, serv_addr, pathname) /* server name */ sn_len = FNAME_SIZE-1; - if (xdr_string_decode(&recv_head, serv_name, &sn_len)) { + if (xdr_string_decode(&rdata, serv_name, &sn_len)) { RPC_PRINTF(("bp_getfile: bad server name\n")); - return (-1); + goto done; } /* server IP address (mountd/NFS) */ - if (xdr_inaddr_decode(&recv_head, serv_addr)) { + if (xdr_inaddr_decode(&rdata, serv_addr)) { RPC_PRINTF(("bp_getfile: bad server addr\n")); - return (-1); + goto done; } /* server pathname */ path_len = MAXPATHLEN-1; - if (xdr_string_decode(&recv_head, pathname, &path_len)) { + if (xdr_string_decode(&rdata, pathname, &path_len)) { RPC_PRINTF(("bp_getfile: bad server path\n")); - return (-1); + goto done; } /* success */ - return(0); + rc = 0; +done: + free(pkt); + return (rc); } @@ -329,17 +322,14 @@ bp_getfile(sockfd, key, serv_addr, pathname) int -xdr_string_encode(pkt, str, len) - char **pkt; - char *str; - int len; +xdr_string_encode(char **pkt, char *str, int len) { - u_int32_t *lenp; + uint32_t *lenp; char *datap; int padlen = (len + 3) & ~3; /* padded length */ /* The data will be int aligned. */ - lenp = (u_int32_t*) *pkt; + lenp = (uint32_t *) *pkt; *pkt += sizeof(*lenp); *lenp = htonl(len); @@ -351,18 +341,15 @@ xdr_string_encode(pkt, str, len) } int -xdr_string_decode(pkt, str, len_p) - char **pkt; - char *str; - int *len_p; /* bufsize - 1 */ +xdr_string_decode(char **pkt, char *str, int *len_p) { - u_int32_t *lenp; + uint32_t *lenp; char *datap; int slen; /* string length */ int plen; /* padded length */ /* The data will be int aligned. */ - lenp = (u_int32_t*) *pkt; + lenp = (uint32_t *) *pkt; *pkt += sizeof(*lenp); slen = ntohl(*lenp); plen = (slen + 3) & ~3; @@ -381,9 +368,7 @@ xdr_string_decode(pkt, str, len_p) int -xdr_inaddr_encode(pkt, ia) - char **pkt; - struct in_addr ia; /* network order */ +xdr_inaddr_encode(char **pkt, struct in_addr ia) { struct xdr_inaddr *xi; u_char *cp; @@ -414,9 +399,7 @@ xdr_inaddr_encode(pkt, ia) } int -xdr_inaddr_decode(pkt, ia) - char **pkt; - struct in_addr *ia; /* network order */ +xdr_inaddr_decode(char **pkt, struct in_addr *ia) { struct xdr_inaddr *xi; u_char *cp; diff --git a/lib/libstand/dosfs.c b/lib/libstand/dosfs.c index 859a28d925e..6bbe961dccc 100644 --- a/lib/libstand/dosfs.c +++ b/lib/libstand/dosfs.c @@ -65,6 +65,7 @@ struct fs_ops dosfs_fsops = { #define DEPSEC 16 /* directory entries per sector */ #define DSHIFT 4 /* DEPSEC shift */ #define LOCLUS 2 /* lowest cluster number */ +#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ /* DOS "BIOS Parameter Block" */ typedef struct { @@ -132,18 +133,6 @@ static DOS_DE dot[2] = { ((u_int)cv2((de)->dex.h_clus) << 16) | \ cv2((de)->clus)) -/* - * fat cache metadata - */ -struct fatcache { - int unit; /* disk unit number */ - int size; /* buffer (and fat) size in sectors */ - u_char *buf; -}; - -static struct fatcache fat; - -static int dosunmount(DOS_FS *); static int parsebs(DOS_FS *, DOS_BS *); static int namede(DOS_FS *, const char *, DOS_DE **); static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); @@ -153,36 +142,37 @@ static off_t fsize(DOS_FS *, DOS_DE *); static int fatcnt(DOS_FS *, u_int); static int fatget(DOS_FS *, u_int *); static int fatend(u_int, u_int); -static int ioread(DOS_FS *, u_int, void *, u_int); -static int ioget(struct open_file *, daddr_t, void *, u_int); +static int ioread(DOS_FS *, u_int, void *, size_t); +static int ioget(struct open_file *, daddr_t, void *, size_t); -static void -dos_read_fat(DOS_FS *fs, struct open_file *fd) +static int +dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) { - struct devdesc *dd = fd->f_devdata; - - if (fat.buf != NULL) { /* can we reuse old buffer? */ - if (fat.size != fs->spf) { - free(fat.buf); /* no, free old buffer */ - fat.buf = NULL; - } + int err; + size_t io_size; + daddr_t offset_in_fat, max_offset_in_fat; + + offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; + max_offset_in_fat = secbyt(fs->spf); + io_size = FATBLKSZ; + if (offset_in_fat > max_offset_in_fat) + offset_in_fat = max_offset_in_fat; + if (offset_in_fat + io_size > max_offset_in_fat) + io_size = ((size_t)(max_offset_in_fat - offset_in_fat)); + + if (io_size != 0) { + err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), + fs->fatbuf, io_size); + if (err != 0) { + fs->fatbuf_blknum = ((u_int)(-1)); + return (err); + } } + if (io_size < FATBLKSZ) + memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size); - if (fat.buf == NULL) - fat.buf = malloc(secbyt(fs->spf)); - - if (fat.buf != NULL) { - if (ioget(fd, fs->lsnfat, fat.buf, secbyt(fs->spf)) == 0) { - fat.size = fs->spf; - fat.unit = dd->d_unit; - return; - } - } - if (fat.buf != NULL) /* got IO error */ - free(fat.buf); - fat.buf = NULL; - fat.unit = -1; /* impossible unit */ - fat.size = 0; + fs->fatbuf_blknum = blknum; + return (0); } /* @@ -192,24 +182,27 @@ static int dos_mount(DOS_FS *fs, struct open_file *fd) { int err; - struct devdesc *dd = fd->f_devdata; u_char *buf; bzero(fs, sizeof(DOS_FS)); fs->fd = fd; - if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || - (err = ioget(fs->fd, 0, buf, secbyt(1))) || + if ((buf = malloc(secbyt(1))) == NULL) + return (errno); + if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || (err = parsebs(fs, (DOS_BS *)buf))) { - if (buf != NULL) - free(buf); - (void)dosunmount(fs); + free(buf); return (err); } free(buf); - if (fat.buf == NULL || fat.unit != dd->d_unit) - dos_read_fat(fs, fd); + if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) + return (errno); + err = dos_read_fatblk(fs, fd, 0); + if (err != 0) { + free(fs->fatbuf); + return (err); + } fs->root = dot[0]; fs->root.name[0] = ' '; @@ -228,21 +221,9 @@ dos_mount(DOS_FS *fs, struct open_file *fd) static int dos_unmount(DOS_FS *fs) { - int err; - if (fs->links) return (EBUSY); - if ((err = dosunmount(fs))) - return (err); - return (0); -} - -/* - * Common code shared by dos_mount() and dos_unmount() - */ -static int -dosunmount(DOS_FS *fs) -{ + free(fs->fatbuf); free(fs); return (0); } @@ -257,16 +238,20 @@ dos_open(const char *path, struct open_file *fd) DOS_FILE *f; DOS_FS *fs; u_int size, clus; - int err = 0; + int err; /* Allocate mount structure, associate with open */ - fs = malloc(sizeof(DOS_FS)); - - if ((err = dos_mount(fs, fd))) - goto out; + if ((fs = malloc(sizeof(DOS_FS))) == NULL) + return (errno); + if ((err = dos_mount(fs, fd))) { + free(fs); + return (err); + } - if ((err = namede(fs, path, &de))) - goto out; + if ((err = namede(fs, path, &de))) { + dos_unmount(fs); + return (err); + } clus = stclus(fs->fatsz, de); size = cv4(de->size); @@ -274,18 +259,20 @@ dos_open(const char *path, struct open_file *fd) if ((!(de->attr & FA_DIR) && (!clus != !size)) || ((de->attr & FA_DIR) && size) || (clus && !okclus(fs, clus))) { - err = EINVAL; - goto out; + dos_unmount(fs); + return (EINVAL); + } + if ((f = malloc(sizeof(DOS_FILE))) == NULL) { + err = errno; + dos_unmount(fs); + return (err); } - f = malloc(sizeof(DOS_FILE)); bzero(f, sizeof(DOS_FILE)); f->fs = fs; fs->links++; f->de = *de; fd->f_fsdata = (void *)f; - - out: - return (err); + return (0); } /* @@ -761,34 +748,57 @@ fatcnt(DOS_FS *fs, u_int c) } /* - * Get next cluster in cluster chain. Use in core fat cache unless another - * device replaced it. + * Get next cluster in cluster chain. Use in core fat cache unless + * the number of current 128K block in FAT has changed. */ static int fatget(DOS_FS *fs, u_int *c) { - u_char buf[4]; - u_int x, offset, n, nbyte; - struct devdesc *dd = fs->fd->f_devdata; - int err = 0; + u_int val_in, val_out, offset, blknum, nbyte; + const u_char *p_entry; + int err; - if (fat.unit != dd->d_unit) { - /* fat cache was changed to another device, dont use it */ - err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, - fs->fatsz != 32 ? 2 : 4); - if (err) - return err; - } else { - offset = fatoff(fs->fatsz, *c); - nbyte = fs->fatsz != 32 ? 2 : 4; + /* check input value to prevent overflow in fatoff() */ + val_in = *c; + if (val_in & 0xf0000000) + return (EINVAL); - if (offset + nbyte > secbyt(fat.size)) - return (EINVAL); - memcpy(buf, fat.buf + offset, nbyte); + /* ensure that current 128K FAT block is cached */ + offset = fatoff(fs->fatsz, val_in); + nbyte = fs->fatsz != 32 ? 2 : 4; + if (offset + nbyte > secbyt(fs->spf)) + return (EINVAL); + blknum = offset / FATBLKSZ; + offset %= FATBLKSZ; + if (offset + nbyte > FATBLKSZ) + return (EINVAL); + if (blknum != fs->fatbuf_blknum) { + err = dos_read_fatblk(fs, fs->fd, blknum); + if (err != 0) + return (err); } + p_entry = fs->fatbuf + offset; - x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); - *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; + /* extract cluster number from FAT entry */ + switch (fs->fatsz) { + case 32: + val_out = cv4(p_entry); + val_out &= 0x0fffffff; + break; + case 16: + val_out = cv2(p_entry); + break; + case 12: + val_out = cv2(p_entry); + if (val_in & 1) + val_out >>= 4; + else + val_out &= 0xfff; + break; + default: + return (EINVAL); + } + *c = val_out; return (0); } @@ -805,7 +815,7 @@ fatend(u_int sz, u_int c) * Offset-based I/O primitive */ static int -ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) +ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte) { char *s; u_int off, n; @@ -843,8 +853,16 @@ ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) * Sector-based I/O primitive */ static int -ioget(struct open_file *fd, daddr_t lsec, void *buf, u_int size) +ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) { - return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, - size, buf, NULL)); + size_t rsize; + int rv; + + /* Make sure we get full read or error. */ + rsize = 0; + rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, + size, buf, &rsize); + if ((rv == 0) && (size != rsize)) + rv = EIO; + return (rv); } diff --git a/lib/libstand/dosfs.h b/lib/libstand/dosfs.h index f2370ee502f..5b11e0fc375 100644 --- a/lib/libstand/dosfs.h +++ b/lib/libstand/dosfs.h @@ -96,6 +96,8 @@ typedef union { typedef struct { struct open_file *fd; /* file descriptor */ + u_char *fatbuf; /* FAT cache buffer */ + u_int fatbuf_blknum; /* number of 128K block in FAT cache buffer */ u_int links; /* active links to structure */ u_int spc; /* sectors per cluster */ u_int bsize; /* cluster size in bytes */ diff --git a/lib/libstand/ether.c b/lib/libstand/ether.c index 50a8934ca1f..7ab9d74e325 100644 --- a/lib/libstand/ether.c +++ b/lib/libstand/ether.c @@ -54,12 +54,7 @@ __FBSDID("$FreeBSD$"); /* Caller must leave room for ethernet header in front!! */ ssize_t -sendether(d, pkt, len, dea, etype) - struct iodesc *d; - void *pkt; - size_t len; - u_char *dea; - int etype; +sendether(struct iodesc *d, void *pkt, size_t len, uint8_t *dea, int etype) { ssize_t n; struct ether_header *eh; @@ -86,32 +81,31 @@ sendether(d, pkt, len, dea, etype) /* * Get a packet of any Ethernet type, with our address or - * the broadcast address. Save the Ether type in arg 5. - * NOTE: Caller must leave room for the Ether header. + * the broadcast address. Save the Ether type in etype. + * Unless there is an error, we pass the whole packet and the unencapsulated + * data. */ ssize_t -readether(d, pkt, len, tleft, etype) - struct iodesc *d; - void *pkt; - size_t len; - time_t tleft; - u_int16_t *etype; +readether(struct iodesc *d, void **pkt, void **payload, time_t tleft, + uint16_t *etype) { ssize_t n; struct ether_header *eh; + void *ptr; #ifdef ETHER_DEBUG if (debug) printf("readether: called\n"); #endif - eh = (struct ether_header *)pkt - 1; - len += sizeof(*eh); - - n = netif_get(d, eh, len, tleft); - if (n == -1 || n < sizeof(*eh)) + ptr = NULL; + n = netif_get(d, &ptr, tleft); + if (n == -1 || n < sizeof(*eh)) { + free(ptr); return (-1); + } + eh = (struct ether_header *)((uintptr_t)ptr + ETHER_ALIGN); /* Validate Ethernet address. */ if (bcmp(d->myea, eh->ether_dhost, 6) != 0 && bcmp(bcea, eh->ether_dhost, 6) != 0) { @@ -120,8 +114,12 @@ readether(d, pkt, len, tleft, etype) printf("readether: not ours (ea=%s)\n", ether_sprintf(eh->ether_dhost)); #endif + free(ptr); return (-1); } + + *pkt = ptr; + *payload = (void *)((uintptr_t)eh + sizeof(*eh)); *etype = ntohs(eh->ether_type); n -= sizeof(*eh); @@ -133,8 +131,7 @@ readether(d, pkt, len, tleft, etype) */ static char digits[] = "0123456789abcdef"; char * -ether_sprintf(ap) - u_char *ap; +ether_sprintf(u_char *ap) { int i; static char etherbuf[18]; diff --git a/lib/libstand/globals.c b/lib/libstand/globals.c index 83b9f777a85..8fb56cf23e6 100644 --- a/lib/libstand/globals.c +++ b/lib/libstand/globals.c @@ -32,7 +32,6 @@ struct in_addr nameip; /* DNS server ip address */ struct in_addr rootip; /* root ip address */ struct in_addr swapip; /* swap ip address */ struct in_addr gateip; /* gateway ip address */ -struct in_addr tftpip; /* TFTP ip address */ n_long netmask = 0xffffff00; /* subnet or net mask */ u_int intf_mtu; /* interface mtu from bootp/dhcp */ int errno; /* our old friend */ diff --git a/lib/libstand/ip.c b/lib/libstand/ip.c new file mode 100644 index 00000000000..541cabc4bdd --- /dev/null +++ b/lib/libstand/ip.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The send and receive functions were originally implemented in udp.c and + * moved here. Also it is likely some more cleanup can be done, especially + * once we will implement the support for tcp. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stand.h" +#include "net.h" + +typedef STAILQ_HEAD(ipqueue, ip_queue) ip_queue_t; +struct ip_queue { + void *ipq_pkt; + struct ip *ipq_hdr; + STAILQ_ENTRY(ip_queue) ipq_next; +}; + +/* + * Fragment re-assembly queue. + */ +struct ip_reasm { + struct in_addr ip_src; + struct in_addr ip_dst; + uint16_t ip_id; + uint8_t ip_proto; + uint8_t ip_ttl; + size_t ip_total_size; + ip_queue_t ip_queue; + void *ip_pkt; + struct ip *ip_hdr; + STAILQ_ENTRY(ip_reasm) ip_next; +}; + +STAILQ_HEAD(ire_list, ip_reasm) ire_list = STAILQ_HEAD_INITIALIZER(ire_list); + +/* Caller must leave room for ethernet and ip headers in front!! */ +ssize_t +sendip(struct iodesc *d, void *pkt, size_t len, uint8_t proto) +{ + ssize_t cc; + struct ip *ip; + u_char *ea; + +#ifdef NET_DEBUG + if (debug) { + printf("sendip: proto: %x d=%p called.\n", proto, (void *)d); + if (d) { + printf("saddr: %s:%d", + inet_ntoa(d->myip), ntohs(d->myport)); + printf(" daddr: %s:%d\n", + inet_ntoa(d->destip), ntohs(d->destport)); + } + } +#endif + + ip = (struct ip *)pkt - 1; + len += sizeof(*ip); + + bzero(ip, sizeof(*ip)); + + ip->ip_v = IPVERSION; /* half-char */ + ip->ip_hl = sizeof(*ip) >> 2; /* half-char */ + ip->ip_len = htons(len); + ip->ip_p = proto; /* char */ + ip->ip_ttl = IPDEFTTL; /* char */ + ip->ip_src = d->myip; + ip->ip_dst = d->destip; + ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */ + + if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 || + netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask)) + ea = arpwhohas(d, ip->ip_dst); + else + ea = arpwhohas(d, gateip); + + cc = sendether(d, ip, len, ea, ETHERTYPE_IP); + if (cc == -1) + return (-1); + if (cc != len) + panic("sendip: bad write (%zd != %zd)", cc, len); + return (cc - sizeof(*ip)); +} + +static void +ip_reasm_free(struct ip_reasm *ipr) +{ + struct ip_queue *ipq; + + while ((ipq = STAILQ_FIRST(&ipr->ip_queue)) != NULL) { + STAILQ_REMOVE_HEAD(&ipr->ip_queue, ipq_next); + free(ipq->ipq_pkt); + free(ipq); + } + free(ipr->ip_pkt); + free(ipr); +} + +static int +ip_reasm_add(struct ip_reasm *ipr, void *pkt, struct ip *ip) +{ + struct ip_queue *ipq, *prev, *p; + + if ((ipq = calloc(1, sizeof (*ipq))) == NULL) + return (1); + + ipq->ipq_pkt = pkt; + ipq->ipq_hdr = ip; + + prev = NULL; + STAILQ_FOREACH(p, &ipr->ip_queue, ipq_next) { + if ((ntohs(p->ipq_hdr->ip_off) & IP_OFFMASK) < + (ntohs(ip->ip_off) & IP_OFFMASK)) { + prev = p; + continue; + } + if (prev == NULL) + break; + + STAILQ_INSERT_AFTER(&ipr->ip_queue, prev, ipq, ipq_next); + return (0); + } + STAILQ_INSERT_HEAD(&ipr->ip_queue, ipq, ipq_next); + return (0); +} + +/* + * Receive a IP packet and validate it is for us. + */ +static ssize_t +readipv4(struct iodesc *d, void **pkt, void **payload, time_t tleft, + uint8_t proto) +{ + ssize_t n; + size_t hlen; + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + uint16_t etype; /* host order */ + char *ptr; + struct ip_reasm *ipr; + struct ip_queue *ipq, *last; + +#ifdef NET_DEBUG + if (debug) + printf("readip: called\n"); +#endif + + ip = NULL; + ptr = NULL; + n = readether(d, (void **)&ptr, (void **)&ip, tleft, &etype); + if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) { + free(ptr); + return (-1); + } + + /* Ethernet address checks now in readether() */ + + /* Need to respond to ARP requests. */ + if (etype == ETHERTYPE_ARP) { + struct arphdr *ah = (void *)ip; + if (ah->ar_op == htons(ARPOP_REQUEST)) { + /* Send ARP reply */ + arp_reply(d, ah); + } + free(ptr); + errno = EAGAIN; /* Call me again. */ + return (-1); + } + + if (etype != ETHERTYPE_IP) { +#ifdef NET_DEBUG + if (debug) + printf("readip: not IP. ether_type=%x\n", etype); +#endif + free(ptr); + return (-1); + } + + /* Check ip header */ + if (ip->ip_v != IPVERSION || + ip->ip_p != proto) { /* half char */ +#ifdef NET_DEBUG + if (debug) { + printf("readip: IP version or proto. ip_v=%d ip_p=%d\n", + ip->ip_v, ip->ip_p); + } +#endif + free(ptr); + return (-1); + } + + hlen = ip->ip_hl << 2; + if (hlen < sizeof(*ip) || + in_cksum(ip, hlen) != 0) { +#ifdef NET_DEBUG + if (debug) + printf("readip: short hdr or bad cksum.\n"); +#endif + free(ptr); + return (-1); + } + if (n < ntohs(ip->ip_len)) { +#ifdef NET_DEBUG + if (debug) + printf("readip: bad length %d < %d.\n", + (int)n, ntohs(ip->ip_len)); +#endif + free(ptr); + return (-1); + } + if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) { +#ifdef NET_DEBUG + if (debug) { + printf("readip: bad saddr %s != ", inet_ntoa(d->myip)); + printf("%s\n", inet_ntoa(ip->ip_dst)); + } +#endif + free(ptr); + return (-1); + } + + /* Unfragmented packet. */ + if ((ntohs(ip->ip_off) & IP_MF) == 0 && + (ntohs(ip->ip_off) & IP_OFFMASK) == 0) { + uh = (struct udphdr *)((uintptr_t)ip + sizeof (*ip)); + /* If there were ip options, make them go away */ + if (hlen != sizeof(*ip)) { + bcopy(((u_char *)ip) + hlen, uh, uh->uh_ulen - hlen); + ip->ip_len = htons(sizeof(*ip)); + n -= hlen - sizeof(*ip); + } + + n = (n > (ntohs(ip->ip_len) - sizeof(*ip))) ? + ntohs(ip->ip_len) - sizeof(*ip) : n; + *pkt = ptr; + *payload = (void *)((uintptr_t)ip + sizeof(*ip)); + return (n); + } + + STAILQ_FOREACH(ipr, &ire_list, ip_next) { + if (ipr->ip_src.s_addr == ip->ip_src.s_addr && + ipr->ip_dst.s_addr == ip->ip_dst.s_addr && + ipr->ip_id == ip->ip_id && + ipr->ip_proto == ip->ip_p) + break; + } + + /* Allocate new reassembly entry */ + if (ipr == NULL) { + if ((ipr = calloc(1, sizeof (*ipr))) == NULL) { + free(ptr); + return (-1); + } + + ipr->ip_src = ip->ip_src; + ipr->ip_dst = ip->ip_dst; + ipr->ip_id = ip->ip_id; + ipr->ip_proto = ip->ip_p; + ipr->ip_ttl = MAXTTL; + STAILQ_INIT(&ipr->ip_queue); + STAILQ_INSERT_TAIL(&ire_list, ipr, ip_next); + } + + if (ip_reasm_add(ipr, ptr, ip) != 0) { + STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); + free(ipr); + free(ptr); + return (-1); + } + + if ((ntohs(ip->ip_off) & IP_MF) == 0) { + ipr->ip_total_size = (8 * (ntohs(ip->ip_off) & IP_OFFMASK)); + ipr->ip_total_size += n + sizeof (*ip); + ipr->ip_total_size += sizeof (struct ether_header); + + ipr->ip_pkt = malloc(ipr->ip_total_size + 2); + if (ipr->ip_pkt == NULL) { + STAILQ_REMOVE(&ire_list, ipr, ip_reasm, ip_next); + ip_reasm_free(ipr); + return (-1); + } + } + + /* + * If we do not have re-assembly buffer ipr->ip_pkt, we are still + * missing fragments, so just restart the read. + */ + if (ipr->ip_pkt == NULL) { + errno = EAGAIN; + return (-1); + } + + /* + * Walk the packet list in reassembly queue, if we got all the + * fragments, build the packet. + */ + n = 0; + last = NULL; + STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { + if ((ntohs(ipq->ipq_hdr->ip_off) & IP_OFFMASK) != n / 8) { + errno = EAGAIN; + return (-1); + } + + n += ntohs(ipq->ipq_hdr->ip_len) - (ipq->ipq_hdr->ip_hl << 2); + last = ipq; + } + if ((ntohs(last->ipq_hdr->ip_off) & IP_MF) != 0) { + errno = EAGAIN; + return (-1); + } + + ipq = STAILQ_FIRST(&ipr->ip_queue); + /* Fabricate ethernet header */ + eh = (struct ether_header *)((uintptr_t)ipr->ip_pkt + 2); + bcopy((void *)((uintptr_t)ipq->ipq_pkt + 2), eh, sizeof (*eh)); + + /* Fabricate IP header */ + ipr->ip_hdr = (struct ip *)((uintptr_t)eh + sizeof (*eh)); + bcopy(ipq->ipq_hdr, ipr->ip_hdr, sizeof (*ipr->ip_hdr)); + ipr->ip_hdr->ip_hl = sizeof (*ipr->ip_hdr) >> 2; + ipr->ip_hdr->ip_len = htons(n); + ipr->ip_hdr->ip_sum = 0; + ipr->ip_hdr->ip_sum = in_cksum(ipr->ip_hdr, sizeof (*ipr->ip_hdr)); + + n = 0; + ptr = (char *)((uintptr_t)ipr->ip_hdr + sizeof (*ipr->ip_hdr)); + STAILQ_FOREACH(ipq, &ipr->ip_queue, ipq_next) { + char *data; + size_t len; + + hlen = ipq->ipq_hdr->ip_hl << 2; + len = ntohs(ipq->ipq_hdr->ip_len) - hlen; + data = (char *)((uintptr_t)ipq->ipq_hdr + hlen); + + bcopy(data, ptr + n, len); + n += len; + } + + *pkt = ipr->ip_pkt; + ipr->ip_pkt = NULL; /* Avoid free from ip_reasm_free() */ + *payload = ptr; + + /* Clean up the reassembly list */ + while ((ipr = STAILQ_FIRST(&ire_list)) != NULL) { + STAILQ_REMOVE_HEAD(&ire_list, ip_next); + ip_reasm_free(ipr); + } + return (n); +} + +/* + * Receive a IP packet. + */ +ssize_t +readip(struct iodesc *d, void **pkt, void **payload, time_t tleft, + uint8_t proto) +{ + time_t t; + ssize_t ret = -1; + + t = getsecs(); + while ((getsecs() - t) < tleft) { + errno = 0; + ret = readipv4(d, pkt, payload, tleft, proto); + if (errno != EAGAIN) + break; + } + return (ret); +} diff --git a/lib/libstand/net.c b/lib/libstand/net.c index 24593e6d2ec..175ca5acc09 100644 --- a/lib/libstand/net.c +++ b/lib/libstand/net.c @@ -70,10 +70,10 @@ __FBSDID("$FreeBSD$"); */ ssize_t sendrecv(struct iodesc *d, - ssize_t (*sproc)(struct iodesc *, void *, size_t), - void *sbuf, size_t ssize, - ssize_t (*rproc)(struct iodesc *, void *, size_t, time_t), - void *rbuf, size_t rsize) + ssize_t (*sproc)(struct iodesc *, void *, size_t), + void *sbuf, size_t ssize, + ssize_t (*rproc)(struct iodesc *, void **, void **, time_t), + void **pkt, void **payload) { ssize_t cc; time_t t, tmo, tlast; @@ -116,7 +116,7 @@ sendrecv(struct iodesc *d, } /* Try to get a packet and process it. */ - cc = (*rproc)(d, rbuf, rsize, tleft); + cc = (*rproc)(d, pkt, payload, tleft); /* Return on data, EOF or real error. */ if (cc != -1 || errno != 0) return (cc); diff --git a/lib/libstand/net.h b/lib/libstand/net.h index 2c7fd4c9715..f91435c6cdc 100644 --- a/lib/libstand/net.h +++ b/lib/libstand/net.h @@ -91,7 +91,6 @@ extern struct in_addr rootip; extern struct in_addr swapip; extern struct in_addr gateip; extern struct in_addr nameip; -extern struct in_addr tftpip; extern n_long netmask; extern u_int intf_mtu; @@ -107,19 +106,20 @@ int rarp_getipaddress(int); /* Link functions: */ ssize_t sendether(struct iodesc *d, void *pkt, size_t len, u_char *dea, int etype); -ssize_t readether(struct iodesc *d, void *pkt, size_t len, - time_t tleft, u_int16_t *etype); +ssize_t readether(struct iodesc *, void **, void **, time_t, uint16_t *); +ssize_t sendip(struct iodesc *, void *, size_t, uint8_t); +ssize_t readip(struct iodesc *, void **, void **, time_t, uint8_t); ssize_t sendudp(struct iodesc *, void *, size_t); -ssize_t readudp(struct iodesc *, void *, size_t, time_t); +ssize_t readudp(struct iodesc *, void **, void **, time_t); ssize_t sendrecv(struct iodesc *, - ssize_t (*)(struct iodesc *, void *, size_t), + ssize_t (*)(struct iodesc *, void *, size_t), void *, size_t, - ssize_t (*)(struct iodesc *, void *, size_t, time_t), - void *, size_t); + ssize_t (*)(struct iodesc *, void **, void **, time_t), + void **, void **); /* bootp/DHCP */ -void bootp(int, int); +void bootp(int); /* Utilities: */ char *ether_sprintf(u_char *); diff --git a/lib/libstand/netif.c b/lib/libstand/netif.c index c260690c503..105f9a31ab5 100644 --- a/lib/libstand/netif.c +++ b/lib/libstand/netif.c @@ -59,7 +59,7 @@ int netif_debug = 0; */ void -netif_init() +netif_init(void) { struct netif_driver *drv; int d, i; @@ -76,13 +76,11 @@ netif_init() } int -netif_match(nif, machdep_hint) - struct netif *nif; - void *machdep_hint; +netif_match(struct netif *nif, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; -#if 0 +#if NETIF_DEBUG if (netif_debug) printf("%s%d: netif_match (%d)\n", drv->netif_bname, nif->nif_unit, nif->nif_sel); @@ -91,8 +89,7 @@ netif_match(nif, machdep_hint) } struct netif * -netif_select(machdep_hint) - void *machdep_hint; +netif_select(void *machdep_hint) { int d, u, unit_done, s; struct netif_driver *drv; @@ -162,9 +159,7 @@ netif_select(machdep_hint) } int -netif_probe(nif, machdep_hint) - struct netif *nif; - void *machdep_hint; +netif_probe(struct netif *nif, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; @@ -176,10 +171,7 @@ netif_probe(nif, machdep_hint) } void -netif_attach(nif, desc, machdep_hint) - struct netif *nif; - struct iodesc *desc; - void *machdep_hint; +netif_attach(struct netif *nif, struct iodesc *desc, void *machdep_hint) { struct netif_driver *drv = nif->nif_driver; @@ -199,8 +191,7 @@ netif_attach(nif, desc, machdep_hint) } void -netif_detach(nif) - struct netif *nif; +netif_detach(struct netif *nif) { struct netif_driver *drv = nif->nif_driver; @@ -217,11 +208,7 @@ netif_detach(nif) } ssize_t -netif_get(desc, pkt, len, timo) - struct iodesc *desc; - void *pkt; - size_t len; - time_t timo; +netif_get(struct iodesc *desc, void **pkt, time_t timo) { #ifdef NETIF_DEBUG struct netif *nif = desc->io_netif; @@ -238,20 +225,17 @@ netif_get(desc, pkt, len, timo) panic("%s%d: no netif_get support\n", drv->netif_bname, nif->nif_unit); #endif - rv = drv->netif_get(desc, pkt, len, timo); + rv = drv->netif_get(desc, pkt, timo); #ifdef NETIF_DEBUG if (netif_debug) printf("%s%d: netif_get returning %d\n", drv->netif_bname, nif->nif_unit, (int)rv); #endif - return rv; + return (rv); } ssize_t -netif_put(desc, pkt, len) - struct iodesc *desc; - void *pkt; - size_t len; +netif_put(struct iodesc *desc, void *pkt, size_t len) { #ifdef NETIF_DEBUG struct netif *nif = desc->io_netif; @@ -274,12 +258,11 @@ netif_put(desc, pkt, len) printf("%s%d: netif_put returning %d\n", drv->netif_bname, nif->nif_unit, (int)rv); #endif - return rv; + return (rv); } struct iodesc * -socktodesc(sock) - int sock; +socktodesc(int sock) { if (sock >= SOPEN_MAX) { errno = EBADF; @@ -289,8 +272,7 @@ socktodesc(sock) } int -netif_open(machdep_hint) - void *machdep_hint; +netif_open(void *machdep_hint) { int fd; struct iodesc *s; @@ -313,23 +295,22 @@ netif_open(machdep_hint) printf("netboot: couldn't probe %s%d\n", nif->nif_driver->netif_bname, nif->nif_unit); errno = EINVAL; - return(-1); + return (-1); } netif_attach(nif, s, machdep_hint); - return(fd); + return (fd); } int -netif_close(sock) - int sock; +netif_close(int sock) { if (sock >= SOPEN_MAX) { errno = EBADF; - return(-1); + return (-1); } netif_detach(sockets[sock].io_netif); sockets[sock].io_netif = (struct netif *)0; - return(0); + return (0); } diff --git a/lib/libstand/netif.h b/lib/libstand/netif.h index dac285107a7..44165ab0d88 100644 --- a/lib/libstand/netif.h +++ b/lib/libstand/netif.h @@ -6,15 +6,13 @@ #define __SYS_LIBNETBOOT_NETIF_H #include "iodesc.h" -#define NENTS(x) sizeof(x)/sizeof(x[0]) - struct netif_driver { const char *netif_bname; int (*netif_match)(struct netif *, void *); int (*netif_probe)(struct netif *, void *); void (*netif_init)(struct iodesc *, void *); - int (*netif_get)(struct iodesc *, void *, size_t, time_t); - int (*netif_put)(struct iodesc *, void *, size_t); + ssize_t (*netif_get)(struct iodesc *, void **, time_t); + ssize_t (*netif_put)(struct iodesc *, void *, size_t); void (*netif_end)(struct netif *); struct netif_dif *netif_ifs; int netif_nifs; @@ -56,7 +54,7 @@ struct netif *netif_select(void *); int netif_probe(struct netif *, void *); void netif_attach(struct netif *, struct iodesc *, void *); void netif_detach(struct netif *); -ssize_t netif_get(struct iodesc *, void *, size_t, time_t); +ssize_t netif_get(struct iodesc *, void **, time_t); ssize_t netif_put(struct iodesc *, void *, size_t); int netif_open(void *); diff --git a/lib/libstand/nfs.c b/lib/libstand/nfs.c index 8a7e111fcdd..322acb8d527 100644 --- a/lib/libstand/nfs.c +++ b/lib/libstand/nfs.c @@ -52,7 +52,7 @@ __FBSDID("$FreeBSD$"); #define NFS_DEBUGxx #define NFSREAD_MIN_SIZE 1024 -#define NFSREAD_MAX_SIZE 4096 +#define NFSREAD_MAX_SIZE 16384 /* NFSv3 definitions */ #define NFS_V3MAXFHSIZE 64 @@ -185,6 +185,7 @@ set_nfs_read_size(void) int nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) { + void *pkt = NULL; int len; struct args { uint32_t len; @@ -201,10 +202,6 @@ nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - uint32_t h[RPC_HEADER_WORDS]; - struct repl d; - } rdata; size_t cc; #ifdef NFS_DEBUG @@ -213,7 +210,6 @@ nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) #endif args = &sdata.d; - repl = &rdata.d; bzero(args, sizeof(*args)); len = strlen(path); @@ -224,18 +220,25 @@ nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) len = sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, - args, len, repl, sizeof(*repl)); - if (cc == -1) + args, len, (void **)&repl, &pkt); + if (cc == -1) { + free(pkt); /* errno was set by rpc_call */ return (errno); - if (cc < 2 * sizeof (uint32_t)) + } + if (cc < 2 * sizeof (uint32_t)) { + free(pkt); return (EBADRPC); - if (repl->errno != 0) + } + if (repl->errno != 0) { + free(pkt); return (ntohl(repl->errno)); + } *fhlenp = ntohl(repl->fhsize); bcopy(repl->fh, fhp, *fhlenp); set_nfs_read_size(); + free(pkt); return (0); } @@ -246,6 +249,7 @@ nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, u_char *fhp) int nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) { + void *pkt = NULL; int len, rlen, pos; struct args { uint32_t fhsize; @@ -263,10 +267,6 @@ nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - uint32_t h[RPC_HEADER_WORDS]; - struct repl d; - } rdata; ssize_t cc; #ifdef NFS_DEBUG @@ -275,7 +275,6 @@ nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) #endif args = &sdata.d; - repl = &rdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); @@ -289,23 +288,30 @@ nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) len = sizeof(uint32_t) + pos * sizeof(uint32_t) + roundup(len, sizeof(uint32_t)); - rlen = sizeof(*repl); - cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP, - args, len, repl, rlen); - if (cc == -1) + args, len, (void **)&repl, &pkt); + if (cc == -1) { + free(pkt); return (errno); /* XXX - from rpc_call */ - if (cc < 2 * sizeof(uint32_t)) + } + if (cc < 2 * sizeof(uint32_t)) { + free(pkt); return (EIO); - if (repl->errno != 0) + } + if (repl->errno != 0) { + free(pkt); /* saerrno.h now matches NFS error numbers. */ return (ntohl(repl->errno)); + } newfd->fhsize = ntohl(repl->fhsize); bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize); pos = roundup(newfd->fhsize, sizeof(uint32_t)) / sizeof(uint32_t); - if (repl->fhplusattr[pos++] == 0) + if (repl->fhplusattr[pos++] == 0) { + free(pkt); return (EIO); + } bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof(newfd->fa)); + free(pkt); return (0); } @@ -316,6 +322,7 @@ nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd) int nfs_readlink(struct nfs_iodesc *d, char *buf) { + void *pkt = NULL; struct args { uint32_t fhsize; u_char fh[NFS_V3MAXFHSIZE]; @@ -331,11 +338,8 @@ nfs_readlink(struct nfs_iodesc *d, char *buf) uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - uint32_t h[RPC_HEADER_WORDS]; - struct repl d; - } rdata; ssize_t cc; + int rc = 0; #ifdef NFS_DEBUG if (debug) @@ -343,32 +347,41 @@ nfs_readlink(struct nfs_iodesc *d, char *buf) #endif args = &sdata.d; - repl = &rdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); bcopy(d->fh, args->fh, d->fhsize); cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK, args, sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), - repl, sizeof(*repl)); + (void **)&repl, &pkt); if (cc == -1) return (errno); - if (cc < 2 * sizeof(uint32_t)) - return (EIO); + if (cc < 2 * sizeof(uint32_t)) { + rc = EIO; + goto done; + } - if (repl->errno != 0) - return (ntohl(repl->errno)); + if (repl->errno != 0) { + rc = ntohl(repl->errno); + goto done; + } - if (repl->ok == 0) - return (EIO); + if (repl->ok == 0) { + rc = EIO; + goto done; + } repl->len = ntohl(repl->len); - if (repl->len > NFS_MAXPATHLEN) - return (ENAMETOOLONG); + if (repl->len > NFS_MAXPATHLEN) { + rc = ENAMETOOLONG; + goto done; + } bcopy(repl->path, buf, repl->len); buf[repl->len] = 0; +done: + free(pkt); return (0); } #endif @@ -380,6 +393,7 @@ nfs_readlink(struct nfs_iodesc *d, char *buf) ssize_t nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) { + void *pkt = NULL; struct args { uint32_t fhsize; uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof(uint32_t) + 3]; @@ -397,16 +411,11 @@ nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - uint32_t h[RPC_HEADER_WORDS]; - struct repl d; - } rdata; size_t cc; long x; int hlen, rlen, pos; args = &sdata.d; - repl = &rdata.d; bzero(args, sizeof(*args)); args->fhsize = htonl(d->fhsize); @@ -421,16 +430,19 @@ nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ, args, 4 * sizeof(uint32_t) + roundup(d->fhsize, sizeof(uint32_t)), - repl, sizeof(*repl)); - if (cc == -1) + (void **)&repl, &pkt); + if (cc == -1) { /* errno was already set by rpc_call */ return (-1); + } if (cc < hlen) { errno = EBADRPC; + free(pkt); return (-1); } if (repl->errno != 0) { errno = ntohl(repl->errno); + free(pkt); return (-1); } rlen = cc - hlen; @@ -438,9 +450,11 @@ nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len) if (rlen < x) { printf("nfsread: short packet, %d < %ld\n", rlen, x); errno = EBADRPC; + free(pkt); return (-1); } bcopy(repl->data, addr, x); + free(pkt); return (x); } @@ -481,17 +495,8 @@ nfs_open(const char *upath, struct open_file *f) return (ENXIO); } - /* - * This is silly - we should look at dv_type but that value is - * arch dependant and we can't use it here. - */ -#ifndef __i386__ - if (strcmp(f->f_dev->dv_name, "net") != 0) - return (EINVAL); -#else - if (strcmp(f->f_dev->dv_name, "pxe") != 0) + if (f->f_dev->dv_type != DEVT_NET) return (EINVAL); -#endif if (!(desc = socktodesc(*(int *)(f->f_devdata)))) return (EINVAL); @@ -660,9 +665,8 @@ nfs_close(struct open_file *f) printf("nfs_close: fp=0x%lx\n", (u_long)fp); #endif - if (fp) - free(fp); - f->f_fsdata = (void *)0; + free(fp); + f->f_fsdata = NULL; return (0); } @@ -773,11 +777,12 @@ nfs_readdir(struct open_file *f, struct dirent *d) struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata; struct nfsv3_readdir_repl *repl; struct nfsv3_readdir_entry *rent; + static void *pkt = NULL; static char *buf; static struct nfs_iodesc *pfp = NULL; static uint64_t cookie = 0; size_t cc; - int pos; + int pos, rc; struct args { uint32_t fhsize; @@ -787,14 +792,12 @@ nfs_readdir(struct open_file *f, struct dirent *d) uint32_t h[RPC_HEADER_WORDS]; struct args d; } sdata; - static struct { - uint32_t h[RPC_HEADER_WORDS]; - u_char d[NFS_READDIRSIZE]; - } rdata; if (fp != pfp || fp->off != cookie) { pfp = NULL; refill: + free(pkt); + pkt = NULL; args = &sdata.d; bzero(args, sizeof(*args)); @@ -810,11 +813,16 @@ nfs_readdir(struct open_file *f, struct dirent *d) cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR, args, 6 * sizeof(uint32_t) + roundup(fp->fhsize, sizeof(uint32_t)), - rdata.d, sizeof(rdata.d)); - buf = rdata.d; + (void **)&buf, &pkt); + if (cc == -1) { + rc = errno; + goto err; + } repl = (struct nfsv3_readdir_repl *)buf; - if (repl->errno != 0) - return (ntohl(repl->errno)); + if (repl->errno != 0) { + rc = ntohl(repl->errno); + goto err; + } pfp = fp; cookie = fp->off; fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) | @@ -826,8 +834,8 @@ nfs_readdir(struct open_file *f, struct dirent *d) if (rent->follows == 0) { /* fid0 is actually eof */ if (rent->fid0 != 0) { - cookie = 0; - return (ENOENT); + rc = ENOENT; + goto err; } goto refill; } @@ -842,4 +850,11 @@ nfs_readdir(struct open_file *f, struct dirent *d) pos += 2; buf = (u_char *)&rent->nameplus[pos]; return (0); + +err: + free(pkt); + pkt = NULL; + pfp = NULL; + cookie = 0; + return (rc); } diff --git a/lib/libstand/rarp.c b/lib/libstand/rarp.c index 07ec1ccd4c7..3b8088e5358 100644 --- a/lib/libstand/rarp.c +++ b/lib/libstand/rarp.c @@ -54,17 +54,17 @@ __FBSDID("$FreeBSD$"); static ssize_t rarpsend(struct iodesc *, void *, size_t); -static ssize_t rarprecv(struct iodesc *, void *, size_t, time_t); +static ssize_t rarprecv(struct iodesc *, void **, void **, time_t); /* * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826). */ int -rarp_getipaddress(sock) - int sock; +rarp_getipaddress(int sock) { struct iodesc *d; struct ether_arp *ap; + void *pkt; struct { u_char header[ETHER_SIZE]; struct { @@ -72,13 +72,6 @@ rarp_getipaddress(sock) u_char pad[18]; /* 60 - sizeof(arp) */ } data; } wbuf; - struct { - u_char header[ETHER_SIZE]; - struct { - struct ether_arp arp; - u_char pad[24]; /* extra space */ - } data; - } rbuf; #ifdef RARP_DEBUG if (debug) @@ -102,21 +95,21 @@ rarp_getipaddress(sock) ap->arp_op = htons(ARPOP_REVREQUEST); bcopy(d->myea, ap->arp_sha, 6); bcopy(d->myea, ap->arp_tha, 6); + pkt = NULL; if (sendrecv(d, rarpsend, &wbuf.data, sizeof(wbuf.data), - rarprecv, &rbuf.data, sizeof(rbuf.data)) < 0) - { + rarprecv, &pkt, (void *)&ap) < 0) { printf("No response for RARP request\n"); return (-1); } - ap = &rbuf.data.arp; bcopy(ap->arp_tpa, (char *)&myip, sizeof(myip)); #if 0 /* XXX - Can NOT assume this is our root server! */ bcopy(ap->arp_spa, (char *)&rootip, sizeof(rootip)); #endif + free(pkt); /* Compute our "natural" netmask. */ if (IN_CLASSA(myip.s_addr)) @@ -134,10 +127,7 @@ rarp_getipaddress(sock) * Broadcast a RARP request (i.e. who knows who I am) */ static ssize_t -rarpsend(d, pkt, len) - struct iodesc *d; - void *pkt; - size_t len; +rarpsend(struct iodesc *d, void *pkt, size_t len) { #ifdef RARP_DEBUG @@ -153,28 +143,26 @@ rarpsend(d, pkt, len) * else -1 (and errno == 0) */ static ssize_t -rarprecv(d, pkt, len, tleft) - struct iodesc *d; - void *pkt; - size_t len; - time_t tleft; +rarprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; struct ether_arp *ap; - u_int16_t etype; /* host order */ + void *ptr = NULL; + uint16_t etype; /* host order */ #ifdef RARP_DEBUG if (debug) printf("rarprecv: "); #endif - n = readether(d, pkt, len, tleft, &etype); + n = readether(d, &ptr, (void **)&ap, tleft, &etype); errno = 0; /* XXX */ if (n == -1 || n < sizeof(struct ether_arp)) { #ifdef RARP_DEBUG if (debug) printf("bad len=%d\n", n); #endif + free(ptr); return (-1); } @@ -183,10 +171,10 @@ rarprecv(d, pkt, len, tleft) if (debug) printf("bad type=0x%x\n", etype); #endif + free(ptr); return (-1); } - ap = (struct ether_arp *)pkt; if (ap->arp_hrd != htons(ARPHRD_ETHER) || ap->arp_pro != htons(ETHERTYPE_IP) || ap->arp_hln != sizeof(ap->arp_sha) || @@ -196,6 +184,7 @@ rarprecv(d, pkt, len, tleft) if (debug) printf("bad hrd/pro/hln/pln\n"); #endif + free(ptr); return (-1); } @@ -204,6 +193,7 @@ rarprecv(d, pkt, len, tleft) if (debug) printf("bad op=0x%x\n", ntohs(ap->arp_op)); #endif + free(ptr); return (-1); } @@ -213,6 +203,7 @@ rarprecv(d, pkt, len, tleft) if (debug) printf("unwanted address\n"); #endif + free(ptr); return (-1); } @@ -221,5 +212,7 @@ rarprecv(d, pkt, len, tleft) if (debug) printf("got it\n"); #endif + *pkt = ptr; + *payload = ap; return (n); } diff --git a/lib/libstand/rpc.c b/lib/libstand/rpc.c index ee155f6ee04..94e6ce6b7e9 100644 --- a/lib/libstand/rpc.c +++ b/lib/libstand/rpc.c @@ -97,7 +97,7 @@ struct rpc_reply { }; /* Local forwards */ -static ssize_t recvrpc(struct iodesc *, void *, size_t, time_t); +static ssize_t recvrpc(struct iodesc *, void **, void **, time_t); static int rpc_getport(struct iodesc *, n_long, n_long); int rpc_xid; @@ -109,14 +109,14 @@ int rpc_port = 0x400; /* predecrement */ */ ssize_t rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, - void *sdata, size_t slen, void *rdata, size_t rlen) + void *sdata, size_t slen, void **rdata, void **pkt) { - ssize_t cc; + ssize_t cc, rsize; struct auth_info *auth; struct rpc_call *call; struct rpc_reply *reply; char *send_head, *send_tail; - char *recv_head, *recv_tail; + void *ptr; n_long x; int port; /* host order */ @@ -145,7 +145,6 @@ rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, auth->authtype = htonl(RPCAUTH_NULL); auth->authlen = 0; -#if 1 /* Auth credentials: always auth unix (as root) */ send_head -= sizeof(struct auth_unix); bzero(send_head, sizeof(struct auth_unix)); @@ -153,13 +152,6 @@ rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, auth = (struct auth_info *)send_head; auth->authtype = htonl(RPCAUTH_UNIX); auth->authlen = htonl(sizeof(struct auth_unix)); -#else - /* Auth credentials: always auth_null (XXX OK?) */ - send_head -= sizeof(*auth); - auth = send_head; - auth->authtype = htonl(RPCAUTH_NULL); - auth->authlen = 0; -#endif /* RPC call structure. */ send_head -= sizeof(*call); @@ -172,34 +164,28 @@ rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, call->rp_vers = htonl(vers); call->rp_proc = htonl(proc); - /* Make room for the rpc_reply header. */ - recv_head = rdata; - recv_tail = (char *)rdata + rlen; - recv_head -= sizeof(*reply); - + ptr = NULL; cc = sendrecv(d, sendudp, send_head, send_tail - send_head, - recvrpc, recv_head, recv_tail - recv_head); + recvrpc, &ptr, (void **)&reply); #ifdef RPC_DEBUG if (debug) - printf("callrpc: cc=%ld rlen=%lu\n", (long)cc, (u_long)rlen); + printf("callrpc: cc=%zd\n", cc); #endif if (cc == -1) return (-1); if (cc <= sizeof(*reply)) { errno = EBADRPC; + free(ptr); return (-1); } - recv_tail = recv_head + cc; - /* * Check the RPC reply status. * The xid, dir, astatus were already checked. */ - reply = (struct rpc_reply *)recv_head; auth = &reply->rp_u.rpu_rok.rok_auth; x = ntohl(auth->authlen); if (x != 0) { @@ -208,17 +194,21 @@ rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, printf("callrpc: reply auth != NULL\n"); #endif errno = EBADRPC; - return(-1); + free(ptr); + return (-1); } x = ntohl(reply->rp_u.rpu_rok.rok_status); if (x != 0) { printf("callrpc: error = %ld\n", (long)x); errno = EBADRPC; - return(-1); + free(ptr); + return (-1); } - recv_head += sizeof(*reply); - return (ssize_t)(recv_tail - recv_head); + rsize = cc - sizeof(*reply); + *rdata = (void *)((uintptr_t)reply + sizeof(*reply)); + *pkt = ptr; + return (rsize); } /* @@ -227,8 +217,9 @@ rpc_call(struct iodesc *d, n_long prog, n_long vers, n_long proc, * Remaining checks are done by callrpc */ static ssize_t -recvrpc(struct iodesc *d, void *pkt, size_t len, time_t tleft) +recvrpc(struct iodesc *d, void **pkt, void **payload, time_t tleft) { + void *ptr; struct rpc_reply *reply; ssize_t n; int x; @@ -236,14 +227,15 @@ recvrpc(struct iodesc *d, void *pkt, size_t len, time_t tleft) errno = 0; #ifdef RPC_DEBUG if (debug) - printf("recvrpc: called len=%lu\n", (u_long)len); + printf("recvrpc: called\n"); #endif - n = readudp(d, pkt, len, tleft); - if (n <= (4 * 4)) - return -1; - - reply = (struct rpc_reply *)pkt; + ptr = NULL; + n = readudp(d, &ptr, (void **)&reply, tleft); + if (n <= (4 * 4)) { + free(ptr); + return (-1); + } x = ntohl(reply->rp_xid); if (x != rpc_xid) { @@ -251,7 +243,8 @@ recvrpc(struct iodesc *d, void *pkt, size_t len, time_t tleft) if (debug) printf("recvrpc: rp_xid %d != xid %d\n", x, rpc_xid); #endif - return -1; + free(ptr); + return (-1); } x = ntohl(reply->rp_direction); @@ -260,16 +253,20 @@ recvrpc(struct iodesc *d, void *pkt, size_t len, time_t tleft) if (debug) printf("recvrpc: rp_direction %d != REPLY\n", x); #endif - return -1; + free(ptr); + return (-1); } x = ntohl(reply->rp_astatus); if (x != RPC_MSGACCEPTED) { errno = ntohl(reply->rp_u.rpu_errno); printf("recvrpc: reject, astat=%d, errno=%d\n", x, errno); - return -1; + free(ptr); + return (-1); } + *pkt = ptr; + *payload = reply; /* Return data count (thus indicating success) */ return (n); } @@ -387,11 +384,7 @@ rpc_getport(struct iodesc *d, n_long prog, n_long vers) n_long h[RPC_HEADER_WORDS]; struct args d; } sdata; - struct { - n_long h[RPC_HEADER_WORDS]; - struct res d; - n_long pad; - } rdata; + void *pkt; ssize_t cc; int port; @@ -416,16 +409,18 @@ rpc_getport(struct iodesc *d, n_long prog, n_long vers) args->vers = htonl(vers); args->proto = htonl(IPPROTO_UDP); args->port = 0; - res = &rdata.d; + pkt = NULL; cc = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, - args, sizeof(*args), res, sizeof(*res)); + args, sizeof(*args), (void **)&res, &pkt); if (cc < sizeof(*res)) { printf("getport: %s", strerror(errno)); errno = EBADRPC; + free(pkt); return (-1); } port = (int)ntohl(res->port); + free(pkt); rpc_pmap_putcache(d->destip, prog, vers, port); diff --git a/lib/libstand/rpc.h b/lib/libstand/rpc.h index e06ce416921..6dda0604828 100644 --- a/lib/libstand/rpc.h +++ b/lib/libstand/rpc.h @@ -48,7 +48,7 @@ /* RPC functions: */ ssize_t rpc_call(struct iodesc *, n_long, n_long, n_long, - void *, size_t, void *, size_t); + void *, size_t, void **, void **); void rpc_fromaddr(void *, struct in_addr *, u_short *); int rpc_pmap_getcache(struct in_addr, u_int, u_int); void rpc_pmap_putcache(struct in_addr, u_int, u_int, int); diff --git a/lib/libstand/stand.h b/lib/libstand/stand.h index 537fca75995..d09cd3e9ed8 100644 --- a/lib/libstand/stand.h +++ b/lib/libstand/stand.h @@ -194,6 +194,9 @@ extern struct open_file files[]; #define F_WRITE 0x0002 /* file opened for writing */ #define F_RAW 0x0004 /* raw device open - no file system */ #define F_NODEV 0x0008 /* network open - no device */ +#define F_MASK 0xFFFF +/* Mode modifier for strategy() */ +#define F_NORA (0x01 << 16) /* Disable Read-Ahead */ #define isascii(c) (((c) & ~0x7F) == 0) diff --git a/lib/libstand/tftp.c b/lib/libstand/tftp.c index b3d2da7b0ed..922142c2a80 100644 --- a/lib/libstand/tftp.c +++ b/lib/libstand/tftp.c @@ -73,8 +73,8 @@ static int tftp_stat(struct open_file *f, struct stat *sb); static ssize_t sendrecv_tftp(struct tftp_handle *h, ssize_t (*sproc)(struct iodesc *, void *, size_t), void *sbuf, size_t ssize, - ssize_t (*rproc)(struct tftp_handle *h, void *, ssize_t, time_t, unsigned short *), - void *rbuf, size_t rsize, unsigned short *rtype); + ssize_t (*rproc)(struct tftp_handle *h, void **, void **, time_t, unsigned short *), + void **, void **, unsigned short *rtype); struct fs_ops tftp_fsops = { "tftp", @@ -114,11 +114,8 @@ struct tftp_handle { char *path; /* saved for re-requests */ unsigned int tftp_blksize; unsigned long tftp_tsize; - struct { - u_char header[HEADER_SIZE]; - struct tftphdr t; - u_char space[TFTP_MAX_BLKSIZE]; - } __packed __aligned(4) lastdata; + void *pkt; + struct tftphdr *tftp_hdr; }; #define TFTP_MAX_ERRCODE EOPTNEG @@ -181,20 +178,23 @@ tftp_sendack(struct tftp_handle *h) } static ssize_t -recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, +recvtftp(struct tftp_handle *h, void **pkt, void **payload, time_t tleft, unsigned short *rtype) { struct iodesc *d = h->iodesc; struct tftphdr *t; + void *ptr = NULL; + ssize_t len; errno = 0; - len = readudp(d, pkt, len, tleft); + len = readudp(d, &ptr, (void **)&t, tleft); - if (len < 4) + if (len < 4) { + free(ptr); return (-1); + } - t = (struct tftphdr *) pkt; *rtype = ntohs(t->th_opcode); switch (ntohs(t->th_opcode)) { case DATA: { @@ -204,6 +204,7 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, /* * Expected block? */ + free(ptr); return (-1); } if (d->xid == 1) { @@ -211,11 +212,13 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, * First data packet from new port. */ struct udphdr *uh; - uh = (struct udphdr *) pkt - 1; + uh = (struct udphdr *) t - 1; d->destport = uh->uh_sport; } /* else check uh_sport has not changed??? */ - got = len - (t->th_data - (char *) t); - return got; + got = len - (t->th_data - (char *)t); + *pkt = ptr; + *payload = t; + return (got); } case ERROR: if ((unsigned) ntohs(t->th_code) > TFTP_MAX_ERRCODE) { @@ -227,6 +230,7 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, #endif errno = tftperrors[ntohs(t->th_code)]; } + free(ptr); return (-1); case OACK: { struct udphdr *uh; @@ -237,6 +241,7 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, * Drop the pkt. */ if (d->xid != 1) { + free(ptr); return (-1); } @@ -244,7 +249,7 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, * Remember which port this OACK came from, because we need * to send the ACK or errors back to it. */ - uh = (struct udphdr *) pkt - 1; + uh = (struct udphdr *) t - 1; d->destport = uh->uh_sport; /* Parse options ACK-ed by the server. */ @@ -252,14 +257,18 @@ recvtftp(struct tftp_handle *h, void *pkt, ssize_t len, time_t tleft, if (tftp_parse_oack(h, t->th_u.tu_stuff, tftp_oack_len) != 0) { tftp_senderr(h, EOPTNEG, "Malformed OACK"); errno = EIO; + free(ptr); return (-1); } + *pkt = ptr; + *payload = t; return (0); } default: #ifdef TFTP_DEBUG printf("tftp type %d not handled\n", ntohs(t->th_opcode)); #endif + free(ptr); return (-1); } } @@ -276,6 +285,7 @@ tftp_makereq(struct tftp_handle *h) char *wtail; int l; ssize_t res; + void *pkt; struct tftphdr *t; char *tftp_blksize = NULL; int blksize_l; @@ -314,8 +324,6 @@ tftp_makereq(struct tftp_handle *h) bcopy("0", wtail, 2); wtail += 2; - t = &h->lastdata.t; - /* h->iodesc->myport = htons(--tftpport); */ h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff)); h->iodesc->destport = htons(IPPORT_TFTP); @@ -325,8 +333,17 @@ tftp_makereq(struct tftp_handle *h) h->islastblock = 0; h->validsize = 0; + pkt = NULL; res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, - &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype); + &recvtftp, &pkt, (void **)&t, &rtype); + if (res == -1) { + free(pkt); + return (errno); + } + + free(h->pkt); + h->pkt = pkt; + h->tftp_hdr = t; if (rtype == OACK) return (tftp_getnextblock(h)); @@ -362,6 +379,7 @@ tftp_getnextblock(struct tftp_handle *h) } __packed __aligned(4) wbuf; char *wtail; int res; + void *pkt; struct tftphdr *t; unsigned short rtype = 0; wbuf.t.th_opcode = htons((u_short) ACK); @@ -369,16 +387,20 @@ tftp_getnextblock(struct tftp_handle *h) wbuf.t.th_block = htons((u_short) h->currblock); wtail += 2; - t = &h->lastdata.t; - h->iodesc->xid = h->currblock + 1; /* expected block */ + pkt = NULL; res = sendrecv_tftp(h, &sendudp, &wbuf.t, wtail - (char *) &wbuf.t, - &recvtftp, t, sizeof(*t) + h->tftp_blksize, &rtype); + &recvtftp, &pkt, (void **)&t, &rtype); - if (res == -1) /* 0 is OK! */ + if (res == -1) { /* 0 is OK! */ + free(pkt); return (errno); + } + free(h->pkt); + h->pkt = pkt; + h->tftp_hdr = t; h->currblock++; h->validsize = res; if (res < h->tftp_blksize) @@ -405,14 +427,8 @@ tftp_open(const char *path, struct open_file *f) if (netproto != NET_TFTP) return (EINVAL); - if (strcmp(f->f_dev->dv_name, "net") != 0) { -#ifdef __i386__ - if (strcmp(f->f_dev->dv_name, "pxe") != 0) - return (EINVAL); -#else + if (f->f_dev->dv_type != DEVT_NET) return (EINVAL); -#endif - } if (is_open) return (EBUSY); @@ -507,7 +523,7 @@ tftp_read(struct open_file *f, void *addr, size_t size, return (EINVAL); } count = (size < inbuffer ? size : inbuffer); - bcopy(tftpfile->lastdata.t.th_data + offinblock, + bcopy(tftpfile->tftp_hdr->th_data + offinblock, addr, count); addr = (char *)addr + count; @@ -540,6 +556,7 @@ tftp_close(struct open_file *f) if (tftpfile) { free(tftpfile->path); + free(tftpfile->pkt); free(tftpfile); } is_open = 0; @@ -591,8 +608,9 @@ static ssize_t sendrecv_tftp(struct tftp_handle *h, ssize_t (*sproc)(struct iodesc *, void *, size_t), void *sbuf, size_t ssize, - ssize_t (*rproc)(struct tftp_handle *, void *, ssize_t, time_t, unsigned short *), - void *rbuf, size_t rsize, unsigned short *rtype) + ssize_t (*rproc)(struct tftp_handle *, void **, void **, time_t, + unsigned short *), + void **pkt, void **payload, unsigned short *rtype) { struct iodesc *d = h->iodesc; ssize_t cc; @@ -624,7 +642,7 @@ sendrecv_tftp(struct tftp_handle *h, recvnext: /* Try to get a packet and process it. */ - cc = (*rproc)(h, rbuf, rsize, tleft, rtype); + cc = (*rproc)(h, pkt, payload, tleft, rtype); /* Return on data, EOF or real error. */ if (cc != -1 || errno != 0) return (cc); diff --git a/lib/libstand/udp.c b/lib/libstand/udp.c index d41be902a3f..8e52f29e296 100644 --- a/lib/libstand/udp.c +++ b/lib/libstand/udp.c @@ -59,15 +59,11 @@ __FBSDID("$FreeBSD$"); /* Caller must leave room for ethernet, ip and udp headers in front!! */ ssize_t -sendudp(d, pkt, len) - struct iodesc *d; - void *pkt; - size_t len; +sendudp(struct iodesc *d, void *pkt, size_t len) { ssize_t cc; - struct ip *ip; + struct udpiphdr *ui; struct udphdr *uh; - u_char *ea; #ifdef NET_DEBUG if (debug) { @@ -81,167 +77,76 @@ sendudp(d, pkt, len) } #endif - uh = (struct udphdr *)pkt - 1; - ip = (struct ip *)uh - 1; - len += sizeof(*ip) + sizeof(*uh); - - bzero(ip, sizeof(*ip) + sizeof(*uh)); + ui = (struct udpiphdr *)pkt - 1; + bzero(ui, sizeof(*ui)); - ip->ip_v = IPVERSION; /* half-char */ - ip->ip_hl = sizeof(*ip) >> 2; /* half-char */ - ip->ip_len = htons(len); - ip->ip_p = IPPROTO_UDP; /* char */ - ip->ip_ttl = IPDEFTTL; /* char */ - ip->ip_src = d->myip; - ip->ip_dst = d->destip; - ip->ip_sum = in_cksum(ip, sizeof(*ip)); /* short, but special */ + uh = (struct udphdr *)pkt - 1; + len += sizeof(*uh); uh->uh_sport = d->myport; uh->uh_dport = d->destport; - uh->uh_ulen = htons(len - sizeof(*ip)); + uh->uh_ulen = htons(len); -#ifndef UDP_NO_CKSUM - { - struct udpiphdr *ui; - struct ip tip; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = uh->uh_ulen; + ui->ui_src = d->myip; + ui->ui_dst = d->destip; - /* Calculate checksum (must save and restore ip header) */ - tip = *ip; - ui = (struct udpiphdr *)ip; - bzero(&ui->ui_x1, sizeof(ui->ui_x1)); - ui->ui_len = uh->uh_ulen; - uh->uh_sum = in_cksum(ui, len); - *ip = tip; - } +#ifndef UDP_NO_CKSUM + uh->uh_sum = in_cksum(ui, len + sizeof (struct ip)); #endif - if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_src.s_addr == 0 || - netmask == 0 || SAMENET(ip->ip_src, ip->ip_dst, netmask)) - ea = arpwhohas(d, ip->ip_dst); - else - ea = arpwhohas(d, gateip); - - cc = sendether(d, ip, len, ea, ETHERTYPE_IP); + cc = sendip(d, uh, len, IPPROTO_UDP); if (cc == -1) return (-1); if (cc != len) panic("sendudp: bad write (%zd != %zd)", cc, len); - return (cc - (sizeof(*ip) + sizeof(*uh))); + return (cc - sizeof(*uh)); } /* * Receive a UDP packet and validate it is for us. - * Caller leaves room for the headers (Ether, IP, UDP) */ ssize_t -readudp(d, pkt, len, tleft) - struct iodesc *d; - void *pkt; - size_t len; - time_t tleft; +readudp(struct iodesc *d, void **pkt, void **payload, time_t tleft) { ssize_t n; - size_t hlen; - struct ip *ip; struct udphdr *uh; - u_int16_t etype; /* host order */ + void *ptr; #ifdef NET_DEBUG if (debug) printf("readudp: called\n"); #endif - uh = (struct udphdr *)pkt - 1; - ip = (struct ip *)uh - 1; - - n = readether(d, ip, len + sizeof(*ip) + sizeof(*uh), tleft, &etype); - if (n == -1 || n < sizeof(*ip) + sizeof(*uh)) - return -1; - - /* Ethernet address checks now in readether() */ - - /* Need to respond to ARP requests. */ - if (etype == ETHERTYPE_ARP) { - struct arphdr *ah = (void *)ip; - if (ah->ar_op == htons(ARPOP_REQUEST)) { - /* Send ARP reply */ - arp_reply(d, ah); - } - return -1; - } - - if (etype != ETHERTYPE_IP) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: not IP. ether_type=%x\n", etype); -#endif - return -1; - } - - /* Check ip header */ - if (ip->ip_v != IPVERSION || - ip->ip_p != IPPROTO_UDP) { /* half char */ -#ifdef NET_DEBUG - if (debug) - printf("readudp: IP version or not UDP. ip_v=%d ip_p=%d\n", ip->ip_v, ip->ip_p); -#endif - return -1; - } - - hlen = ip->ip_hl << 2; - if (hlen < sizeof(*ip) || - in_cksum(ip, hlen) != 0) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: short hdr or bad cksum.\n"); -#endif - return -1; - } - if (n < ntohs(ip->ip_len)) { -#ifdef NET_DEBUG - if (debug) - printf("readudp: bad length %d < %d.\n", - (int)n, ntohs(ip->ip_len)); -#endif - return -1; - } - if (d->myip.s_addr && ip->ip_dst.s_addr != d->myip.s_addr) { -#ifdef NET_DEBUG - if (debug) { - printf("readudp: bad saddr %s != ", inet_ntoa(d->myip)); - printf("%s\n", inet_ntoa(ip->ip_dst)); - } -#endif - return -1; + uh = NULL; + ptr = NULL; + n = readip(d, &ptr, (void **)&uh, tleft, IPPROTO_UDP); + if (n == -1 || n < sizeof(*uh) || n != ntohs(uh->uh_ulen)) { + free(ptr); + return (-1); } - /* If there were ip options, make them go away */ - if (hlen != sizeof(*ip)) { - bcopy(((u_char *)ip) + hlen, uh, len - hlen); - ip->ip_len = htons(sizeof(*ip)); - n -= hlen - sizeof(*ip); - } if (uh->uh_dport != d->myport) { #ifdef NET_DEBUG if (debug) printf("readudp: bad dport %d != %d\n", d->myport, ntohs(uh->uh_dport)); #endif - return -1; + free(ptr); + return (-1); } #ifndef UDP_NO_CKSUM if (uh->uh_sum) { struct udpiphdr *ui; + struct ip *ip; struct ip tip; n = ntohs(uh->uh_ulen) + sizeof(*ip); - if (n > RECV_SIZE - ETHER_SIZE) { - printf("readudp: huge packet, udp len %d\n", (int)n); - return -1; - } /* Check checksum (must save and restore ip header) */ + ip = (struct ip *)uh - 1; tip = *ip; ui = (struct udpiphdr *)ip; bzero(&ui->ui_x1, sizeof(ui->ui_x1)); @@ -251,8 +156,8 @@ readudp(d, pkt, len, tleft) if (debug) printf("readudp: bad cksum\n"); #endif - *ip = tip; - return -1; + free(ptr); + return (-1); } *ip = tip; } @@ -263,10 +168,13 @@ readudp(d, pkt, len, tleft) printf("readudp: bad udp len %d < %d\n", ntohs(uh->uh_ulen), (int)sizeof(*uh)); #endif - return -1; + free(ptr); + return (-1); } n = (n > (ntohs(uh->uh_ulen) - sizeof(*uh))) ? ntohs(uh->uh_ulen) - sizeof(*uh) : n; + *pkt = ptr; + *payload = (void *)((uintptr_t)uh + sizeof(*uh)); return (n); } diff --git a/share/mk/bsd.stand.mk b/share/mk/bsd.stand.mk index b4480d50de7..8e528f409ac 100644 --- a/share/mk/bsd.stand.mk +++ b/share/mk/bsd.stand.mk @@ -5,7 +5,10 @@ # CFLAGS+= -ffreestanding -Wformat -CFLAGS+= ${CFLAGS_NO_SIMD} -msoft-float -D_STANDALONE +CFLAGS+= ${CFLAGS_NO_SIMD} -D_STANDALONE +.if ${MACHINE_CPUARCH} != "aarch64" +CFLAGS+= -msoft-float +.endif .if ${MACHINE_CPUARCH} == "i386" CFLAGS.gcc+= -mpreferred-stack-boundary=2 diff --git a/sys/boot/common/bcache.c b/sys/boot/common/bcache.c index 3c1ee64792a..198dd5f3b7a 100644 --- a/sys/boot/common/bcache.c +++ b/sys/boot/common/bcache.c @@ -295,7 +295,11 @@ read_strategy(void *devdata, int rw, daddr_t blk, size_t size, * Our choice of 16 read ahead blocks will always fit inside the bcache. */ - ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size); + if ((rw & F_NORA) == F_NORA) + ra = 0; + else + ra = bc->bcache_nblks - BHASH(bc, p_blk + p_size); + if (ra != 0 && ra != bc->bcache_nblks) { /* do we have RA space? */ ra = MIN(bc->ra, ra - 1); ra = rounddown(ra, 16); /* multiple of 16 blocks */ @@ -316,6 +320,7 @@ read_strategy(void *devdata, int rw, daddr_t blk, size_t size, * in either case we should return the data in bcache and only * return error if there is no data. */ + rw &= F_MASK; result = dd->dv_strategy(dd->dv_devdata, rw, p_blk, p_size * bcache_blksize, p_buf, &r_size); @@ -381,10 +386,11 @@ bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, ((size * 2 / bcache_blksize) > bcache_nblks)) { DEBUG("bypass %zu from %qu", size / bcache_blksize, blk); bcache_bypasses++; + rw &= F_MASK; return (dd->dv_strategy(dd->dv_devdata, rw, blk, size, buf, rsize)); } - switch (rw) { + switch (rw & F_MASK) { case F_READ: nblk = size / bcache_blksize; if (size != 0 && nblk == 0) @@ -423,7 +429,7 @@ bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, return (ret); case F_WRITE: - return write_strategy(devdata, rw, blk, size, buf, rsize); + return write_strategy(devdata, F_WRITE, blk, size, buf, rsize); } return -1; } diff --git a/sys/boot/common/commands.c b/sys/boot/common/commands.c index 2b0e8cbe0ac..def7ff28464 100644 --- a/sys/boot/common/commands.c +++ b/sys/boot/common/commands.c @@ -132,7 +132,7 @@ command_help(int argc, char *argv[]) char *topic, *subtopic, *t, *s, *d; /* page the help text from our load path */ - sprintf(buf, "%s/boot/loader.help", getenv("loaddev")); + snprintf(buf, sizeof(buf), "%s/boot/loader.help", getenv("loaddev")); if ((hfd = open(buf, O_RDONLY)) < 0) { printf("Verbose help not available, use '?' to list commands\n"); return(CMD_OK); diff --git a/sys/boot/common/dev_net.c b/sys/boot/common/dev_net.c index 1795767dc5b..c5b1e0abd92 100644 --- a/sys/boot/common/dev_net.c +++ b/sys/boot/common/dev_net.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -79,7 +80,7 @@ static int net_init(void); static int net_open(struct open_file *, ...); static int net_close(struct open_file *); static void net_cleanup(void); -static int net_strategy(); +static int net_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int net_print(int); static int net_getparams(int sock); @@ -96,6 +97,14 @@ struct devsw netdev = { net_cleanup }; +static struct uri_scheme { + const char *scheme; + int proto; +} uri_schemes[] = { + { "tftp:/", NET_TFTP }, + { "nfs:/", NET_NFS }, +}; + static int net_init(void) { @@ -216,7 +225,8 @@ net_cleanup(void) } static int -net_strategy() +net_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, + size_t *rsize) { return (EIO); @@ -255,7 +265,7 @@ net_getparams(int sock) * use RARP and RPC/bootparam (the Sun way) to get them. */ if (try_bootp) - bootp(sock, BOOTP_NONE); + bootp(sock); if (myip.s_addr != 0) goto exit; #ifdef NETIF_DEBUG @@ -356,29 +366,69 @@ net_print(int verbose) } /* - * Strip the server's address off of the rootpath if present and return it in - * network byte order, leaving just the pathname part in the global rootpath. + * Parses the rootpath if present + * + * The rootpath format can be in the form + * ://ip/path + * :/path + * + * For compatibility with previous behaviour it also accepts as an NFS scheme + * ip:/path + * /path + * + * If an ip is set it returns it in network byte order. + * The default scheme defined in the global netproto, if not set it defaults to + * NFS. + * It leaves just the pathname in the global rootpath. */ uint32_t net_parse_rootpath() { - int i; - n_long addr = INADDR_NONE; + n_long addr = htonl(INADDR_NONE); + size_t i; + char ip[FNAME_SIZE]; + char *ptr, *val; - netproto = NET_NFS; + netproto = NET_NONE; - if (tftpip.s_addr != 0) { - netproto = NET_TFTP; - addr = tftpip.s_addr; - } + for (i = 0; i < nitems(uri_schemes); i++) { + if (strncmp(rootpath, uri_schemes[i].scheme, + strlen(uri_schemes[i].scheme)) != 0) + continue; - for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) - if (rootpath[i] == ':') - break; - if (i && i != FNAME_SIZE && rootpath[i] == ':') { - rootpath[i++] = '\0'; - addr = inet_addr(&rootpath[0]); - bcopy(&rootpath[i], rootpath, strlen(&rootpath[i])+1); + netproto = uri_schemes[i].proto; + break; + } + ptr = rootpath; + /* Fallback for compatibility mode */ + if (netproto == NET_NONE) { + netproto = NET_NFS; + (void)strsep(&ptr, ":"); + if (ptr != NULL) { + addr = inet_addr(rootpath); + bcopy(ptr, rootpath, strlen(ptr) + 1); + } + } else { + ptr += strlen(uri_schemes[i].scheme); + if (*ptr == '/') { + /* we are in the form ://, we do expect an ip */ + ptr++; + /* + * XXX when http will be there we will need to check for + * a port, but right now we do not need it yet + */ + val = strchr(ptr, '/'); + if (val != NULL) { + snprintf(ip, sizeof(ip), "%.*s", + (int)((uintptr_t)val - (uintptr_t)ptr), + ptr); + addr = inet_addr(ip); + bcopy(val, rootpath, strlen(val) + 1); + } + } else { + ptr--; + bcopy(ptr, rootpath, strlen(ptr) + 1); + } } return (addr); diff --git a/sys/boot/common/disk.c b/sys/boot/common/disk.c index e441df16a6b..e9496b2b9c0 100644 --- a/sys/boot/common/disk.c +++ b/sys/boot/common/disk.c @@ -87,7 +87,12 @@ ptblread(void *d, void *buf, size_t blocks, uint64_t offset) dev = (struct disk_devdesc *)d; od = (struct open_disk *)dev->d_opendata; - return (dev->d_dev->dv_strategy(dev, F_READ, offset, + + /* + * As the GPT backup partition is located at the end of the disk, + * to avoid reading past disk end, flag bcache not to use RA. + */ + return (dev->d_dev->dv_strategy(dev, F_READ | F_NORA, offset, blocks * od->sectorsize, (char *)buf, NULL)); } diff --git a/sys/boot/common/md.c b/sys/boot/common/md.c index 22a0e953ad5..bb469a27f62 100644 --- a/sys/boot/common/md.c +++ b/sys/boot/common/md.c @@ -106,7 +106,7 @@ md_strategy(void *devdata, int rw, daddr_t blk, size_t size, if (rsize != NULL) *rsize = size; - switch (rw) { + switch (rw & F_MASK) { case F_READ: bcopy(md_image.start + ofs, buf, size); return (0); diff --git a/sys/boot/common/part.c b/sys/boot/common/part.c index 307ced493cc..cdb1e006695 100644 --- a/sys/boot/common/part.c +++ b/sys/boot/common/part.c @@ -42,7 +42,7 @@ __FBSDID("$FreeBSD$"); #include #ifdef PART_DEBUG -#define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) +#define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) #else #define DEBUG(fmt, args...) #endif @@ -145,7 +145,7 @@ gpt_parttype(uuid_t type) return (PART_UNKNOWN); } -static struct gpt_hdr* +static struct gpt_hdr * gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, uint16_t sectorsize) { @@ -199,7 +199,7 @@ gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, } static int -gpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size, +gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size, uint64_t lba_last) { struct gpt_ent *ent; @@ -226,13 +226,13 @@ gpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size, return (0); } -static struct ptable* +static struct ptable * ptable_gptread(struct ptable *table, void *dev, diskread_t dread) { struct pentry *entry; struct gpt_hdr *phdr, hdr; struct gpt_ent *ent; - u_char *buf, *tbl; + uint8_t *buf, *tbl; uint64_t offset; int pri, sec; size_t size, i; @@ -379,7 +379,7 @@ mbr_parttype(uint8_t type) return (PART_UNKNOWN); } -static struct ptable* +static struct ptable * ptable_ebrread(struct ptable *table, void *dev, diskread_t dread) { struct dos_partition *dp; @@ -457,13 +457,13 @@ bsd_parttype(uint8_t type) return (PART_UNKNOWN); } -static struct ptable* +static struct ptable * ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) { struct disklabel *dl; struct partition *part; struct pentry *entry; - u_char *buf; + uint8_t *buf; uint32_t raw_offset; int i; @@ -539,12 +539,12 @@ vtoc8_parttype(uint16_t type) return (PART_UNKNOWN); } -static struct ptable* +static struct ptable * ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) { struct pentry *entry; struct vtoc8 *dl; - u_char *buf; + uint8_t *buf; uint16_t sum, heads, sectors; int i; @@ -603,13 +603,13 @@ ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) } #endif /* LOADER_VTOC8_SUPPORT */ -struct ptable* +struct ptable * ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, diskread_t *dread) { struct dos_partition *dp; struct ptable *table; - u_char *buf; + uint8_t *buf; int i, count; #ifdef LOADER_MBR_SUPPORT struct pentry *entry; @@ -795,14 +795,14 @@ ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) * 5: Active FAT/FAT32 slice * 6: non-active FAT/FAT32 slice */ -#define PREF_RAWDISK 0 -#define PREF_FBSD_ACT 1 -#define PREF_FBSD 2 -#define PREF_LINUX_ACT 3 -#define PREF_LINUX 4 -#define PREF_DOS_ACT 5 -#define PREF_DOS 6 -#define PREF_NONE 7 +#define PREF_RAWDISK 0 +#define PREF_FBSD_ACT 1 +#define PREF_FBSD 2 +#define PREF_LINUX_ACT 3 +#define PREF_LINUX 4 +#define PREF_DOS_ACT 5 +#define PREF_DOS 6 +#define PREF_NONE 7 int ptable_getbestpart(const struct ptable *table, struct ptable_entry *part) { @@ -884,12 +884,12 @@ ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) #endif #ifdef LOADER_VTOC8_SUPPORT if (table->type == PTABLE_VTOC8) - sprintf(name, "%c", (u_char) 'a' + + sprintf(name, "%c", (uint8_t) 'a' + entry->part.index); else #endif if (table->type == PTABLE_BSD) - sprintf(name, "%c", (u_char) 'a' + + sprintf(name, "%c", (uint8_t) 'a' + entry->part.index); if ((ret = iter(arg, name, &entry->part)) != 0) return (ret); diff --git a/sys/boot/efi/Makefile.inc b/sys/boot/efi/Makefile.inc index eac5992a4c4..405f4cd7601 100644 --- a/sys/boot/efi/Makefile.inc +++ b/sys/boot/efi/Makefile.inc @@ -9,9 +9,13 @@ CFLAGS+= -mno-aes # Options used when building app-specific efi components # See conf/kern.mk for the correct set of these -CFLAGS+= -ffreestanding -Wformat -msoft-float ${CFLAGS_NO_SIMD} +CFLAGS+= -ffreestanding -Wformat ${CFLAGS_NO_SIMD} LDFLAGS+= -nostdlib +.if ${MACHINE_CPUARCH} != "aarch64" +CFLAGS+= -msoft-float +.endif + .if ${MACHINE_CPUARCH} == "amd64" CFLAGS+= -fshort-wchar CFLAGS+= -mno-red-zone diff --git a/sys/boot/efi/boot1/Makefile b/sys/boot/efi/boot1/Makefile index 4e25f5169b5..567263bc3ae 100644 --- a/sys/boot/efi/boot1/Makefile +++ b/sys/boot/efi/boot1/Makefile @@ -71,7 +71,7 @@ LDSCRIPT= ${.CURDIR}/../loader/arch/${MACHINE}/ldscript.${MACHINE} LDFLAGS+= -Wl,-T${LDSCRIPT} -Wl,-Bsymbolic -shared .if ${MACHINE_CPUARCH} == "aarch64" -CFLAGS+= -msoft-float -mgeneral-regs-only +CFLAGS+= -mgeneral-regs-only .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" CFLAGS+= -fPIC diff --git a/sys/boot/efi/boot1/boot1.c b/sys/boot/efi/boot1/boot1.c index 5bbbaf5305e..fb60dcc7083 100644 --- a/sys/boot/efi/boot1/boot1.c +++ b/sys/boot/efi/boot1/boot1.c @@ -529,6 +529,7 @@ probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) EFI_STATUS status; BOOLEAN preferred; + preferred = FALSE; status = probe_handle(h, imgpath, &preferred); DPRINTF("probe: "); diff --git a/sys/boot/efi/boot1/zfs_module.c b/sys/boot/efi/boot1/zfs_module.c index 1fb1a0dbff7..bf861ccaaf7 100644 --- a/sys/boot/efi/boot1/zfs_module.c +++ b/sys/boot/efi/boot1/zfs_module.c @@ -40,6 +40,15 @@ static dev_info_t *devices; +uint64_t +ldi_get_size(void *priv) +{ + dev_info_t *devinfo = priv; + + return (devinfo->dev->Media->BlockSize * + (devinfo->dev->Media->LastBlock + 1)); +} + static int vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) { diff --git a/sys/boot/efi/fdt/Makefile b/sys/boot/efi/fdt/Makefile index 65f6e6336ba..56fc2997a13 100644 --- a/sys/boot/efi/fdt/Makefile +++ b/sys/boot/efi/fdt/Makefile @@ -10,9 +10,11 @@ WARNS?= 6 SRCS= efi_fdt.c -CFLAGS+= -ffreestanding -msoft-float +CFLAGS+= -ffreestanding .if ${MACHINE_CPUARCH} == "aarch64" CFLAGS+= -mgeneral-regs-only +.else +CFLAGS+= -msoft-float .endif CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ diff --git a/sys/boot/efi/include/efidef.h b/sys/boot/efi/include/efidef.h index a770f03a58d..9cc5cdcbe88 100644 --- a/sys/boot/efi/include/efidef.h +++ b/sys/boot/efi/include/efidef.h @@ -178,6 +178,7 @@ typedef enum { #define EFI_MEMORY_DESCRIPTOR_VERSION 1 typedef struct { UINT32 Type; // Field size is 32 bits followed by 32 bit pad + UINT32 Pad; EFI_PHYSICAL_ADDRESS PhysicalStart; // Field size is 64 bits EFI_VIRTUAL_ADDRESS VirtualStart; // Field size is 64 bits UINT64 NumberOfPages; // Field size is 64 bits diff --git a/sys/boot/efi/libefi/Makefile b/sys/boot/efi/libefi/Makefile index 33a8f3e7e04..b10ae64fc70 100644 --- a/sys/boot/efi/libefi/Makefile +++ b/sys/boot/efi/libefi/Makefile @@ -30,7 +30,7 @@ CWARNFLAGS.efipart.c+= -Wno-format CWARNFLAGS.env.c+= -Wno-format .if ${MACHINE_CPUARCH} == "aarch64" -CFLAGS+= -msoft-float -mgeneral-regs-only +CFLAGS+= -mgeneral-regs-only .endif .if ${MACHINE_ARCH} == "amd64" CFLAGS+= -fPIC -mno-red-zone diff --git a/sys/boot/efi/libefi/efinet.c b/sys/boot/efi/libefi/efinet.c index c598548ed9f..b5c8ae74ead 100644 --- a/sys/boot/efi/libefi/efinet.c +++ b/sys/boot/efi/libefi/efinet.c @@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include @@ -36,19 +37,17 @@ __FBSDID("$FreeBSD$"); #include #include -#include - #include #include static EFI_GUID sn_guid = EFI_SIMPLE_NETWORK_PROTOCOL; static void efinet_end(struct netif *); -static int efinet_get(struct iodesc *, void *, size_t, time_t); +static ssize_t efinet_get(struct iodesc *, void **, time_t); static void efinet_init(struct iodesc *, void *); static int efinet_match(struct netif *, void *); static int efinet_probe(struct netif *, void *); -static int efinet_put(struct iodesc *, void *, size_t); +static ssize_t efinet_put(struct iodesc *, void *, size_t); struct netif_driver efinetif = { .netif_bname = "efinet", @@ -113,7 +112,7 @@ efinet_probe(struct netif *nif, void *machdep_hint) return (0); } -static int +static ssize_t efinet_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; @@ -125,14 +124,14 @@ efinet_put(struct iodesc *desc, void *pkt, size_t len) if (net == NULL) return (-1); - status = net->Transmit(net, 0, len, pkt, 0, 0, 0); + status = net->Transmit(net, 0, len, pkt, NULL, NULL, NULL); if (status != EFI_SUCCESS) return (-1); /* Wait for the buffer to be transmitted */ do { buf = NULL; /* XXX Is this needed? */ - status = net->GetStatus(net, 0, &buf); + status = net->GetStatus(net, NULL, &buf); /* * XXX EFI1.1 and the E1000 card returns a different * address than we gave. Sigh. @@ -143,41 +142,42 @@ efinet_put(struct iodesc *desc, void *pkt, size_t len) return ((status == EFI_SUCCESS) ? len : -1); } -static int -efinet_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +static ssize_t +efinet_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; EFI_SIMPLE_NETWORK *net; EFI_STATUS status; UINTN bufsz; time_t t; - char buf[2048]; + char *buf, *ptr; + ssize_t ret = -1; net = nif->nif_devdata; if (net == NULL) - return (0); + return (ret); + + bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN; + buf = malloc(bufsz + ETHER_ALIGN); + if (buf == NULL) + return (ret); + ptr = buf + ETHER_ALIGN; - t = time(0); - while ((time(0) - t) < timeout) { - bufsz = sizeof(buf); - status = net->Receive(net, 0, &bufsz, buf, 0, 0, 0); + t = getsecs(); + while ((getsecs() - t) < timeout) { + status = net->Receive(net, NULL, &bufsz, ptr, NULL, NULL, NULL); if (status == EFI_SUCCESS) { - /* - * XXX EFI1.1 and the E1000 card trash our - * workspace if we do not do this silly copy. - * Either they are not respecting the len - * value or do not like the alignment. - */ - if (bufsz > len) - bufsz = len; - bcopy(buf, pkt, bufsz); - return (bufsz); + *pkt = buf; + ret = (ssize_t)bufsz; + break; } if (status != EFI_NOT_READY) - return (0); + break; } - return (0); + if (ret == -1) + free(buf); + return (ret); } static void @@ -205,8 +205,8 @@ efinet_init(struct iodesc *desc, void *machdep_hint) if (net->Mode->State == EfiSimpleNetworkStopped) { status = net->Start(net); if (status != EFI_SUCCESS) { - printf("net%d: cannot start interface (status=%ld)\n", - nif->nif_unit, (long)status); + printf("net%d: cannot start interface (status=%lu)\n", + nif->nif_unit, EFI_ERROR_CODE(status)); return; } } @@ -214,8 +214,8 @@ efinet_init(struct iodesc *desc, void *machdep_hint) if (net->Mode->State != EfiSimpleNetworkInitialized) { status = net->Initialize(net, 0, 0); if (status != EFI_SUCCESS) { - printf("net%d: cannot init. interface (status=%ld)\n", - nif->nif_unit, (long)status); + printf("net%d: cannot init. interface (status=%lu)\n", + nif->nif_unit, EFI_ERROR_CODE(status)); return; } } @@ -224,10 +224,10 @@ efinet_init(struct iodesc *desc, void *machdep_hint) UINT32 mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST; - status = net->ReceiveFilters(net, mask, 0, FALSE, 0, 0); + status = net->ReceiveFilters(net, mask, 0, FALSE, 0, NULL); if (status != EFI_SUCCESS) { - printf("net%d: cannot set rx. filters (status=%ld)\n", - nif->nif_unit, (long)status); + printf("net%d: cannot set rx. filters (status=%lu)\n", + nif->nif_unit, EFI_ERROR_CODE(status)); return; } } @@ -258,9 +258,9 @@ struct devsw efinet_dev = { .dv_name = "net", .dv_type = DEVT_NET, .dv_init = efinet_dev_init, - .dv_strategy = net_strategy, - .dv_open = net_open, - .dv_close = net_close, + .dv_strategy = NULL, /* Will be set in efinet_dev_init */ + .dv_open = NULL, /* Will be set in efinet_dev_init */ + .dv_close = NULL, /* Will be set in efinet_dev_init */ .dv_ioctl = noioctl, .dv_print = efinet_dev_print, .dv_cleanup = NULL @@ -277,13 +277,14 @@ efinet_dev_init() EFI_STATUS status; UINTN sz; int err, i, nifs; + extern struct devsw netdev; sz = 0; handles = NULL; - status = BS->LocateHandle(ByProtocol, &sn_guid, 0, &sz, 0); + status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL); if (status == EFI_BUFFER_TOO_SMALL) { handles = (EFI_HANDLE *)malloc(sz); - status = BS->LocateHandle(ByProtocol, &sn_guid, 0, &sz, + status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, handles); if (EFI_ERROR(status)) free(handles); @@ -313,10 +314,11 @@ efinet_dev_init() * pull packets off the network leading to lost packets. */ status = BS->OpenProtocol(handles[i], &sn_guid, (void **)&net, - IH, 0, EFI_OPEN_PROTOCOL_EXCLUSIVE); + IH, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE); if (status != EFI_SUCCESS) { printf("Unable to open network interface %d for " - "exclusive access: %d\n", i, EFI_ERROR(status)); + "exclusive access: %lu\n", i, + EFI_ERROR_CODE(status)); } handles2[nifs] = handles[i]; @@ -351,6 +353,11 @@ efinet_dev_init() dif->dif_stats = &stats[i]; dif->dif_private = handles2[i]; } + + efinet_dev.dv_open = netdev.dv_open; + efinet_dev.dv_close = netdev.dv_close; + efinet_dev.dv_strategy = netdev.dv_strategy; + done: free(handles2); return (err); diff --git a/sys/boot/efi/libefi/efipart.c b/sys/boot/efi/libefi/efipart.c index d4f7edff756..206a7d991e6 100644 --- a/sys/boot/efi/libefi/efipart.c +++ b/sys/boot/efi/libefi/efipart.c @@ -819,7 +819,7 @@ efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, if ((blk + nblks - 1) > blkio->Media->LastBlock) return (EIO); - switch (rw) { + switch (rw & F_MASK) { case F_READ: status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); diff --git a/sys/boot/efi/libefi/time.c b/sys/boot/efi/libefi/time.c index 99831e1cf1a..1f9ee6f58c0 100644 --- a/sys/boot/efi/libefi/time.c +++ b/sys/boot/efi/libefi/time.c @@ -230,5 +230,5 @@ time(time_t *tloc) time_t getsecs(void) { - return time(0); + return time(NULL); } diff --git a/sys/boot/efi/loader/Makefile b/sys/boot/efi/loader/Makefile index 23845aab2ca..0f54aa4f767 100644 --- a/sys/boot/efi/loader/Makefile +++ b/sys/boot/efi/loader/Makefile @@ -9,6 +9,7 @@ MK_SSP= no PROG= loader.sym INTERNALPROG= WARNS?= 3 +LOADER_NET_SUPPORT?= yes # architecture-specific loader code SRCS= autoload.c \ @@ -35,6 +36,10 @@ CWARNFLAGS.zfs.c+= -Wno-array-bounds CWARNFLAGS.zfs.c+= -Wno-missing-prototypes .endif +.if defined(LOADER_NET_SUPPORT) +CFLAGS+= -I${.CURDIR}/../../../../lib/libstand +.endif + .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201 CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized .endif diff --git a/sys/boot/efi/loader/arch/arm/start.S b/sys/boot/efi/loader/arch/arm/start.S index 9bc0c9f0f08..5b6182d67d0 100644 --- a/sys/boot/efi/loader/arch/arm/start.S +++ b/sys/boot/efi/loader/arch/arm/start.S @@ -42,7 +42,7 @@ #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 - .section .peheader + .section .peheader,"a" efi_start: /* The MS-DOS Stub, only used to get the offset of the COFF header */ .ascii "MZ" diff --git a/sys/boot/efi/loader/arch/arm64/Makefile.inc b/sys/boot/efi/loader/arch/arm64/Makefile.inc index d263db956a8..fbb75398fe9 100644 --- a/sys/boot/efi/loader/arch/arm64/Makefile.inc +++ b/sys/boot/efi/loader/arch/arm64/Makefile.inc @@ -8,7 +8,7 @@ SRCS+= exec.c \ CFLAGS+=-I${.CURDIR}/../../arm64/libarm64 SRCS+= cache.c -CFLAGS+= -msoft-float -mgeneral-regs-only +CFLAGS+= -mgeneral-regs-only CLEANFILES+= loader.help diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c index 0b3951156a1..542674414b8 100644 --- a/sys/boot/efi/loader/main.c +++ b/sys/boot/efi/loader/main.c @@ -28,6 +28,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include #include @@ -307,7 +308,9 @@ main(int argc, CHAR16 *argv[]) int i, j, vargood, howto; UINTN k; int has_kbd; +#if !defined(__arm__) char buf[40]; +#endif archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; @@ -481,6 +484,7 @@ main(int argc, CHAR16 *argv[]) for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; +#if !defined(__arm__) if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); @@ -488,6 +492,7 @@ main(int argc, CHAR16 *argv[]) smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } +#endif } interact(NULL); /* doesn't return */ @@ -506,8 +511,7 @@ command_reboot(int argc, char *argv[]) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); - RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, - (CHAR16 *)"Reboot from the loader"); + RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); @@ -795,6 +799,100 @@ command_fdt(int argc, char *argv[]) COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif +/* + * Chain load another efi loader. + */ +static int +command_chain(int argc, char *argv[]) +{ + EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; + EFI_HANDLE loaderhandle; + EFI_LOADED_IMAGE *loaded_image; + EFI_STATUS status; + struct stat st; + struct devdesc *dev; + char *name, *path; + void *buf; + int fd; + + if (argc < 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + name = argv[1]; + + if ((fd = open(name, O_RDONLY)) < 0) { + command_errmsg = "no such file"; + return (CMD_ERROR); + } + + if (fstat(fd, &st) < -1) { + command_errmsg = "stat failed"; + close(fd); + return (CMD_ERROR); + } + + status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf); + if (status != EFI_SUCCESS) { + command_errmsg = "failed to allocate buffer"; + close(fd); + return (CMD_ERROR); + } + if (read(fd, buf, st.st_size) != st.st_size) { + command_errmsg = "error while reading the file"; + (void)BS->FreePool(buf); + close(fd); + return (CMD_ERROR); + } + close(fd); + status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle); + (void)BS->FreePool(buf); + if (status != EFI_SUCCESS) { + command_errmsg = "LoadImage failed"; + return (CMD_ERROR); + } + status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID, + (void **)&loaded_image); + + if (argc > 2) { + int i, len = 0; + CHAR16 *argp; + + for (i = 2; i < argc; i++) + len += strlen(argv[i]) + 1; + + len *= sizeof (*argp); + loaded_image->LoadOptions = argp = malloc (len); + loaded_image->LoadOptionsSize = len; + for (i = 2; i < argc; i++) { + char *ptr = argv[i]; + while (*ptr) + *(argp++) = *(ptr++); + *(argp++) = ' '; + } + *(--argv) = 0; + } + + if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) + loaded_image->DeviceHandle = + efi_find_handle(dev->d_dev, dev->d_unit); + + dev_cleanup(); + status = BS->StartImage(loaderhandle, NULL, NULL); + if (status != EFI_SUCCESS) { + command_errmsg = "StartImage failed"; + free(loaded_image->LoadOptions); + loaded_image->LoadOptions = NULL; + status = BS->UnloadImage(loaded_image); + return (CMD_ERROR); + } + + return (CMD_ERROR); /* not reached */ +} + +COMMAND_SET(chain, "chain", "chain load file", command_chain); + #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void) @@ -803,39 +901,37 @@ efi_zfs_probe(void) pdinfo_t *hd, *pd = NULL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_LOADED_IMAGE *img; - EFI_HANDLE boot_disk = NULL; char devname[SPECNAMELEN + 1]; - uint64_t *guidp = NULL; BS->HandleProtocol(IH, &imgid, (VOID**)&img); - - /* Find the handle for the boot disk. */ hdi = efiblk_get_pdinfo_list(&efipart_hddev); - STAILQ_FOREACH(hd, hdi, pd_link) { - STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { - if (pd->pd_handle == img->DeviceHandle) - boot_disk = hd->pd_handle; - } - } /* - * We provide non-NULL guid pointer if the disk was used for boot, - * and reset after the first found pool. - * Technically this solution is not very correct, we assume the boot - * pool is the first pool on this disk. + * Find the handle for the boot device. The boot1 did find the + * device with loader binary, now we need to search for the + * same device and if it is part of the zfs pool, we record the + * pool GUID for currdev setup. */ - STAILQ_FOREACH(hd, hdi, pd_link) { - if (hd->pd_handle == boot_disk) - guidp = &pool_guid; - STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { + snprintf(devname, sizeof(devname), "%s%dp%d:", efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit); - (void) zfs_probe_dev(devname, guidp); - if (guidp != NULL && pool_guid != 0) - guidp = NULL; + if (pd->pd_handle == img->DeviceHandle) + (void) zfs_probe_dev(devname, &pool_guid); + else + (void) zfs_probe_dev(devname, NULL); } } } + +uint64_t +ldi_get_size(void *priv) +{ + int fd = (uintptr_t) priv; + uint64_t size; + + ioctl(fd, DIOCGMEDIASIZE, &size); + return (size); +} #endif diff --git a/sys/boot/forth/menu.rc b/sys/boot/forth/menu.rc index 3c7de7138b8..6fe3dfe1428 100644 --- a/sys/boot/forth/menu.rc +++ b/sys/boot/forth/menu.rc @@ -73,8 +73,23 @@ s" currdev" getenv dup 0> [if] drop 4 s" zfs:" compare 0= [if] set mainmenu_command[7]="3 goto_menu" set mainmenu_keycode[7]=101 set mainansi_caption[7]="Select Boot ^[1mE^[37mnvironment..." + + s" chain_disk" getenv? [if] + set mainmenu_caption[8]="Chain[L]oad ${chain_disk}" + set mainmenu_command[8]="chain ${chain_disk}" + set mainmenu_keycode[8]=108 + set mainansi_caption[8]="Chain^[1mL^[moad ${chain_disk}" + [then] +[else] + s" chain_disk" getenv? [if] + set mainmenu_caption[7]="Chain[L]oad ${chain_disk}" + set mainmenu_command[7]="chain ${chain_disk}" + set mainmenu_keycode[7]=108 + set mainansi_caption[7]="Chain^[1mL^[moad ${chain_disk}" + [then] [then] [else] drop [then] + \ \ BOOT OPTIONS MENU \ diff --git a/sys/boot/i386/boot0/Makefile b/sys/boot/i386/boot0/Makefile index db761c2f266..d0ce8e598c6 100644 --- a/sys/boot/i386/boot0/Makefile +++ b/sys/boot/i386/boot0/Makefile @@ -78,6 +78,3 @@ CFLAGS+=-DFLAGS=${BOOT_BOOT0_FLAGS} \ LDFLAGS=${LDFLAGS_BIN} .include - -# XXX: clang integrated-as doesn't grok .codeNN directives yet -CFLAGS.boot0.S= ${CLANG_NO_IAS} diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h index e437cb83bf9..c0995df45ea 100644 --- a/sys/boot/i386/common/drv.h +++ b/sys/boot/i386/common/drv.h @@ -36,7 +36,7 @@ struct dsk { unsigned int slice; int part; daddr_t start; - int init; + uint64_t size; }; int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index 88c1c07ab48..2a7d1e2213d 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -6,7 +6,7 @@ INTERNALLIB= SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ comconsole.c devicename.c elf32_freebsd.c \ - elf64_freebsd.c multiboot.c multiboot_tramp.S \ + elf64_freebsd.c multiboot.c multiboot_tramp.S relocater_tramp.S \ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c .PATH: ${.CURDIR}/../../zfs diff --git a/sys/boot/i386/libi386/bioscd.c b/sys/boot/i386/libi386/bioscd.c index ba3be09c877..2e8fc3b11b4 100644 --- a/sys/boot/i386/libi386/bioscd.c +++ b/sys/boot/i386/libi386/bioscd.c @@ -268,7 +268,7 @@ bc_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, return (EINVAL); #endif - if (rw != F_READ) + if ((rw & F_MASK) != F_READ) return(EROFS); dev = (struct i386_devdesc *)devdata; unit = dev->d_unit; diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c index 188b8ebb9de..b3597cccc06 100644 --- a/sys/boot/i386/libi386/biosdisk.c +++ b/sys/boot/i386/libi386/biosdisk.c @@ -618,7 +618,7 @@ bd_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, DEBUG("short read %d", blks); } - switch(rw){ + switch (rw & F_MASK) { case F_READ: DEBUG("read %d from %lld to %p", blks, dblk, buf); diff --git a/sys/boot/i386/libi386/biosmem.c b/sys/boot/i386/libi386/biosmem.c index abcbddbfa48..9da4dd2e7f4 100644 --- a/sys/boot/i386/libi386/biosmem.c +++ b/sys/boot/i386/libi386/biosmem.c @@ -200,7 +200,7 @@ bios_getmem(void) /* * If we have extended memory and did not find a suitable heap - * region in the SMAP, use the last 3MB of 'extended' memory as a + * region in the SMAP, use the last HEAP_MIN of 'extended' memory as a * high heap candidate. */ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h index 76f02718ccd..e65a060acb7 100644 --- a/sys/boot/i386/libi386/libi386.h +++ b/sys/boot/i386/libi386/libi386.h @@ -60,6 +60,38 @@ struct i386_devdesc } d_kind; }; +/* + * relocater trampoline support. + */ +struct relocate_data { + uint32_t src; + uint32_t dest; + uint32_t size; +}; + +extern void relocater(void); + +/* + * The relocater_data[] is fixed size array allocated in relocater_tramp.S + */ +extern struct relocate_data relocater_data[]; +extern uint32_t relocater_size; + +extern uint16_t relocator_ip; +extern uint16_t relocator_cs; +extern uint16_t relocator_ds; +extern uint16_t relocator_es; +extern uint16_t relocator_fs; +extern uint16_t relocator_gs; +extern uint16_t relocator_ss; +extern uint16_t relocator_sp; +extern uint32_t relocator_esi; +extern uint32_t relocator_eax; +extern uint32_t relocator_ebx; +extern uint32_t relocator_edx; +extern uint32_t relocator_ebp; +extern uint16_t relocator_a20_enabled; + int i386_getdev(void **vdev, const char *devspec, const char **path); char *i386_fmtdev(void *vdev); int i386_setcurrdev(struct env_var *ev, int flags, const void *value); diff --git a/sys/boot/i386/libi386/pxe.c b/sys/boot/i386/libi386/pxe.c index 31c89c202ca..7cb48331792 100644 --- a/sys/boot/i386/libi386/pxe.c +++ b/sys/boot/i386/libi386/pxe.c @@ -30,11 +30,15 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include +#include +#include #include #include +#include #include #include @@ -53,17 +57,15 @@ __FBSDID("$FreeBSD$"); * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. */ #define PXE_BUFFER_SIZE 0x2000 -#define PXE_TFTP_BUFFER_SIZE 512 static char scratch_buffer[PXE_BUFFER_SIZE]; static char data_buffer[PXE_BUFFER_SIZE]; static pxenv_t *pxenv_p = NULL; /* PXENV+ */ static pxe_t *pxe_p = NULL; /* !PXE */ -static BOOTPLAYER bootplayer; /* PXE Cached information. */ +#ifdef PXE_DEBUG static int pxe_debug = 0; -static int pxe_sock = -1; -static int pxe_opens = 0; +#endif void pxe_enable(void *pxeinfo); static void (*pxe_call)(int func); @@ -71,25 +73,17 @@ static void pxenv_call(int func); static void bangpxe_call(int func); static int pxe_init(void); -static int pxe_strategy(void *devdata, int flag, daddr_t dblk, - size_t size, char *buf, size_t *rsize); -static int pxe_open(struct open_file *f, ...); -static int pxe_close(struct open_file *f); static int pxe_print(int verbose); static void pxe_cleanup(void); -static void pxe_setnfshandle(char *rootpath); static void pxe_perror(int error); static int pxe_netif_match(struct netif *nif, void *machdep_hint); static int pxe_netif_probe(struct netif *nif, void *machdep_hint); static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); -static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, - time_t timeout); -static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); +static ssize_t pxe_netif_get(struct iodesc *, void **, time_t); +static ssize_t pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); static void pxe_netif_end(struct netif *nif); -int nfs_getrootfh(struct iodesc*, char*, uint32_t*, u_char*); - extern struct netif_stats pxe_st[]; extern u_int16_t __bangpxeseg; extern u_int16_t __bangpxeoff; @@ -97,25 +91,24 @@ extern void __bangpxeentry(void); extern u_int16_t __pxenvseg; extern u_int16_t __pxenvoff; extern void __pxenventry(void); -extern struct in_addr servip; struct netif_dif pxe_ifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ {0, 1, &pxe_st[0], 0} }; -struct netif_stats pxe_st[NENTS(pxe_ifs)]; +struct netif_stats pxe_st[nitems(pxe_ifs)]; struct netif_driver pxenetif = { - "pxenet", - pxe_netif_match, - pxe_netif_probe, - pxe_netif_init, - pxe_netif_get, - pxe_netif_put, - pxe_netif_end, - pxe_ifs, - NENTS(pxe_ifs) + .netif_bname = "pxenet", + .netif_match = pxe_netif_match, + .netif_probe = pxe_netif_probe, + .netif_init = pxe_netif_init, + .netif_get = pxe_netif_get, + .netif_put = pxe_netif_put, + .netif_end = pxe_netif_end, + .netif_ifs = pxe_ifs, + .netif_nifs = nitems(pxe_ifs) }; struct netif_driver *netif_drivers[] = { @@ -124,15 +117,15 @@ struct netif_driver *netif_drivers[] = { }; struct devsw pxedisk = { - "pxe", - DEVT_NET, - pxe_init, - pxe_strategy, - pxe_open, - pxe_close, - noioctl, - pxe_print, - pxe_cleanup + .dv_name = "net", + .dv_type = DEVT_NET, + .dv_init = pxe_init, + .dv_strategy = NULL, /* Will be set in pxe_init */ + .dv_open = NULL, /* Will be set in pxe_init */ + .dv_close = NULL, /* Will be set in pxe_init */ + .dv_ioctl = noioctl, + .dv_print = pxe_print, + .dv_cleanup = pxe_cleanup }; /* @@ -160,6 +153,7 @@ pxe_init(void) int counter; uint8_t checksum; uint8_t *checkptr; + extern struct devsw netdev; if (pxenv_p == NULL) return (0); @@ -215,7 +209,11 @@ pxe_init(void) break; } } - + + pxedisk.dv_open = netdev.dv_open; + pxedisk.dv_close = netdev.dv_close; + pxedisk.dv_strategy = netdev.dv_strategy; + printf("\nPXE version %d.%d, real mode entry point ", (uint8_t) (pxenv_p->Version >> 8), (uint8_t) (pxenv_p->Version & 0xFF)); @@ -236,192 +234,29 @@ pxe_init(void) pxe_p = NULL; return (0); } - bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), - &bootplayer, gci_p->BufferSize); - return (1); -} - - -static int -pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, - char *buf, size_t *rsize) -{ - return (EIO); -} - -static int -pxe_open(struct open_file *f, ...) -{ - va_list args; - char *devname; /* Device part of file name (or NULL). */ - char temp[FNAME_SIZE]; - int error = 0; - int i; - - va_start(args, f); - devname = va_arg(args, char*); - va_end(args); - - /* On first open, do netif open, mount, etc. */ - if (pxe_opens == 0) { - /* Find network interface. */ - if (pxe_sock < 0) { - pxe_sock = netif_open(devname); - if (pxe_sock < 0) { - printf("pxe_open: netif_open() failed\n"); - return (ENXIO); - } - if (pxe_debug) - printf("pxe_open: netif_open() succeeded\n"); - - if (socktodesc(pxe_sock) == NULL) { - printf("pxe_open: bad socket %d\n", pxe_sock); - return (ENXIO); - } - - } - if (rootip.s_addr == 0) { - /* - * Try to extract the RFC1048 data from PXE. - * If fail do a bootp/dhcp request to find out where our - * NFS/TFTP server is. Even if we dont get back - * the proper information, fall back to the server - * which brought us to life and a default rootpath. - */ - - if (dhcp_try_rfc1048(bootplayer.vendor.d, BOOTP_DHCPVEND) < 0) { - if (pxe_debug) - printf("pxe_open: no RFC1048 data in PXE Cache\n"); - bootp(pxe_sock, BOOTP_PXE); - } else if (pxe_debug) { - printf("pxe_open: loaded RFC1048 data from PXE Cache\n"); - } - -#ifdef LOADER_TFTP_SUPPORT - bootp(pxe_sock, BOOTP_PXE); -#endif - if (rootip.s_addr == 0) - rootip.s_addr = bootplayer.sip; - if (gateip.s_addr == 0) - gateip.s_addr = bootplayer.gip; - if (myip.s_addr == 0) - myip.s_addr = bootplayer.yip; - if (servip.s_addr == 0) - servip = rootip; - - netproto = NET_NFS; - if (tftpip.s_addr != 0) { - netproto = NET_TFTP; - rootip.s_addr = tftpip.s_addr; - } - - if (netproto == NET_NFS && !rootpath[0]) - strcpy(rootpath, PXENFSROOTPATH); - - for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) - if (rootpath[i] == ':') - break; - if (i && i != FNAME_SIZE && rootpath[i] == ':') { - rootpath[i++] = '\0'; - if (inet_addr(&rootpath[0]) != INADDR_NONE) - rootip.s_addr = inet_addr(&rootpath[0]); - bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i]) + 1); - bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i]) + 1); - } - setenv("boot.netif.ip", inet_ntoa(myip), 1); - setenv("boot.netif.netmask", intoa(netmask), 1); - setenv("boot.netif.gateway", inet_ntoa(gateip), 1); - setenv("boot.netif.server", inet_ntoa(rootip), 1); - if (bootplayer.Hardware == ETHER_TYPE) { - sprintf(temp, "%6D", bootplayer.CAddr, ":"); - setenv("boot.netif.hwaddr", temp, 1); - } - if (intf_mtu != 0) { - char mtu[16]; - snprintf(sizeof(mtu), mtu, "%u", intf_mtu); - setenv("boot.netif.mtu", mtu, 1); - } - printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); - printf("pxe_open: server path: %s\n", rootpath); - printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); - printf("pxe_open: my ip: %s\n", inet_ntoa(myip)); - printf("pxe_open: netmask: %s\n", intoa(netmask)); - printf("pxe_open: servip: %s\n", inet_ntoa(servip)); - - if (netproto == NET_TFTP) { - setenv("boot.tftproot.server", inet_ntoa(rootip), 1); - setenv("boot.tftproot.path", rootpath, 1); - } else if (netproto == NET_NFS) { - setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); - setenv("boot.nfsroot.path", rootpath, 1); - } - setenv("dhcp.host-name", hostname, 1); - - setenv("pxeboot.ip", inet_ntoa(myip), 1); - if (bootplayer.Hardware == ETHER_TYPE) { - sprintf(temp, "%6D", bootplayer.CAddr, ":"); - setenv("pxeboot.hwaddr", temp, 1); - } - } + free(bootp_response); + if ((bootp_response = malloc(gci_p->BufferSize)) != NULL) { + bootp_response_size = gci_p->BufferSize; + bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), + bootp_response, bootp_response_size); } - pxe_opens++; - f->f_devdata = &pxe_sock; - return (error); -} - -static int -pxe_close(struct open_file *f) -{ - -#ifdef PXE_DEBUG - if (pxe_debug) - printf("pxe_close: opens=%d\n", pxe_opens); -#endif - - /* On last close, do netif close, etc. */ - f->f_devdata = NULL; - /* Extra close call? */ - if (pxe_opens <= 0) - return (0); - pxe_opens--; - /* Not last close? */ - if (pxe_opens > 0) - return (0); - - if (netproto == NET_NFS) { - /* get an NFS filehandle for our root filesystem */ - pxe_setnfshandle(rootpath); - } - - if (pxe_sock >= 0) { - -#ifdef PXE_DEBUG - if (pxe_debug) - printf("pxe_close: calling netif_close()\n"); -#endif - netif_close(pxe_sock); - pxe_sock = -1; - } - return (0); + return (1); } static int pxe_print(int verbose) { - char line[255]; if (pxe_call == NULL) return (0); printf("%s devices:", pxedisk.dv_name); if (pager_output("\n") != 0) return (1); + printf(" %s0:", pxedisk.dv_name); if (verbose) { - snprintf(line, sizeof(line), " pxe0: %s:%s\n", - inet_ntoa(rootip), rootpath); - } else { - snprintf(line, sizeof(line), " pxe0:\n"); + printf(" %s:%s", inet_ntoa(rootip), rootpath); } - return (pager_output(line)); + return (pager_output("\n")); } static void @@ -460,66 +295,6 @@ pxe_perror(int err) return; } -/* - * Reach inside the libstand NFS code and dig out an NFS handle - * for the root filesystem. - */ -#define NFS_V3MAXFHSIZE 64 - -struct nfs_iodesc { - struct iodesc *iodesc; - off_t off; - uint32_t fhsize; - u_char fh[NFS_V3MAXFHSIZE]; - /* structure truncated */ -}; -extern struct nfs_iodesc nfs_root_node; -extern int rpc_port; - -static void -pxe_rpcmountcall() -{ - struct iodesc *d; - int error; - - if (!(d = socktodesc(pxe_sock))) - return; - d->myport = htons(--rpc_port); - d->destip = rootip; - if ((error = nfs_getrootfh(d, rootpath, &nfs_root_node.fhsize, - nfs_root_node.fh)) != 0) { - printf("NFS MOUNT RPC error: %d\n", error); - nfs_root_node.fhsize = 0; - } - nfs_root_node.iodesc = d; -} - -static void -pxe_setnfshandle(char *rootpath) -{ - int i; - u_char *fh; - char buf[2 * NFS_V3MAXFHSIZE + 3], *cp; - - /* - * If NFS files were never opened, we need to do mount call - * ourselves. Use nfs_root_node.iodesc as flag indicating - * previous NFS usage. - */ - if (nfs_root_node.iodesc == NULL) - pxe_rpcmountcall(); - - fh = &nfs_root_node.fh[0]; - buf[0] = 'X'; - cp = &buf[1]; - for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2) - sprintf(cp, "%02x", fh[i]); - sprintf(cp, "X"); - setenv("boot.nfsroot.nfshandle", buf, 1); - sprintf(buf, "%d", nfs_root_node.fhsize); - setenv("boot.nfsroot.nfshandlelen", buf, 1); -} - void pxenv_call(int func) { @@ -570,121 +345,196 @@ bangpxe_call(int func) static int pxe_netif_match(struct netif *nif, void *machdep_hint) { - return 1; + return (1); } static int pxe_netif_probe(struct netif *nif, void *machdep_hint) { - t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; - if (pxe_call == NULL) - return -1; + return (-1); - bzero(udpopen_p, sizeof(*udpopen_p)); - udpopen_p->src_ip = bootplayer.yip; - pxe_call(PXENV_UDP_OPEN); - - if (udpopen_p->status != 0) { - printf("pxe_netif_probe: failed %x\n", udpopen_p->status); - return -1; - } - return 0; + return (0); } static void pxe_netif_end(struct netif *nif) { - t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; - bzero(udpclose_p, sizeof(*udpclose_p)); + t_PXENV_UNDI_CLOSE *undi_close_p; - pxe_call(PXENV_UDP_CLOSE); - if (udpclose_p->status != 0) - printf("pxe_end failed %x\n", udpclose_p->status); + undi_close_p = (t_PXENV_UNDI_CLOSE *)scratch_buffer; + bzero(undi_close_p, sizeof(*undi_close_p)); + pxe_call(PXENV_UNDI_CLOSE); + if (undi_close_p->Status != 0) + printf("undi close failed: %x\n", undi_close_p->Status); } static void pxe_netif_init(struct iodesc *desc, void *machdep_hint) { - int i; - for (i = 0; i < 6; ++i) - desc->myea[i] = bootplayer.CAddr[i]; - desc->xid = bootplayer.ident; -} + t_PXENV_UNDI_GET_INFORMATION *undi_info_p; + t_PXENV_UNDI_OPEN *undi_open_p; + uint8_t *mac; + int i, len; + + undi_info_p = (t_PXENV_UNDI_GET_INFORMATION *)scratch_buffer; + bzero(undi_info_p, sizeof(*undi_info_p)); + pxe_call(PXENV_UNDI_GET_INFORMATION); + if (undi_info_p->Status != 0) { + printf("undi get info failed: %x\n", undi_info_p->Status); + return; + } -static int -pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) -{ - return len; + /* Make sure the CurrentNodeAddress is valid. */ + for (i = 0; i < undi_info_p->HwAddrLen; ++i) { + if (undi_info_p->CurrentNodeAddress[i] != 0) + break; + } + if (i < undi_info_p->HwAddrLen) { + for (i = 0; i < undi_info_p->HwAddrLen; ++i) { + if (undi_info_p->CurrentNodeAddress[i] != 0xff) + break; + } + } + if (i < undi_info_p->HwAddrLen) + mac = undi_info_p->CurrentNodeAddress; + else + mac = undi_info_p->PermNodeAddress; + + len = min(sizeof (desc->myea), undi_info_p->HwAddrLen); + for (i = 0; i < len; ++i) + desc->myea[i] = mac[i]; + + if (bootp_response != NULL) + desc->xid = bootp_response->bp_xid; + else + desc->xid = 0; + + undi_open_p = (t_PXENV_UNDI_OPEN *)scratch_buffer; + bzero(undi_open_p, sizeof(*undi_open_p)); + undi_open_p->PktFilter = FLTR_DIRECTED | FLTR_BRDCST; + pxe_call(PXENV_UNDI_OPEN); + if (undi_open_p->Status != 0) + printf("undi open failed: %x\n", undi_open_p->Status); } static int -pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +pxe_netif_receive(void **pkt) { - return len; -} + t_PXENV_UNDI_ISR *isr = (t_PXENV_UNDI_ISR *)scratch_buffer; + char *buf, *ptr, *frame; + size_t size, rsize; + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_START; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) + return (-1); + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) + return (-1); + + while (isr->FuncFlag == PXENV_UNDI_ISR_OUT_TRANSMIT) { + /* + * Wait till transmit is done. + */ + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0 || + isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) + return (-1); + } -ssize_t -sendudp(struct iodesc *h, void *pkt, size_t len) -{ - t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; - bzero(udpwrite_p, sizeof(*udpwrite_p)); - - udpwrite_p->ip = h->destip.s_addr; - udpwrite_p->dst_port = h->destport; - udpwrite_p->src_port = h->myport; - udpwrite_p->buffer_size = len; - udpwrite_p->buffer.segment = VTOPSEG(pkt); - udpwrite_p->buffer.offset = VTOPOFF(pkt); - - if (netmask == 0 || SAMENET(myip, h->destip, netmask)) - udpwrite_p->gw = 0; - else - udpwrite_p->gw = gateip.s_addr; + while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE) { + if (isr->Status != 0 || + isr->FuncFlag == PXENV_UNDI_ISR_OUT_DONE) { + return (-1); + } + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + } - pxe_call(PXENV_UDP_WRITE); + size = isr->FrameLength; + buf = malloc(size + ETHER_ALIGN); + if (buf == NULL) + return (-1); + ptr = buf + ETHER_ALIGN; + rsize = 0; + + while (rsize < size) { + frame = (char *)((uintptr_t)isr->Frame.segment << 4); + frame += isr->Frame.offset; + bcopy(PTOV(frame), ptr, isr->BufferLength); + ptr += isr->BufferLength; + rsize += isr->BufferLength; + + bzero(isr, sizeof(*isr)); + isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + pxe_call(PXENV_UNDI_ISR); + if (isr->Status != 0) { + free(buf); + return (-1); + } -#if 0 - /* XXX - I dont know why we need this. */ - delay(1000); -#endif - if (udpwrite_p->status != 0) { - /* XXX: This happens a lot. It shouldn't. */ - if (udpwrite_p->status != 1) - printf("sendudp failed %x\n", udpwrite_p->status); - return -1; + /* Did we got another update? */ + if (isr->FuncFlag == PXENV_UNDI_ISR_OUT_RECEIVE) + continue; + break; } - return len; + + *pkt = buf; + return (rsize); } -ssize_t -readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) +static ssize_t +pxe_netif_get(struct iodesc *desc, void **pkt, time_t timeout) { - t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; - struct udphdr *uh = NULL; + time_t t; + void *ptr; + int ret = -1; + + t = getsecs(); + while ((getsecs() - t) < timeout) { + ret = pxe_netif_receive(&ptr); + if (ret != -1) { + *pkt = ptr; + break; + } + } + return (ret); +} - uh = (struct udphdr *) pkt - 1; - bzero(udpread_p, sizeof(*udpread_p)); +static ssize_t +pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +{ + t_PXENV_UNDI_TRANSMIT *trans_p; + t_PXENV_UNDI_TBD *tbd_p; + char *data; - udpread_p->dest_ip = h->myip.s_addr; - udpread_p->d_port = h->myport; - udpread_p->buffer_size = len; - udpread_p->buffer.segment = VTOPSEG(data_buffer); - udpread_p->buffer.offset = VTOPOFF(data_buffer); + trans_p = (t_PXENV_UNDI_TRANSMIT *)scratch_buffer; + bzero(trans_p, sizeof(*trans_p)); + tbd_p = (t_PXENV_UNDI_TBD *)(scratch_buffer + sizeof(*trans_p)); + bzero(tbd_p, sizeof(*tbd_p)); - pxe_call(PXENV_UDP_READ); + data = scratch_buffer + sizeof(*trans_p) + sizeof(*tbd_p); -#if 0 - /* XXX - I dont know why we need this. */ - delay(1000); -#endif - if (udpread_p->status != 0) { - /* XXX: This happens a lot. It shouldn't. */ - if (udpread_p->status != 1) - printf("readudp failed %x\n", udpread_p->status); - return -1; + trans_p->TBD.segment = VTOPSEG(tbd_p); + trans_p->TBD.offset = VTOPOFF(tbd_p); + + tbd_p->ImmedLength = len; + tbd_p->Xmit.segment = VTOPSEG(data); + tbd_p->Xmit.offset = VTOPOFF(data); + bcopy(pkt, data, len); + + pxe_call(PXENV_UNDI_TRANSMIT); + if (trans_p->Status != 0) { + return (-1); } - bcopy(data_buffer, pkt, udpread_p->buffer_size); - uh->uh_sport = udpread_p->s_port; - return udpread_p->buffer_size; + + return (len); } diff --git a/sys/boot/i386/libi386/pxe.h b/sys/boot/i386/libi386/pxe.h index 62b6aa79cca..119c86be519 100644 --- a/sys/boot/i386/libi386/pxe.h +++ b/sys/boot/i386/libi386/pxe.h @@ -349,7 +349,7 @@ typedef struct { */ # define PXENV_UNDI_ISR_OUT_DONE 0 # define PXENV_UNDI_ISR_OUT_TRANSMIT 2 -# define PXENV_UNDI_ISR_OUT_RECIEVE 3 +# define PXENV_UNDI_ISR_OUT_RECEIVE 3 # define PXENV_UNDI_ISR_OUT_BUSY 4 } PACKED t_PXENV_UNDI_ISR; diff --git a/sys/boot/i386/libi386/relocater_tramp.S b/sys/boot/i386/libi386/relocater_tramp.S new file mode 100644 index 00000000000..6db4a7509b2 --- /dev/null +++ b/sys/boot/i386/libi386/relocater_tramp.S @@ -0,0 +1,358 @@ +/*- + * Copyright 2015 Toomas Soome + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +/* + * relocate is needed to support loading code which has to be located + * below 1MB, as both BTX and loader are using low memory area. + * + * relocate and start loaded code. Since loaded code may need to be + * placed to already occupied memory area, this code is moved to safe + * memory area and then btx __exec will be called with physical pointer + * to this area. __exec will set pointer to %eax and use call *%eax, + * so on entry, we have new "base" address in %eax. + * + * Relocate will first set up and load new safe GDT to shut down BTX, + * then loaded code will be relocated to final memory location, + * then machine will be switched from 32bit protected mode to 16bit + * protected mode following by switch to real mode with A20 enabled or + * disabled. Finally the loaded code will be started and it will take + * over the whole system. + * + * For now, the known "safe" memory area for relocate is 0x600, + * the actual "free" memory is supposed to start from 0x500, leaving + * first 0x100 bytes in reserve. As relocate code+data is very small, + * it will leave enough space to set up boot blocks to 0:7c00 or load + * linux kernel below 1MB space. + */ +/* + * segment selectors + */ + .set SEL_SCODE,0x8 + .set SEL_SDATA,0x10 + .set SEL_RCODE,0x18 + .set SEL_RDATA,0x20 + + .p2align 4 + .globl relocater +relocater: + cli + /* + * set up GDT from new location + */ + movl %eax, %esi /* our base address */ + add $(relocater.1-relocater), %eax + jmp *%eax +relocater.1: + /* set up jump */ + lea (relocater.2-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + + /* set up gdt */ + lea (gdt-relocater) (%esi), %eax + movl %eax, (gdtaddr-relocater) (%esi) + + /* load gdt */ + lgdt (gdtdesc - relocater) (%esi) + lidt (idt-relocater) (%esi) + + /* update cs */ + ljmp *(jump_vector-relocater) (%esi) + + .code32 +relocater.2: + xorl %eax, %eax + movb $SEL_SDATA, %al + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movl %cr0, %eax /* disable paging */ + andl $~0x80000000,%eax + movl %eax, %cr0 + xorl %ecx, %ecx /* flush TLB */ + movl %ecx, %cr3 + cld +/* + * relocate data loop. load source, dest and size from + * relocater_data[i], 0 value will stop the loop. + * registers used for move: %esi, %edi, %ecx. + * %ebx to keep base + * %edx for relocater_data offset + */ + movl %esi, %ebx /* base address */ + xorl %edx, %edx +loop.1: + movl (relocater_data-relocater)(%ebx, %edx, 4), %eax + testl %eax, %eax + jz loop.2 + movl (relocater_data-relocater)(%ebx, %edx, 4), %esi + inc %edx + movl (relocater_data-relocater)(%ebx, %edx, 4), %edi + inc %edx + movl (relocater_data-relocater)(%ebx, %edx, 4), %ecx + inc %edx + rep + movsb + jmp loop.1 +loop.2: + movl %ebx, %esi /* restore esi */ + /* + * data is relocated, switch to 16bit mode + */ + lea (relocater.3-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + movl $SEL_RCODE, %eax + movl %eax, (jump_vector-relocater+4) (%esi) + + ljmp *(jump_vector-relocater) (%esi) +relocater.3: + .code16 + + movw $SEL_RDATA, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + lidt (idt-relocater) (%esi) + lea (relocater.4-relocater)(%esi), %eax + movl %eax, (jump_vector-relocater) (%esi) + xorl %eax, %eax + movl %eax, (jump_vector-relocater+4) (%esi) + /* clear PE */ + movl %cr0, %eax + dec %al + movl %eax, %cr0 + ljmp *(jump_vector-relocater) (%esi) +relocater.4: + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + /* + * set real mode irq offsets + */ + movw $0x7008,%bx + in $0x21,%al # Save master + push %ax # IMR + in $0xa1,%al # Save slave + push %ax # IMR + movb $0x11,%al # ICW1 to + outb %al,$0x20 # master, + outb %al,$0xa0 # slave + movb %bl,%al # ICW2 to + outb %al,$0x21 # master + movb %bh,%al # ICW2 to + outb %al,$0xa1 # slave + movb $0x4,%al # ICW3 to + outb %al,$0x21 # master + movb $0x2,%al # ICW3 to + outb %al,$0xa1 # slave + movb $0x1,%al # ICW4 to + outb %al,$0x21 # master, + outb %al,$0xa1 # slave + pop %ax # Restore slave + outb %al,$0xa1 # IMR + pop %ax # Restore master + outb %al,$0x21 # IMR + # done + /* + * Should A20 be left enabled? + */ + /* movw imm16, %ax */ + .byte 0xb8 + .globl relocator_a20_enabled +relocator_a20_enabled: + .word 0 + test %ax, %ax + jnz a20_done + + movw $0xa00, %ax + movw %ax, %sp + movw %ax, %bp + + /* Disable A20 */ + movw $0x2400, %ax + int $0x15 +# jnc a20_done + + call a20_check_state + testb %al, %al + jz a20_done + + inb $0x92 + andb $(~0x03), %al + outb $0x92 + jmp a20_done + +a20_check_state: + movw $100, %cx +1: + xorw %ax, %ax + movw %ax, %ds + decw %ax + movw %ax, %es + xorw %ax, %ax + movw $0x8000, %ax + movw %ax, %si + addw $0x10, %ax + movw %ax, %di + movb %ds:(%si), %dl + movb %es:(%di), %al + movb %al, %dh + decb %dh + movb %dh, %ds:(%si) + outb %al, $0x80 + outb %al, $0x80 + movb %es:(%di), %dh + subb %dh, %al + xorb $1, %al + movb %dl, %ds:(%si) + testb %al, %al + jz a20_done + loop 1b + ret +a20_done: + /* + * set up registers + */ + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_ds +relocator_ds: .word 0 + movw %ax, %ds + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_es +relocator_es: .word 0 + movw %ax, %es + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_fs +relocator_fs: .word 0 + movw %ax, %fs + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_gs +relocator_gs: .word 0 + movw %ax, %gs + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_ss +relocator_ss: .word 0 + movw %ax, %ss + + /* movw imm16, %ax. */ + .byte 0xb8 + .globl relocator_sp +relocator_sp: .word 0 + movzwl %ax, %esp + + /* movw imm32, %eax. */ + .byte 0x66, 0xb8 + .globl relocator_esi +relocator_esi: .long 0 + movl %eax, %esi + + /* movw imm32, %edx. */ + .byte 0x66, 0xba + .globl relocator_edx +relocator_edx: .long 0 + + /* movw imm32, %ebx. */ + .byte 0x66, 0xbb + .globl relocator_ebx +relocator_ebx: .long 0 + + /* movw imm32, %eax. */ + .byte 0x66, 0xb8 + .globl relocator_eax +relocator_eax: .long 0 + + /* movw imm32, %ebp. */ + .byte 0x66, 0xbd + .globl relocator_ebp +relocator_ebp: .long 0 + + sti + .byte 0xea /* ljmp */ + .globl relocator_ip +relocator_ip: + .word 0 + .globl relocator_cs +relocator_cs: + .word 0 + +/* GDT to reset BTX */ + .code32 + .p2align 4 +jump_vector: .long 0 + .long SEL_SCODE + +gdt: .word 0x0, 0x0 /* null entry */ + .byte 0x0, 0x0, 0x0, 0x0 + .word 0xffff, 0x0 /* SEL_SCODE */ + .byte 0x0, 0x9a, 0xcf, 0x0 + .word 0xffff, 0x0 /* SEL_SDATA */ + .byte 0x0, 0x92, 0xcf, 0x0 + .word 0xffff, 0x0 /* SEL_RCODE */ + .byte 0x0, 0x9a, 0x0f, 0x0 + .word 0xffff, 0x0 /* SEL_RDATA */ + .byte 0x0, 0x92, 0x0f, 0x0 +gdt.1: + +gdtdesc: .word gdt.1 - gdt - 1 /* limit */ +gdtaddr: .long 0 /* base */ + +idt: .word 0x3ff + .long 0 + + .globl relocater_data +relocater_data: + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 /* src */ + .long 0 /* dest */ + .long 0 /* size */ + .long 0 + + .globl relocater_size +relocater_size: + .long relocater_size-relocater diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile index 31bf9834ddb..434ccc91494 100644 --- a/sys/boot/i386/loader/Makefile +++ b/sys/boot/i386/loader/Makefile @@ -9,9 +9,12 @@ MAN= INTERNALPROG= NEWVERSWHAT?= "bootstrap loader" x86 VERSION_FILE= ${.CURDIR}/../loader/version +LOADER_NET_SUPPORT?= yes +LOADER_NFS_SUPPORT?= yes +LOADER_TFTP_SUPPORT?= yes # architecture-specific loader code -SRCS= main.c conf.c vers.c +SRCS= main.c conf.c vers.c chain.c # Put LOADER_FIREWIRE_SUPPORT=yes in /etc/make.conf for FireWire/dcons support .if defined(LOADER_FIREWIRE_SUPPORT) @@ -25,10 +28,14 @@ CFLAGS+= -DLOADER_ZFS_SUPPORT LIBZFSBOOT= ${.OBJDIR}/../../zfs/libzfsboot.a .endif -# Enable PXE TFTP or NFS support, not both. +.if defined(LOADER_NET_SUPPORT) +CFLAGS+= -I${.CURDIR}/../../../../lib/libstand +.endif + .if defined(LOADER_TFTP_SUPPORT) CFLAGS+= -DLOADER_TFTP_SUPPORT -.else +.endif +.if defined(LOADER_NFS_SUPPORT) CFLAGS+= -DLOADER_NFS_SUPPORT .endif diff --git a/sys/boot/i386/loader/chain.c b/sys/boot/i386/loader/chain.c new file mode 100644 index 00000000000..d6810ef67fc --- /dev/null +++ b/sys/boot/i386/loader/chain.c @@ -0,0 +1,134 @@ +/*- + * Copyright 2015 Toomas Soome + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Chain loader to load BIOS boot block either from MBR or PBR. + * + * Note the boot block location 0000:7c000 conflicts with loader, so we need to + * read in to temporary space and relocate on exec, when btx is stopped. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include "bootstrap.h" +#include "libi386/libi386.h" +#include "btxv86.h" + +/* + * The MBR/VBR is located in first sector of disk/partition. + * Read 512B to temporary location and set up relocation. Then + * exec relocator. + */ +#define SECTOR_SIZE (512) + +COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain); + +static int +command_chain(int argc, char *argv[]) +{ + int fd, len, size = SECTOR_SIZE; + struct stat st; + vm_offset_t mem = 0x100000; + struct i386_devdesc *rootdev; + + if (argc == 1) { + command_errmsg = "no device or file name specified"; + return (CMD_ERROR); + } + if (argc != 2) { + command_errmsg = "invalid trailing arguments"; + return (CMD_ERROR); + } + + fd = open(argv[1], O_RDONLY); + if (fd == -1) { + command_errmsg = "open failed"; + return (CMD_ERROR); + } + + len = strlen(argv[1]); + if (argv[1][len-1] != ':') { + if (fstat(fd, &st) == -1) { + command_errmsg = "stat failed"; + close(fd); + return (CMD_ERROR); + } + size = st.st_size; + } else if (strncmp(argv[1], "disk", 4) != 0) { + command_errmsg = "can only use disk device"; + close(fd); + return (CMD_ERROR); + } + + i386_getdev((void **)(&rootdev), argv[1], NULL); + if (rootdev == NULL) { + command_errmsg = "can't determine root device"; + return (CMD_ERROR); + } + + if (archsw.arch_readin(fd, mem, SECTOR_SIZE) != SECTOR_SIZE) { + command_errmsg = "failed to read disk"; + close(fd); + return (CMD_ERROR); + } + close(fd); + + if (*((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) { + command_errmsg = "wrong magic"; + return (CMD_ERROR); + } + + relocater_data[0].src = mem; + relocater_data[0].dest = 0x7C00; + relocater_data[0].size = SECTOR_SIZE; + + relocator_edx = bd_unit2bios(rootdev->d_unit); + relocator_esi = relocater_size; + relocator_ds = 0; + relocator_es = 0; + relocator_fs = 0; + relocator_gs = 0; + relocator_ss = 0; + relocator_cs = 0; + relocator_sp = 0x7C00; + relocator_ip = 0x7C00; + relocator_a20_enabled = 0; + + i386_copyin(relocater, 0x600, relocater_size); + + dev_cleanup(); + + __exec((void *)0x600); + + panic("exec returned"); + return (CMD_ERROR); /* not reached */ +} diff --git a/sys/boot/i386/loader/help.i386 b/sys/boot/i386/loader/help.i386 index dc285347c3a..0ff628643c8 100644 --- a/sys/boot/i386/loader/help.i386 +++ b/sys/boot/i386/loader/help.i386 @@ -43,3 +43,12 @@ Displays the BIOS SMAP (system memory map) table. ################################################################################ +# Tchain DChain load disk block + + chain disk: + + chain will read stage1 (MBR or VBR) boot block from specified device + to address 0000:7C00 and attempts to run it. Use lsdev to get available + device names. Disk name must end with colon. + +################################################################################ diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 919165f5f3e..81bc2ff55dd 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -418,7 +419,7 @@ command_reloadbe(int argc, char *argv[]) /* There does not appear to be a ZFS pool here, exit without error */ return (CMD_OK); } - err = zfs_bootenv(getenv("zfs_be_root")); + err = zfs_bootenv(root); } if (err != 0) { @@ -463,4 +464,14 @@ i386_zfs_probe(void) zfs_probe_dev(devname, NULL); } } + +uint64_t +ldi_get_size(void *priv) +{ + int fd = (uintptr_t) priv; + uint64_t size; + + ioctl(fd, DIOCGMEDIASIZE, &size); + return (size); +} #endif diff --git a/sys/boot/i386/pxeldr/pxeboot.8 b/sys/boot/i386/pxeldr/pxeboot.8 index 7f57d5a087b..0194e218e52 100644 --- a/sys/boot/i386/pxeldr/pxeboot.8 +++ b/sys/boot/i386/pxeldr/pxeboot.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 1, 2000 +.Dd May 27, 2017 .Dt PXEBOOT 8 .Os .Sh NAME @@ -39,6 +39,11 @@ configured to run under Intel's Preboot Execution Environment (PXE) system. PXE is a form of smart boot ROM, built into Intel EtherExpress Pro/100 and 3Com 3c905c Ethernet cards, and Ethernet-equipped Intel motherboards. PXE supports DHCP configuration and provides low-level NIC access services. +.Pp +The DHCP client will set a DHCP user class named +.Va FreeBSD +to allow flexible configuration of the DHCP server. +.Pp The .Nm bootloader retrieves the kernel, modules, @@ -69,6 +74,9 @@ max-lease-time 120; subnet 10.0.0.0 netmask 255.255.255.0 { filename "pxeboot"; range 10.0.0.10 10.0.0.254; + if exists user-class and option user-class = "FreeBSD" { + option root-path "tftp://10.0.0.1/FreeBSD"; + } } .Ed @@ -85,13 +93,34 @@ expects to fetch .Pa /boot/loader.rc from the specified server before loading any other files. .Pp +Valid +.Va option root-path +Syntax is the following +.Bl -tag -width ://ip/path indent +.It /path +path to the root filesystem on the NFS server +.It ip:/path +path to the root filesystem on the NFS server +.Ar ip +.It nfs:/path +path to the root filesystem on the NFS server +.It nfs://ip/path +path to the root filesystem on the NFS server +.Ar ip +.It tftp:/path +path to the root filesystem on the TFTP server +.It tftp://ip/path +path to the root filesystem on the TFTP server +.Ar ip +.El +.Pp .Nm defaults to a conservative 1024 byte NFS data packet size. This may be changed by setting the .Va nfs.read_size variable in .Pa /boot/loader.conf . -Valid values range from 1024 to 4096 bytes. +Valid values range from 1024 to 16384 bytes. .Pp In all other respects, .Nm diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index fe73f97ef4e..88b5231efe4 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include "lib.h" #include "rbx.h" #include "drv.h" +#include "edd.h" #include "util.h" #include "cons.h" #include "bootargs.h" @@ -125,6 +126,7 @@ static int parse_cmd(void); static void bios_getmem(void); void *malloc(size_t n); void free(void *ptr); +int main(void); void * malloc(size_t n) @@ -468,6 +470,73 @@ copy_dsk(struct dsk *dsk) return (newdsk); } +/* + * Get disk size from eax=0x800 and 0x4800. We need to probe both + * because 0x4800 may not be available and we would like to get more + * or less correct disk size - if it is possible at all. + * Note we do not really want to touch drv.c because that code is shared + * with boot2 and we can not afford to grow that code. + */ +static uint64_t +drvsize_ext(struct dsk *dskp) +{ + uint64_t size, tmp; + int cyl, hds, sec; + + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x800; + v86.edx = dskp->drive; + v86int(); + + /* Don't error out if we get bad sector number, try EDD as well */ + if (V86_CY(v86.efl) || /* carry set */ + (v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */ + return (0); + + cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; + /* Convert max head # -> # of heads */ + hds = ((v86.edx & 0xff00) >> 8) + 1; + sec = v86.ecx & 0x3f; + + size = (uint64_t)cyl * hds * sec; + + /* Determine if we can use EDD with this device. */ + v86.ctl = V86_FLAGS; + v86.addr = 0x13; + v86.eax = 0x4100; + v86.edx = dskp->drive; + v86.ebx = 0x55aa; + v86int(); + if (V86_CY(v86.efl) || /* carry set */ + (v86.ebx & 0xffff) != 0xaa55 || /* signature */ + (v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) + return (size); + + tmp = drvsize(dskp); + if (tmp > size) + size = tmp; + + return (size); +} + +/* + * The "layered" ioctl to read disk/partition size. Unfortunately + * the zfsboot case is hardest, because we do not have full software + * stack available, so we need to do some manual work here. + */ +uint64_t +ldi_get_size(void *priv) +{ + struct dsk *dskp = priv; + uint64_t size = dskp->size; + + if (dskp->start == 0) + size = drvsize_ext(dskp); + + return (size * DEV_BSIZE); +} + static void probe_drive(struct dsk *dsk) { @@ -498,7 +567,7 @@ probe_drive(struct dsk *dsk) * out the partition table and probe each slice/partition * in turn for a vdev or GELI encrypted vdev. */ - elba = drvsize(dsk); + elba = drvsize_ext(dsk); if (elba > 0) { elba--; } @@ -549,6 +618,7 @@ probe_drive(struct dsk *dsk) if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, sizeof(uuid_t)) == 0) { dsk->start = ent->ent_lba_start; + dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1; dsk->slice = part + 1; dsk->part = 255; if (vdev_probe(vdev_read, dsk, NULL) == 0) { @@ -593,6 +663,7 @@ probe_drive(struct dsk *dsk) if (!dp[i].dp_typ) continue; dsk->start = dp[i].dp_start; + dsk->size = dp[i].dp_size; dsk->slice = i + 1; if (vdev_probe(vdev_read, dsk, NULL) == 0) { dsk = copy_dsk(dsk); @@ -648,7 +719,7 @@ main(void) dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1; dsk->part = 0; dsk->start = 0; - dsk->init = 0; + dsk->size = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); @@ -699,7 +770,7 @@ main(void) dsk->slice = 0; dsk->part = 0; dsk->start = 0; - dsk->init = 0; + dsk->size = 0; probe_drive(dsk); } diff --git a/sys/boot/mips/beri/boot2/Makefile b/sys/boot/mips/beri/boot2/Makefile index be2dc0a6bb5..a12db1b6e6b 100644 --- a/sys/boot/mips/beri/boot2/Makefile +++ b/sys/boot/mips/beri/boot2/Makefile @@ -63,7 +63,7 @@ LIBSTAND= ${.OBJDIR}/../../../../../lib/libstand/libstand.a LDFLAGS= -nostdlib \ -static \ - -N \ + -Wl,-N \ -G0 \ -L${.CURDIR} diff --git a/sys/boot/mips/beri/loader/beri_disk_cfi.c b/sys/boot/mips/beri/loader/beri_disk_cfi.c index ffccce7959b..a21947fdfec 100644 --- a/sys/boot/mips/beri/loader/beri_disk_cfi.c +++ b/sys/boot/mips/beri/loader/beri_disk_cfi.c @@ -73,6 +73,7 @@ beri_cfi_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, { int error; + flag &= F_MASK; if (flag == F_WRITE) return (EROFS); if (flag != F_READ) diff --git a/sys/boot/mips/beri/loader/beri_disk_sdcard.c b/sys/boot/mips/beri/loader/beri_disk_sdcard.c index aab7a664b59..266fb4a813f 100644 --- a/sys/boot/mips/beri/loader/beri_disk_sdcard.c +++ b/sys/boot/mips/beri/loader/beri_disk_sdcard.c @@ -73,6 +73,7 @@ beri_sdcard_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, { int error; + flag &= F_MASK; if (flag == F_WRITE) return (EROFS); if (flag != F_READ) diff --git a/sys/boot/ofw/libofw/ofw_disk.c b/sys/boot/ofw/libofw/ofw_disk.c index 43717ae700a..2c3bd568a26 100644 --- a/sys/boot/ofw/libofw/ofw_disk.c +++ b/sys/boot/ofw/libofw/ofw_disk.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "bootstrap.h" #include "libofw.h" @@ -154,11 +155,26 @@ ofwd_close(struct open_file *f) } static int -ofwd_ioctl(struct open_file *f __unused, u_long cmd __unused, - void *data __unused) +ofwd_ioctl(struct open_file *f, u_long cmd, void *data) { - - return (EINVAL); + struct ofw_devdesc *dev = f->f_devdata; + int block_size; + unsigned int n; + + switch (cmd) { + case DIOCGSECTORSIZE: + block_size = OF_block_size(dev->d_handle); + *(u_int *)data = block_size; + break; + case DIOCGMEDIASIZE: + block_size = OF_block_size(dev->d_handle); + n = OF_blocks(dev->d_handle); + *(uint64_t *)data = (uint64_t)(n * block_size); + break; + default: + return (ENOTTY); + } + return (0); } static int diff --git a/sys/boot/ofw/libofw/ofw_net.c b/sys/boot/ofw/libofw/ofw_net.c index 691dedab052..ea5e47e7ed0 100644 --- a/sys/boot/ofw/libofw/ofw_net.c +++ b/sys/boot/ofw/libofw/ofw_net.c @@ -46,8 +46,8 @@ __FBSDID("$FreeBSD$"); static int ofwn_probe(struct netif *, void *); static int ofwn_match(struct netif *, void *); static void ofwn_init(struct iodesc *, void *); -static int ofwn_get(struct iodesc *, void *, size_t, time_t); -static int ofwn_put(struct iodesc *, void *, size_t); +static ssize_t ofwn_get(struct iodesc *, void **, time_t); +static ssize_t ofwn_put(struct iodesc *, void *, size_t); static void ofwn_end(struct netif *); extern struct netif_stats ofwn_stats[]; @@ -57,7 +57,7 @@ struct netif_dif ofwn_ifs[] = { { 0, 1, &ofwn_stats[0], 0, }, }; -struct netif_stats ofwn_stats[NENTS(ofwn_ifs)]; +struct netif_stats ofwn_stats[nitems(ofwn_ifs)]; struct netif_driver ofwnet = { "net", /* netif_bname */ @@ -68,7 +68,7 @@ struct netif_driver ofwnet = { ofwn_put, /* netif_put */ ofwn_end, /* netif_end */ ofwn_ifs, /* netif_ifs */ - NENTS(ofwn_ifs) /* netif_nifs */ + nitems(ofwn_ifs) /* netif_nifs */ }; static ihandle_t netinstance; @@ -87,7 +87,7 @@ ofwn_probe(struct netif *nif, void *machdep_hint) return 0; } -static int +static ssize_t ofwn_put(struct iodesc *desc, void *pkt, size_t len) { size_t sendlen; @@ -124,20 +124,32 @@ ofwn_put(struct iodesc *desc, void *pkt, size_t len) return rv; } -static int -ofwn_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +static ssize_t +ofwn_get(struct iodesc *desc, void **pkt, time_t timeout) { time_t t; - int length; + ssize_t length; + size_t len; + char *buf, *ptr; #if defined(NETIF_DEBUG) - printf("netif_get: pkt=%p, maxlen=%d, timeout=%d\n", pkt, len, - timeout); + printf("netif_get: pkt=%p, timeout=%d\n", pkt, timeout); #endif + /* + * We should read the "max-frame-size" int property instead, + * but at this time the iodesc does not have mtu, so we will take + * a small shortcut here. + */ + len = ETHER_MAX_LEN; + buf = malloc(len + ETHER_ALIGN); + if (buf == NULL) + return (-1); + ptr = buf + ETHER_ALIGN; + t = getsecs(); do { - length = OF_read(netinstance, pkt, len); + length = OF_read(netinstance, ptr, len); } while ((length == -2 || length == 0) && (getsecs() - t < timeout)); @@ -145,12 +157,14 @@ ofwn_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) printf("netif_get: received length=%d (%x)\n", length, length); #endif - if (length < 12) - return -1; + if (length < 12) { + free(buf); + return (-1); + } #if defined(NETIF_VERBOSE_DEBUG) { - char *ch = pkt; + char *ch = ptr; int i; for(i = 0; i < 96; i += 4) { @@ -163,7 +177,7 @@ ofwn_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) #if defined(NETIF_DEBUG) { - struct ether_header *eh = pkt; + struct ether_header *eh = ptr; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); @@ -171,7 +185,8 @@ ofwn_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) } #endif - return length; + *pkt = buf; + return (length); } extern char *strchr(); diff --git a/sys/boot/ofw/libofw/openfirm.c b/sys/boot/ofw/libofw/openfirm.c index a8626cc403f..1ccae60d66d 100644 --- a/sys/boot/ofw/libofw/openfirm.c +++ b/sys/boot/ofw/libofw/openfirm.c @@ -622,6 +622,53 @@ OF_seek(ihandle_t instance, u_int64_t pos) return (args.status); } +/* Blocks. */ +unsigned int +OF_blocks(ihandle_t instance) +{ + static struct { + cell_t name; + cell_t nargs; + cell_t nreturns; + cell_t instance; + cell_t result; + cell_t blocks; + } args = { + (cell_t)"#blocks", + 2, + 1, + }; + + args.instance = instance; + if (openfirmware(&args) == -1) + return ((unsigned int)-1); + return (args.blocks); +} + +/* Block size. */ +int +OF_block_size(ihandle_t instance) +{ + static struct { + cell_t name; + cell_t nargs; + cell_t nreturns; + cell_t instance; + cell_t result; + cell_t size; + } args = { + (cell_t)"block-size", + 2, + 1, + }; + + args.instance = instance; + if (openfirmware(&args) == -1) + return (512); + return (args.size); +} + +/* /* * Memory functions */ diff --git a/sys/boot/ofw/libofw/openfirm.h b/sys/boot/ofw/libofw/openfirm.h index ecb1f328347..9fe77c69a09 100644 --- a/sys/boot/ofw/libofw/openfirm.h +++ b/sys/boot/ofw/libofw/openfirm.h @@ -105,6 +105,8 @@ void OF_close(ihandle_t); int OF_read(ihandle_t, void *, int); int OF_write(ihandle_t, void *, int); int OF_seek(ihandle_t, u_quad_t); +unsigned int OF_blocks(ihandle_t); +int OF_block_size(ihandle_t); /* Memory functions */ void *OF_claim(void *, u_int, u_int); diff --git a/sys/boot/powerpc/ps3/ps3cdrom.c b/sys/boot/powerpc/ps3/ps3cdrom.c index ea4992c6da4..d8d41fde49a 100644 --- a/sys/boot/powerpc/ps3/ps3cdrom.c +++ b/sys/boot/powerpc/ps3/ps3cdrom.c @@ -83,6 +83,7 @@ static int ps3cdrom_strategy(void *devdata, int flag, daddr_t dblk, DEBUG("d_unit=%u dblk=%llu size=%u", dev->d_unit, dblk, size); + flag &= F_MASK; if (flag != F_READ) { dev_printf(dev, "write operation is not supported!"); return EROFS; diff --git a/sys/boot/powerpc/ps3/ps3disk.c b/sys/boot/powerpc/ps3/ps3disk.c index 05f1f4debf8..c33a023e70a 100644 --- a/sys/boot/powerpc/ps3/ps3disk.c +++ b/sys/boot/powerpc/ps3/ps3disk.c @@ -115,6 +115,7 @@ static int ps3disk_strategy(void *devdata, int flag, daddr_t dblk, struct open_dev *od = (struct open_dev *) dev->d_disk.data; int err; + flag &= F_MASK; if (flag != F_READ) { dev_printf(dev, "write operation is not supported!\n"); return EROFS; diff --git a/sys/boot/sparc64/loader/main.c b/sys/boot/sparc64/loader/main.c index e70d9bbb039..e8cdf5fcc5e 100644 --- a/sys/boot/sparc64/loader/main.c +++ b/sys/boot/sparc64/loader/main.c @@ -735,6 +735,16 @@ tlb_init_sun4u(void) } #ifdef LOADER_ZFS_SUPPORT + +/* Set by sparc64_zfs_probe to provide partition size. */ +static size_t part_size; + +uint64_t +ldi_get_size(void *priv __unused) +{ + return ((uint64_t)part_size); +} + static void sparc64_zfs_probe(void) { @@ -742,13 +752,11 @@ sparc64_zfs_probe(void) char alias[64], devname[sizeof(alias) + sizeof(":x") - 1]; char type[sizeof("device_type")]; char *bdev, *dev, *odev; - uint64_t guid; + uint64_t guid, *guidp; int fd, len, part; phandle_t aliases, options; - /* Get the GUID of the ZFS pool on the boot device. */ guid = 0; - zfs_probe_dev(bootpath, &guid); /* * Get the GUIDs of the ZFS pools on any additional disks listed in @@ -771,12 +779,6 @@ sparc64_zfs_probe(void) continue; strcpy(alias, dev); (void)OF_getprop(aliases, dev, alias, sizeof(alias)); - /* - * Don't probe the boot disk twice. Note that bootpath - * includes the partition specifier. - */ - if (strncmp(alias, bootpath, strlen(alias)) == 0) - continue; if (OF_getprop(OF_finddevice(alias), "device_type", type, sizeof(type)) == -1) continue; @@ -798,8 +800,14 @@ sparc64_zfs_probe(void) if (part == 2 || vtoc.part[part].tag != VTOC_TAG_FREEBSD_ZFS) continue; + part_size = vtoc.map[part].nblks; (void)sprintf(devname, "%s:%c", alias, part + 'a'); - if (zfs_probe_dev(devname, NULL) == ENXIO) + /* Get the GUID of the ZFS pool on the boot device. */ + if (strcmp(devname, bootpath) == 0) + guidp = &guid; + else + guidp = NULL; + if (zfs_probe_dev(devname, guidp) == ENXIO) break; } } diff --git a/sys/boot/uboot/lib/disk.c b/sys/boot/uboot/lib/disk.c index 6ca1f9d61f5..f5e44c749ba 100644 --- a/sys/boot/uboot/lib/disk.c +++ b/sys/boot/uboot/lib/disk.c @@ -149,6 +149,7 @@ stor_strategy(void *devdata, int rw, daddr_t blk, size_t size, daddr_t bcount; int err; + rw &= F_MASK; if (rw != F_READ) { stor_printf("write attempt, operation not supported!\n"); return (EROFS); @@ -274,8 +275,13 @@ static int stor_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; + int rc; dev = (struct disk_devdesc *)f->f_devdata; + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = SI(dev).bsize; diff --git a/sys/boot/uboot/lib/net.c b/sys/boot/uboot/lib/net.c index 9bc0c075437..a0d1cb4f458 100644 --- a/sys/boot/uboot/lib/net.c +++ b/sys/boot/uboot/lib/net.c @@ -50,8 +50,8 @@ __FBSDID("$FreeBSD$"); static int net_probe(struct netif *, void *); static int net_match(struct netif *, void *); static void net_init(struct iodesc *, void *); -static int net_get(struct iodesc *, void *, size_t, time_t); -static int net_put(struct iodesc *, void *, size_t); +static ssize_t net_get(struct iodesc *, void **, time_t); +static ssize_t net_put(struct iodesc *, void *, size_t); static void net_end(struct netif *); extern struct netif_stats net_stats[]; @@ -61,7 +61,7 @@ struct netif_dif net_ifs[] = { { 0, 1, &net_stats[0], 0, }, }; -struct netif_stats net_stats[NENTS(net_ifs)]; +struct netif_stats net_stats[nitems(net_ifs)]; struct netif_driver uboot_net = { "uboot_eth", /* netif_bname */ @@ -72,7 +72,7 @@ struct netif_driver uboot_net = { net_put, /* netif_put */ net_end, /* netif_end */ net_ifs, /* netif_ifs */ - NENTS(net_ifs) /* netif_nifs */ + nitems(net_ifs) /* netif_nifs */ }; struct uboot_softc { @@ -232,7 +232,7 @@ net_probe(struct netif *nif, void *machdep_hint) return (0); } -static int +static ssize_t net_put(struct iodesc *desc, void *pkt, size_t len) { struct netif *nif = desc->io_netif; @@ -271,18 +271,21 @@ net_put(struct iodesc *desc, void *pkt, size_t len) return (rv); } -static int -net_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +static ssize_t +net_get(struct iodesc *desc, void **pkt, time_t timeout) { struct netif *nif = desc->io_netif; struct uboot_softc *sc = nif->nif_devdata; time_t t; int err, rlen; + size_t len; + char *buf; #if defined(NETIF_DEBUG) - printf("net_get: pkt %p, len %d, timeout %d\n", pkt, len, timeout); + printf("net_get: pkt %p, timeout %d\n", pkt, timeout); #endif t = getsecs(); + len = sizeof(sc->sc_rxbuf); do { err = ub_dev_recv(sc->sc_handle, sc->sc_rxbuf, len, &rlen); @@ -299,13 +302,12 @@ net_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) #endif if (rlen > 0) { - memcpy(pkt, sc->sc_rxbuf, MIN(len, rlen)); - if (rlen != len) { -#if defined(NETIF_DEBUG) - printf("net_get: len %x, rlen %x\n", len, rlen); -#endif - } - return (rlen); + buf = malloc(rlen + ETHER_ALIGN); + if (buf == NULL) + return (-1); + memcpy(buf + ETHER_ALIGN, sc->sc_rxbuf, rlen); + *pkt = buf; + return ((ssize_t)rlen); } return (-1); diff --git a/sys/boot/usb/storage/umass_loader.c b/sys/boot/usb/storage/umass_loader.c index 3414201a33d..fbb890be279 100644 --- a/sys/boot/usb/storage/umass_loader.c +++ b/sys/boot/usb/storage/umass_loader.c @@ -92,6 +92,7 @@ umass_disk_strategy(void *devdata, int flag, daddr_t dblk, size_t size, if (rsizep != NULL) *rsizep = 0; + flag &= F_MASK; if (flag == F_WRITE) { if (usb_msc_write_10(umass_uaa.device, 0, dblk, size >> 9, buf) != 0) return (EINVAL); diff --git a/sys/boot/userboot/userboot/main.c b/sys/boot/userboot/userboot/main.c index 87733cd659a..9e69724b17a 100644 --- a/sys/boot/userboot/userboot/main.c +++ b/sys/boot/userboot/userboot/main.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "bootstrap.h" #include "disk.h" @@ -270,6 +271,16 @@ command_reloadbe(int argc, char *argv[]) return (CMD_OK); } + +uint64_t +ldi_get_size(void *priv) +{ + int fd = (uintptr_t) priv; + uint64_t size; + + ioctl(fd, DIOCGMEDIASIZE, &size); + return (size); +} #endif /* USERBOOT_ZFS_SUPPORT */ COMMAND_SET(quit, "quit", "exit the loader", command_quit); diff --git a/sys/boot/userboot/userboot/userboot_disk.c b/sys/boot/userboot/userboot/userboot_disk.c index 4e617b6d252..fc21b5f1792 100644 --- a/sys/boot/userboot/userboot/userboot_disk.c +++ b/sys/boot/userboot/userboot/userboot_disk.c @@ -91,8 +91,8 @@ userdisk_init(void) return (ENOMEM); for (i = 0; i < userdisk_maxunit; i++) { if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE, - §orsize) != NULL || CALLBACK(diskioctl, i, - DIOCGMEDIASIZE, &mediasize) != NULL) + §orsize) != 0 || CALLBACK(diskioctl, i, + DIOCGMEDIASIZE, &mediasize) != 0) return (ENXIO); ud_info[i].mediasize = mediasize; ud_info[i].sectorsize = sectorsize; @@ -211,6 +211,7 @@ userdisk_realstrategy(void *devdata, int rw, daddr_t dblk, size_t size, size_t resid; int rc; + rw &= F_MASK; if (rw == F_WRITE) return (EROFS); if (rw != F_READ) @@ -230,7 +231,12 @@ static int userdisk_ioctl(struct open_file *f, u_long cmd, void *data) { struct disk_devdesc *dev; + int rc; dev = (struct disk_devdesc *)f->f_devdata; + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + return (CALLBACK(diskioctl, dev->d_unit, cmd, data)); } diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h index 1b304998867..d1b4674679e 100644 --- a/sys/boot/zfs/libzfs.h +++ b/sys/boot/zfs/libzfs.h @@ -81,6 +81,7 @@ int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, char *zfs_fmtdev(void *vdev); int zfs_probe_dev(const char *devname, uint64_t *pool_guid); int zfs_list(const char *name); +uint64_t ldi_get_size(void *); void init_zfs_bootenv(char *currdev); int zfs_bootenv(const char *name); int zfs_belist_add(const char *name, uint64_t __unused); diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c index a436fef8b53..6cb9d867230 100644 --- a/sys/boot/zfs/zfsimpl.c +++ b/sys/boot/zfs/zfsimpl.c @@ -68,7 +68,7 @@ static const char *features_for_read[] = { */ static spa_list_t zfs_pools; -static const dnode_phys_t *dnode_cache_obj = NULL; +static const dnode_phys_t *dnode_cache_obj; static uint64_t dnode_cache_bn; static char *dnode_cache_buf; static char *zap_scratch; @@ -523,12 +523,11 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, int nkids, i, is_new; uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present; - if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, - DATA_TYPE_UINT64, 0, &guid) - || nvlist_find(nvlist, ZPOOL_CONFIG_ID, - DATA_TYPE_UINT64, 0, &id) - || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, - DATA_TYPE_STRING, 0, &type)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, + NULL, &guid) + || nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id) + || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING, + NULL, &type)) { printf("ZFS: can't find vdev details\n"); return (ENOENT); } @@ -546,15 +545,15 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0; - nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, 0, + nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL, &is_offline); - nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, 0, + nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL, &is_removed); - nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, 0, + nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL, &is_faulted); - nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, 0, + nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, NULL, &is_degraded); - nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, 0, + nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, NULL, &isnt_present); vdev = vdev_find(guid); @@ -573,17 +572,19 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, vdev->v_id = id; vdev->v_top = pvdev != NULL ? pvdev : vdev; if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT, - DATA_TYPE_UINT64, 0, &ashift) == 0) + DATA_TYPE_UINT64, NULL, &ashift) == 0) { vdev->v_ashift = ashift; - else + } else { vdev->v_ashift = 0; + } if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY, - DATA_TYPE_UINT64, 0, &nparity) == 0) + DATA_TYPE_UINT64, NULL, &nparity) == 0) { vdev->v_nparity = nparity; - else + } else { vdev->v_nparity = 0; + } if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH, - DATA_TYPE_STRING, 0, &path) == 0) { + DATA_TYPE_STRING, NULL, &path) == 0) { if (strncmp(path, "/dev/", 5) == 0) path += 5; vdev->v_name = strdup(path); @@ -625,8 +626,8 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, vdev->v_state = VDEV_STATE_CANT_OPEN; } - rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, - DATA_TYPE_NVLIST_ARRAY, &nkids, &kids); + rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, + &nkids, &kids); /* * Its ok if we don't have any kids. */ @@ -744,12 +745,17 @@ spa_get_primary_vdev(const spa_t *spa) #endif static spa_t * -spa_create(uint64_t guid) +spa_create(uint64_t guid, const char *name) { spa_t *spa; - spa = malloc(sizeof(spa_t)); + if ((spa = malloc(sizeof(spa_t))) == NULL) + return (NULL); memset(spa, 0, sizeof(spa_t)); + if ((spa->spa_name = strdup(name)) == NULL) { + free(spa); + return (NULL); + } STAILQ_INIT(&spa->spa_vdevs); spa->spa_guid = guid; STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link); @@ -905,24 +911,39 @@ spa_all_status(void) return (ret); } +uint64_t +vdev_label_offset(uint64_t psize, int l, uint64_t offset) +{ + uint64_t label_offset; + + if (l < VDEV_LABELS / 2) + label_offset = 0; + else + label_offset = psize - VDEV_LABELS * sizeof (vdev_label_t); + + return (offset + l * sizeof (vdev_label_t) + label_offset); +} + static int vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) { vdev_t vtmp; vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch; + vdev_phys_t *tmp_label; spa_t *spa; vdev_t *vdev, *top_vdev, *pool_vdev; off_t off; blkptr_t bp; - const unsigned char *nvlist; + const unsigned char *nvlist = NULL; uint64_t val; uint64_t guid; + uint64_t best_txg = 0; uint64_t pool_txg, pool_guid; - uint64_t is_log; + uint64_t psize; const char *pool_name; const unsigned char *vdevs; const unsigned char *features; - int i, rc, is_newer; + int i, l, rc, is_newer; char *upbuf; const struct uberblock *up; @@ -933,26 +954,56 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) memset(&vtmp, 0, sizeof(vtmp)); vtmp.v_phys_read = _read; vtmp.v_read_priv = read_priv; - off = offsetof(vdev_label_t, vl_vdev_phys); - BP_ZERO(&bp); - BP_SET_LSIZE(&bp, sizeof(vdev_phys_t)); - BP_SET_PSIZE(&bp, sizeof(vdev_phys_t)); - BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); - BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); - DVA_SET_OFFSET(BP_IDENTITY(&bp), off); - ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); - if (vdev_read_phys(&vtmp, &bp, vdev_label, off, 0)) - return (EIO); + psize = P2ALIGN(ldi_get_size(read_priv), + (uint64_t)sizeof (vdev_label_t)); - if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) { + /* Test for minimum pool size. */ + if (psize < SPA_MINDEVSIZE) return (EIO); + + tmp_label = zfs_alloc(sizeof(vdev_phys_t)); + + for (l = 0; l < VDEV_LABELS; l++) { + off = vdev_label_offset(psize, l, + offsetof(vdev_label_t, vl_vdev_phys)); + + BP_ZERO(&bp); + BP_SET_LSIZE(&bp, sizeof(vdev_phys_t)); + BP_SET_PSIZE(&bp, sizeof(vdev_phys_t)); + BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); + BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); + DVA_SET_OFFSET(BP_IDENTITY(&bp), off); + ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); + + if (vdev_read_phys(&vtmp, &bp, tmp_label, off, 0)) + continue; + + if (tmp_label->vp_nvlist[0] != NV_ENCODE_XDR) + continue; + + nvlist = (const unsigned char *) tmp_label->vp_nvlist + 4; + if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, + DATA_TYPE_UINT64, NULL, &pool_txg) != 0) + continue; + + if (best_txg <= pool_txg) { + best_txg = pool_txg; + memcpy(vdev_label, tmp_label, sizeof (vdev_phys_t)); + } } + zfs_free(tmp_label, sizeof (vdev_phys_t)); + + if (best_txg == 0) + return (EIO); + + if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) + return (EIO); + nvlist = (const unsigned char *) vdev_label->vp_nvlist + 4; - if (nvlist_find(nvlist, - ZPOOL_CONFIG_VERSION, - DATA_TYPE_UINT64, 0, &val)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64, + NULL, &val) != 0) { return (EIO); } @@ -963,15 +1014,14 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) } /* Check ZFS features for read */ - if (nvlist_find(nvlist, - ZPOOL_CONFIG_FEATURES_FOR_READ, - DATA_TYPE_NVLIST, 0, &features) == 0 - && nvlist_check_features_for_read(features) != 0) + if (nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ, + DATA_TYPE_NVLIST, NULL, &features) == 0 && + nvlist_check_features_for_read(features) != 0) { return (EIO); + } - if (nvlist_find(nvlist, - ZPOOL_CONFIG_POOL_STATE, - DATA_TYPE_UINT64, 0, &val)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64, + NULL, &val) != 0) { return (EIO); } @@ -980,15 +1030,12 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) return (EIO); } - if (nvlist_find(nvlist, - ZPOOL_CONFIG_POOL_TXG, - DATA_TYPE_UINT64, 0, &pool_txg) - || nvlist_find(nvlist, - ZPOOL_CONFIG_POOL_GUID, - DATA_TYPE_UINT64, 0, &pool_guid) - || nvlist_find(nvlist, - ZPOOL_CONFIG_POOL_NAME, - DATA_TYPE_STRING, 0, &pool_name)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64, + NULL, &pool_txg) != 0 || + nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, + NULL, &pool_guid) != 0 || + nvlist_find(nvlist, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING, + NULL, &pool_name) != 0) { /* * Cache and spare devices end up here - just ignore * them. @@ -997,25 +1044,26 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) return (EIO); } - is_log = 0; - (void) nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, 0, - &is_log); - if (is_log) + if (nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, + NULL, &val) == 0 && val != 0) { return (EIO); + } /* * Create the pool if this is the first time we've seen it. */ spa = spa_find_by_guid(pool_guid); - if (!spa) { - spa = spa_create(pool_guid); - spa->spa_name = strdup(pool_name); + if (spa == NULL) { + spa = spa_create(pool_guid, pool_name); + if (spa == NULL) + return (ENOMEM); } if (pool_txg > spa->spa_txg) { spa->spa_txg = pool_txg; is_newer = 1; - } else + } else { is_newer = 0; + } /* * Get the vdev tree and create our in-core copy of it. @@ -1023,23 +1071,21 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) * be some kind of alias (overlapping slices, dangerously dedicated * disks etc). */ - if (nvlist_find(nvlist, - ZPOOL_CONFIG_GUID, - DATA_TYPE_UINT64, 0, &guid)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, + NULL, &guid) != 0) { return (EIO); } vdev = vdev_find(guid); if (vdev && vdev->v_phys_read) /* Has this vdev already been inited? */ return (EIO); - if (nvlist_find(nvlist, - ZPOOL_CONFIG_VDEV_TREE, - DATA_TYPE_NVLIST, 0, &vdevs)) { + if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, + NULL, &vdevs)) { return (EIO); } rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer); - if (rc) + if (rc != 0) return (rc); /* @@ -1079,36 +1125,37 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) */ upbuf = zfs_alloc(VDEV_UBERBLOCK_SIZE(vdev)); up = (const struct uberblock *)upbuf; - for (i = 0; - i < VDEV_UBERBLOCK_COUNT(vdev); - i++) { - off = VDEV_UBERBLOCK_OFFSET(vdev, i); - BP_ZERO(&bp); - DVA_SET_OFFSET(&bp.blk_dva[0], off); - BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); - BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); - BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); - BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); - ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); - - if (vdev_read_phys(vdev, &bp, upbuf, off, 0)) - continue; + for (l = 0; l < VDEV_LABELS; l++) { + for (i = 0; i < VDEV_UBERBLOCK_COUNT(vdev); i++) { + off = vdev_label_offset(psize, l, + VDEV_UBERBLOCK_OFFSET(vdev, i)); + BP_ZERO(&bp); + DVA_SET_OFFSET(&bp.blk_dva[0], off); + BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); + BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev)); + BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); + BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); + ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); + + if (vdev_read_phys(vdev, &bp, upbuf, off, 0)) + continue; - if (up->ub_magic != UBERBLOCK_MAGIC) - continue; - if (up->ub_txg < spa->spa_txg) - continue; - if (up->ub_txg > spa->spa_uberblock.ub_txg) { - spa->spa_uberblock = *up; - } else if (up->ub_txg == spa->spa_uberblock.ub_txg) { - if (up->ub_timestamp > spa->spa_uberblock.ub_timestamp) + if (up->ub_magic != UBERBLOCK_MAGIC) + continue; + if (up->ub_txg < spa->spa_txg) + continue; + if (up->ub_txg > spa->spa_uberblock.ub_txg || + (up->ub_txg == spa->spa_uberblock.ub_txg && + up->ub_timestamp > + spa->spa_uberblock.ub_timestamp)) { spa->spa_uberblock = *up; + } } } zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev)); vdev->spa = spa; - if (spap) + if (spap != NULL) *spap = spa; return (0); } -- 2.45.0