From owner-svn-src-head@FreeBSD.ORG Thu Jul 15 10:37:49 2010 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 94FDA1065672; Thu, 15 Jul 2010 10:37:49 +0000 (UTC) (envelope-from bschmidt@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 82A308FC1B; Thu, 15 Jul 2010 10:37:49 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o6FAbntR061465; Thu, 15 Jul 2010 10:37:49 GMT (envelope-from bschmidt@svn.freebsd.org) Received: (from bschmidt@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o6FAbnmA061461; Thu, 15 Jul 2010 10:37:49 GMT (envelope-from bschmidt@svn.freebsd.org) Message-Id: <201007151037.o6FAbnmA061461@svn.freebsd.org> From: Bernhard Schmidt Date: Thu, 15 Jul 2010 10:37:49 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r210111 - head/sys/dev/iwn X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 15 Jul 2010 10:37:49 -0000 Author: bschmidt Date: Thu Jul 15 10:37:49 2010 New Revision: 210111 URL: http://svn.freebsd.org/changeset/base/210111 Log: Add support for firmware images in "type-length-value" format. Obtained from: OpenBSD MFC after: 2 weeks Modified: head/sys/dev/iwn/if_iwn.c head/sys/dev/iwn/if_iwnreg.h head/sys/dev/iwn/if_iwnvar.h Modified: head/sys/dev/iwn/if_iwn.c ============================================================================== --- head/sys/dev/iwn/if_iwn.c Thu Jul 15 09:34:00 2010 (r210110) +++ head/sys/dev/iwn/if_iwn.c Thu Jul 15 10:37:49 2010 (r210111) @@ -231,6 +231,10 @@ static int iwn4965_load_firmware(struct static int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, const uint8_t *, int); static int iwn5000_load_firmware(struct iwn_softc *); +static int iwn_read_firmware_leg(struct iwn_softc *, + struct iwn_fw_info *); +static int iwn_read_firmware_tlv(struct iwn_softc *, + struct iwn_fw_info *, uint16_t); static int iwn_read_firmware(struct iwn_softc *); static int iwn_clock_wait(struct iwn_softc *); static int iwn_apm_init(struct iwn_softc *); @@ -5644,39 +5648,19 @@ iwn5000_load_firmware(struct iwn_softc * return 0; } +/* + * Extract text and data sections from a legacy firmware image. + */ static int -iwn_read_firmware(struct iwn_softc *sc) +iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) { - const struct iwn_hal *hal = sc->sc_hal; - struct iwn_fw_info *fw = &sc->fw; const uint32_t *ptr; + size_t hdrlen = 24; uint32_t rev; - size_t size; - - IWN_UNLOCK(sc); - - /* Read firmware image from filesystem. */ - sc->fw_fp = firmware_get(sc->fwname); - if (sc->fw_fp == NULL) { - device_printf(sc->sc_dev, - "%s: could not load firmare image \"%s\"\n", __func__, - sc->fwname); - IWN_LOCK(sc); - return EINVAL; - } - IWN_LOCK(sc); - - size = sc->fw_fp->datasize; - if (size < 28) { - device_printf(sc->sc_dev, - "%s: truncated firmware header: %zu bytes\n", - __func__, size); - return EINVAL; - } - /* Process firmware header. */ ptr = (const uint32_t *)sc->fw_fp->data; rev = le32toh(*ptr++); + /* Check firmware API version. */ if (IWN_FW_API(rev) <= 1) { device_printf(sc->sc_dev, @@ -5685,34 +5669,27 @@ iwn_read_firmware(struct iwn_softc *sc) } if (IWN_FW_API(rev) >= 3) { /* Skip build number (version 2 header). */ - size -= 4; + hdrlen += 4; ptr++; } + if (fw->size < hdrlen) { + device_printf(sc->sc_dev, + "%s: firmware file too short: %zu bytes\n", + __func__, fw->size); + return EINVAL; + } fw->main.textsz = le32toh(*ptr++); fw->main.datasz = le32toh(*ptr++); fw->init.textsz = le32toh(*ptr++); fw->init.datasz = le32toh(*ptr++); fw->boot.textsz = le32toh(*ptr++); - size -= 24; - - /* Sanity-check firmware header. */ - if (fw->main.textsz > hal->fw_text_maxsz || - fw->main.datasz > hal->fw_data_maxsz || - fw->init.textsz > hal->fw_text_maxsz || - fw->init.datasz > hal->fw_data_maxsz || - fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || - (fw->boot.textsz & 3) != 0) { - device_printf(sc->sc_dev, "%s: invalid firmware header\n", - __func__); - return EINVAL; - } /* Check that all firmware sections fit. */ - if (fw->main.textsz + fw->main.datasz + fw->init.textsz + - fw->init.datasz + fw->boot.textsz > size) { + if (fw->size < hdrlen + fw->main.textsz + fw->main.datasz + + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { device_printf(sc->sc_dev, "%s: firmware file too short: %zu bytes\n", - __func__, size); + __func__, fw->size); return EINVAL; } @@ -5726,6 +5703,151 @@ iwn_read_firmware(struct iwn_softc *sc) return 0; } +/* + * Extract text and data sections from a TLV firmware image. + */ +int +iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, + uint16_t alt) +{ + const struct iwn_fw_tlv_hdr *hdr; + const struct iwn_fw_tlv *tlv; + const uint8_t *ptr, *end; + uint64_t altmask; + uint32_t len; + + if (fw->size < sizeof (*hdr)) { + device_printf(sc->sc_dev, + "%s: firmware file too short: %zu bytes\n", + __func__, fw->size); + return EINVAL; + } + hdr = (const struct iwn_fw_tlv_hdr *)fw->data; + if (hdr->signature != htole32(IWN_FW_SIGNATURE)) { + device_printf(sc->sc_dev, + "%s: bad firmware file signature 0x%08x\n", + __func__, le32toh(hdr->signature)); + return EINVAL; + } + + /* + * Select the closest supported alternative that is less than + * or equal to the specified one. + */ + altmask = le64toh(hdr->altmask); + while (alt > 0 && !(altmask & (1ULL << alt))) + alt--; /* Downgrade. */ + + ptr = (const uint8_t *)(hdr + 1); + end = (const uint8_t *)(fw->data + fw->size); + + /* Parse type-length-value fields. */ + while (ptr + sizeof (*tlv) <= end) { + tlv = (const struct iwn_fw_tlv *)ptr; + len = le32toh(tlv->len); + + ptr += sizeof (*tlv); + if (ptr + len > end) { + device_printf(sc->sc_dev, + "%s: firmware file too short: %zu bytes\n", + __func__, fw->size); + return EINVAL; + } + /* Skip other alternatives. */ + if (tlv->alt != 0 && tlv->alt != htole16(alt)) + goto next; + + switch (le16toh(tlv->type)) { + case IWN_FW_TLV_MAIN_TEXT: + fw->main.text = ptr; + fw->main.textsz = len; + break; + case IWN_FW_TLV_MAIN_DATA: + fw->main.data = ptr; + fw->main.datasz = len; + break; + case IWN_FW_TLV_INIT_TEXT: + fw->init.text = ptr; + fw->init.textsz = len; + break; + case IWN_FW_TLV_INIT_DATA: + fw->init.data = ptr; + fw->init.datasz = len; + break; + case IWN_FW_TLV_BOOT_TEXT: + fw->boot.text = ptr; + fw->boot.textsz = len; + break; + default: + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: TLV type %d not handled\n", + __func__, le16toh(tlv->type)); + break; + } +next: /* TLV fields are 32-bit aligned. */ + ptr += (len + 3) & ~3; + } + return 0; +} + +static int +iwn_read_firmware(struct iwn_softc *sc) +{ + const struct iwn_hal *hal = sc->sc_hal; + struct iwn_fw_info *fw = &sc->fw; + int error; + + IWN_UNLOCK(sc); + + memset(fw, 0, sizeof (*fw)); + + /* Read firmware image from filesystem. */ + sc->fw_fp = firmware_get(sc->fwname); + if (sc->fw_fp == NULL) { + device_printf(sc->sc_dev, + "%s: could not load firmare image \"%s\"\n", __func__, + sc->fwname); + IWN_LOCK(sc); + return EINVAL; + } + IWN_LOCK(sc); + + fw->size = sc->fw_fp->datasize; + fw->data = (const uint8_t *)sc->fw_fp->data; + if (fw->size < sizeof (uint32_t)) { + device_printf(sc->sc_dev, + "%s: firmware file too short: %zu bytes\n", + __func__, fw->size); + return EINVAL; + } + + /* Retrieve text and data sections. */ + if (*(const uint32_t *)fw->data != 0) /* Legacy image. */ + error = iwn_read_firmware_leg(sc, fw); + else + error = iwn_read_firmware_tlv(sc, fw, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not read firmware sections\n", __func__); + return error; + } + + /* Make sure text and data sections fit in hardware memory. */ + if (fw->main.textsz > hal->fw_text_maxsz || + fw->main.datasz > hal->fw_data_maxsz || + fw->init.textsz > hal->fw_text_maxsz || + fw->init.datasz > hal->fw_data_maxsz || + fw->boot.textsz > IWN_FW_BOOT_TEXT_MAXSZ || + (fw->boot.textsz & 3) != 0) { + device_printf(sc->sc_dev, + "%s: firmware sections too large\n", __func__); + return EINVAL; + } + + /* We can proceed with loading the firmware. */ + return 0; +} + static int iwn_clock_wait(struct iwn_softc *sc) { Modified: head/sys/dev/iwn/if_iwnreg.h ============================================================================== --- head/sys/dev/iwn/if_iwnreg.h Thu Jul 15 09:34:00 2010 (r210110) +++ head/sys/dev/iwn/if_iwnreg.h Thu Jul 15 10:37:49 2010 (r210111) @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_iwnreg.h,v 1.38 2010/04/10 08:37:36 damien Exp $ */ +/* $OpenBSD: if_iwnreg.h,v 1.40 2010/05/05 19:41:57 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -1260,6 +1260,34 @@ struct iwn_fw_dump { uint32_t time[2]; } __packed; +/* TLV firmware header. */ +struct iwn_fw_tlv_hdr { + uint32_t zero; /* Always 0, to differentiate from legacy. */ + uint32_t signature; +#define IWN_FW_SIGNATURE 0x0a4c5749 /* "IWL\n" */ + + uint8_t descr[64]; + uint32_t rev; +#define IWN_FW_API(x) (((x) >> 8) & 0xff) + + uint32_t build; + uint64_t altmask; +} __packed; + +/* TLV header. */ +struct iwn_fw_tlv { + uint16_t type; +#define IWN_FW_TLV_MAIN_TEXT 1 +#define IWN_FW_TLV_MAIN_DATA 2 +#define IWN_FW_TLV_INIT_TEXT 3 +#define IWN_FW_TLV_INIT_DATA 4 +#define IWN_FW_TLV_BOOT_TEXT 5 +#define IWN_FW_TLV_PBREQ_MAXLEN 6 + + uint16_t alt; + uint32_t len; +} __packed; + #define IWN4965_FW_TEXT_MAXSZ ( 96 * 1024) #define IWN4965_FW_DATA_MAXSZ ( 40 * 1024) #define IWN5000_FW_TEXT_MAXSZ (256 * 1024) @@ -1268,8 +1296,6 @@ struct iwn_fw_dump { #define IWN4965_FWSZ (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ) #define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ -#define IWN_FW_API(x) (((x) >> 8) & 0xff) - /* * Offsets into EEPROM. */ Modified: head/sys/dev/iwn/if_iwnvar.h ============================================================================== --- head/sys/dev/iwn/if_iwnvar.h Thu Jul 15 09:34:00 2010 (r210110) +++ head/sys/dev/iwn/if_iwnvar.h Thu Jul 15 10:37:49 2010 (r210111) @@ -1,5 +1,5 @@ /* $FreeBSD$ */ -/* $OpenBSD: if_iwnvar.h,v 1.17 2010/02/17 18:23:00 damien Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.18 2010/04/30 16:06:46 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -150,7 +150,8 @@ struct iwn_fw_part { }; struct iwn_fw_info { - u_char *data; + const uint8_t *data; + size_t size; struct iwn_fw_part init; struct iwn_fw_part main; struct iwn_fw_part boot;