1/*-
2 * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: releng/11.0/sys/dev/bhnd/cores/chipc/chipc_slicer.c 302189 2016-06-25 04:33:00Z landonf $");
32
33/*
34 * Slicer is required to split firmware images into pieces.
35 * The first supported FW is TRX-based used by Asus routers
36 * TODO: add NetGear FW (CHK)
37 */
38
39#include <sys/param.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/errno.h>
43#include <sys/rman.h>
44#include <sys/bus.h>
45#include <sys/systm.h>
46#include <sys/slicer.h>
47
48#include <machine/bus.h>
49
50#include <dev/bhnd/bhnd_debug.h>
51
52#include "chipc_slicer.h"
53
54#include <dev/cfi/cfi_var.h>
55#include "chipc_spi.h"
56
57static int	chipc_slicer_walk(device_t dev, struct resource *res,
58		    struct flash_slice *slices, int *nslices);
59
60void
61chipc_register_slicer(chipc_flash flash_type)
62{
63	switch (flash_type) {
64	case CHIPC_SFLASH_AT:
65	case CHIPC_SFLASH_ST:
66		flash_register_slicer(chipc_slicer_spi);
67		break;
68	case CHIPC_PFLASH_CFI:
69		flash_register_slicer(chipc_slicer_cfi);
70		break;
71	default:
72		/* Unsupported */
73		break;
74	}
75}
76
77int
78chipc_slicer_cfi(device_t dev, struct flash_slice *slices, int *nslices)
79{
80	struct cfi_softc	*sc;
81	device_t		 parent;
82
83	/* must be CFI flash */
84	if (device_get_devclass(dev) != devclass_find("cfi"))
85		return (ENXIO);
86
87	/* must be attached to chipc */
88	if ((parent = device_get_parent(dev)) == NULL) {
89		BHND_ERROR_DEV(dev, "no found ChipCommon device");
90		return (ENXIO);
91	}
92
93	if (device_get_devclass(parent) != devclass_find("bhnd_chipc")) {
94		BHND_ERROR_DEV(dev, "no found ChipCommon device");
95		return (ENXIO);
96	}
97
98	sc = device_get_softc(dev);
99	return (chipc_slicer_walk(dev, sc->sc_res, slices, nslices));
100}
101
102int
103chipc_slicer_spi(device_t dev, struct flash_slice *slices, int *nslices)
104{
105	struct chipc_spi_softc	*sc;
106	device_t		 chipc, spi, spibus;
107
108	BHND_DEBUG_DEV(dev, "initting SPI slicer: %s", device_get_name(dev));
109
110	/* must be SPI-attached flash */
111	spibus = device_get_parent(dev);
112	if (spibus == NULL) {
113		BHND_ERROR_DEV(dev, "no found ChipCommon SPI BUS device");
114		return (ENXIO);
115	}
116
117	spi = device_get_parent(spibus);
118	if (spi == NULL) {
119		BHND_ERROR_DEV(dev, "no found ChipCommon SPI device");
120		return (ENXIO);
121	}
122
123	chipc = device_get_parent(spi);
124	if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) {
125		BHND_ERROR_DEV(dev, "no found ChipCommon device");
126		return (ENXIO);
127	}
128
129	sc = device_get_softc(spi);
130	return (chipc_slicer_walk(dev, sc->sc_flash_res, slices, nslices));
131}
132
133/*
134 * Main processing part
135 */
136static int
137chipc_slicer_walk(device_t dev, struct resource *res,
138    struct flash_slice *slices, int *nslices)
139{
140	uint32_t	 fw_len;
141	uint32_t	 fs_ofs;
142	uint32_t	 val;
143	uint32_t	 ofs_trx;
144	int		 flash_size;
145
146	*nslices = 0;
147
148	flash_size = rman_get_size(res);
149	ofs_trx = flash_size;
150
151	BHND_TRACE_DEV(dev, "slicer: scanning memory [%x bytes] for headers...",
152	    flash_size);
153
154	/* Find FW header in flash memory with step=128Kb (0x1000) */
155	for(uint32_t ofs = 0; ofs < flash_size; ofs+= 0x1000){
156		val = bus_read_4(res, ofs);
157		switch (val) {
158		case TRX_MAGIC:
159			/* check for second TRX */
160			if (ofs_trx < ofs) {
161				BHND_TRACE_DEV(dev, "stop on 2nd TRX: %x", ofs);
162				break;
163			}
164
165			BHND_TRACE("TRX found: %x", ofs);
166			ofs_trx = ofs;
167			/* read last offset of TRX header */
168			fs_ofs = bus_read_4(res, ofs + 24);
169			BHND_TRACE("FS offset: %x", fs_ofs);
170
171			/*
172			 * GEOM IO will panic if offset is not aligned
173			 * on sector size, i.e. 512 bytes
174			 */
175			if (fs_ofs % 0x200 != 0) {
176				BHND_WARN("WARNING! filesystem offset should be"
177				    " aligned on sector size (%d bytes)", 0x200);
178				BHND_WARN("ignoring TRX firmware image");
179				break;
180			}
181
182			slices[*nslices].base = ofs + fs_ofs;
183			//XXX: fully sized? any other partition?
184			fw_len = bus_read_4(res, ofs + 4);
185			slices[*nslices].size = fw_len - fs_ofs;
186			slices[*nslices].label = "rootfs";
187			*nslices += 1;
188			break;
189		case CFE_MAGIC:
190			BHND_TRACE("CFE found: %x", ofs);
191			break;
192		case NVRAM_MAGIC:
193			BHND_TRACE("NVRAM found: %x", ofs);
194			break;
195		default:
196			break;
197		}
198	}
199
200	BHND_TRACE("slicer: done");
201	return (0);
202}
203