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