xref: /freebsd-src/sys/dev/bhnd/cores/chipc/chipc_slicer.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
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$");
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 
57 static int	chipc_slicer_walk(device_t dev, struct resource *res,
58 		    struct flash_slice *slices, int *nslices);
59 
60 void
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);
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 
77 int
78 chipc_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 
102 int
103 chipc_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  */
136 static int
137 chipc_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