svn commit: r210672 - stable/8/sys/dev/iwn

Bernhard Schmidt bschmidt at FreeBSD.org
Sat Jul 31 10:16:42 UTC 2010


Author: bschmidt
Date: Sat Jul 31 10:16:41 2010
New Revision: 210672
URL: http://svn.freebsd.org/changeset/base/210672

Log:
  MFC r210111:
  Add support for firmware images in "type-length-value" format.
  
  Obtained from:	OpenBSD

Modified:
  stable/8/sys/dev/iwn/if_iwn.c
  stable/8/sys/dev/iwn/if_iwnreg.h
  stable/8/sys/dev/iwn/if_iwnvar.h
Directory Properties:
  stable/8/sys/   (props changed)
  stable/8/sys/amd64/include/xen/   (props changed)
  stable/8/sys/cddl/contrib/opensolaris/   (props changed)
  stable/8/sys/contrib/dev/acpica/   (props changed)
  stable/8/sys/contrib/pf/   (props changed)
  stable/8/sys/dev/xen/xenpci/   (props changed)

Modified: stable/8/sys/dev/iwn/if_iwn.c
==============================================================================
--- stable/8/sys/dev/iwn/if_iwn.c	Sat Jul 31 10:15:18 2010	(r210671)
+++ stable/8/sys/dev/iwn/if_iwn.c	Sat Jul 31 10:16:41 2010	(r210672)
@@ -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: stable/8/sys/dev/iwn/if_iwnreg.h
==============================================================================
--- stable/8/sys/dev/iwn/if_iwnreg.h	Sat Jul 31 10:15:18 2010	(r210671)
+++ stable/8/sys/dev/iwn/if_iwnreg.h	Sat Jul 31 10:16:41 2010	(r210672)
@@ -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: stable/8/sys/dev/iwn/if_iwnvar.h
==============================================================================
--- stable/8/sys/dev/iwn/if_iwnvar.h	Sat Jul 31 10:15:18 2010	(r210671)
+++ stable/8/sys/dev/iwn/if_iwnvar.h	Sat Jul 31 10:16:41 2010	(r210672)
@@ -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;


More information about the svn-src-stable-8 mailing list