1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 /*
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
57 static int chipc_slicer_walk(device_t dev, struct resource *res,
58 struct flash_slice *slices, int *nslices);
59
60 void
chipc_register_slicer(chipc_flash flash_type)61 chipc_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, FLASH_SLICES_TYPE_SPI,
67 TRUE);
68 break;
69 case CHIPC_PFLASH_CFI:
70 flash_register_slicer(chipc_slicer_cfi, FLASH_SLICES_TYPE_CFI,
71 TRUE);
72 break;
73 default:
74 /* Unsupported */
75 break;
76 }
77 }
78
79 int
chipc_slicer_cfi(device_t dev,const char * provider __unused,struct flash_slice * slices,int * nslices)80 chipc_slicer_cfi(device_t dev, const char *provider __unused,
81 struct flash_slice *slices, int *nslices)
82 {
83 struct cfi_softc *sc;
84 device_t parent;
85
86 /* must be CFI flash */
87 if (device_get_devclass(dev) != devclass_find("cfi"))
88 return (ENXIO);
89
90 /* must be attached to chipc */
91 if ((parent = device_get_parent(dev)) == NULL) {
92 BHND_ERROR_DEV(dev, "no found ChipCommon device");
93 return (ENXIO);
94 }
95
96 if (device_get_devclass(parent) != devclass_find("bhnd_chipc")) {
97 BHND_ERROR_DEV(dev, "no found ChipCommon device");
98 return (ENXIO);
99 }
100
101 sc = device_get_softc(dev);
102 return (chipc_slicer_walk(dev, sc->sc_res, slices, nslices));
103 }
104
105 int
chipc_slicer_spi(device_t dev,const char * provider __unused,struct flash_slice * slices,int * nslices)106 chipc_slicer_spi(device_t dev, const char *provider __unused,
107 struct flash_slice *slices, int *nslices)
108 {
109 struct chipc_spi_softc *sc;
110 device_t chipc, spi, spibus;
111
112 BHND_DEBUG_DEV(dev, "initting SPI slicer: %s", device_get_name(dev));
113
114 /* must be SPI-attached flash */
115 spibus = device_get_parent(dev);
116 if (spibus == NULL) {
117 BHND_ERROR_DEV(dev, "no found ChipCommon SPI BUS device");
118 return (ENXIO);
119 }
120
121 spi = device_get_parent(spibus);
122 if (spi == NULL) {
123 BHND_ERROR_DEV(dev, "no found ChipCommon SPI device");
124 return (ENXIO);
125 }
126
127 chipc = device_get_parent(spi);
128 if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) {
129 BHND_ERROR_DEV(dev, "no found ChipCommon device");
130 return (ENXIO);
131 }
132
133 sc = device_get_softc(spi);
134 return (chipc_slicer_walk(dev, sc->sc_flash_res, slices, nslices));
135 }
136
137 /*
138 * Main processing part
139 */
140 static int
chipc_slicer_walk(device_t dev,struct resource * res,struct flash_slice * slices,int * nslices)141 chipc_slicer_walk(device_t dev, struct resource *res,
142 struct flash_slice *slices, int *nslices)
143 {
144 uint32_t fw_len;
145 uint32_t fs_ofs;
146 uint32_t val;
147 uint32_t ofs_trx;
148 int flash_size;
149
150 *nslices = 0;
151
152 flash_size = rman_get_size(res);
153 ofs_trx = flash_size;
154
155 BHND_TRACE_DEV(dev, "slicer: scanning memory [%x bytes] for headers...",
156 flash_size);
157
158 /* Find FW header in flash memory with step=128Kb (0x1000) */
159 for(uint32_t ofs = 0; ofs < flash_size; ofs+= 0x1000){
160 val = bus_read_4(res, ofs);
161 switch (val) {
162 case TRX_MAGIC:
163 /* check for second TRX */
164 if (ofs_trx < ofs) {
165 BHND_TRACE_DEV(dev, "stop on 2nd TRX: %x", ofs);
166 break;
167 }
168
169 BHND_TRACE("TRX found: %x", ofs);
170 ofs_trx = ofs;
171 /* read last offset of TRX header */
172 fs_ofs = bus_read_4(res, ofs + 24);
173 BHND_TRACE("FS offset: %x", fs_ofs);
174
175 /*
176 * GEOM IO will panic if offset is not aligned
177 * on sector size, i.e. 512 bytes
178 */
179 if (fs_ofs % 0x200 != 0) {
180 BHND_WARN("WARNING! filesystem offset should be"
181 " aligned on sector size (%d bytes)", 0x200);
182 BHND_WARN("ignoring TRX firmware image");
183 break;
184 }
185
186 slices[*nslices].base = ofs + fs_ofs;
187 //XXX: fully sized? any other partition?
188 fw_len = bus_read_4(res, ofs + 4);
189 slices[*nslices].size = fw_len - fs_ofs;
190 slices[*nslices].label = "rootfs";
191 *nslices += 1;
192 break;
193 case CFE_MAGIC:
194 BHND_TRACE("CFE found: %x", ofs);
195 break;
196 case NVRAM_MAGIC:
197 BHND_TRACE("NVRAM found: %x", ofs);
198 break;
199 default:
200 break;
201 }
202 }
203
204 BHND_TRACE("slicer: done");
205 return (0);
206 }
207