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