1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2020 Rockchip Electronics Co., Ltd 4 */ 5 #include <common.h> 6 #include <dm.h> 7 #include <misc.h> 8 #include <dm/uclass.h> 9 #include <dm/uclass-internal.h> 10 11 #define HEAD_CRC 2 12 #define EXTRA_FIELD 4 13 #define ORIG_NAME 8 14 #define COMMENT 0x10 15 #define RESERVED 0xe0 16 #define DEFLATED 8 17 18 static u32 misc_decomp_async, misc_decomp_sync; 19 20 static void decomp_set_flags(u32 *flags, u8 comp) 21 { 22 if (comp == IH_COMP_GZIP) 23 *flags |= DECOM_GZIP; 24 else if (comp == IH_COMP_LZ4) 25 *flags |= DECOM_LZ4; 26 } 27 28 void misc_decompress_async(u8 comp) 29 { 30 decomp_set_flags(&misc_decomp_async, comp); 31 } 32 33 void misc_decompress_sync(u8 comp) 34 { 35 decomp_set_flags(&misc_decomp_sync, comp); 36 } 37 38 static int misc_gzip_parse_header(const unsigned char *src, unsigned long len) 39 { 40 int i, flags; 41 42 /* skip header */ 43 i = 10; 44 flags = src[3]; 45 if (src[2] != DEFLATED || (flags & RESERVED) != 0) { 46 debug("Error: Bad gzipped data\n"); 47 return (-1); 48 } 49 if ((flags & EXTRA_FIELD) != 0) 50 i = 12 + src[10] + (src[11] << 8); 51 if ((flags & ORIG_NAME) != 0) 52 while (src[i++] != 0) 53 ; 54 if ((flags & COMMENT) != 0) 55 while (src[i++] != 0) 56 ; 57 if ((flags & HEAD_CRC) != 0) 58 i += 2; 59 if (i >= len) { 60 puts("Error: gunzip out of data in header\n"); 61 return (-1); 62 } 63 return i; 64 } 65 66 static u32 misc_get_data_size(unsigned long src, unsigned long len, u32 comp) 67 { 68 if (comp == DECOM_GZIP) 69 return *(u32 *)(src + len - 4); 70 71 return 0; 72 } 73 74 static void misc_setup_default_sync(u32 comp) 75 { 76 if (comp == DECOM_GZIP) 77 misc_decompress_sync(IH_COMP_GZIP); 78 else if (comp == DECOM_LZ4) 79 misc_decompress_sync(IH_COMP_LZ4); 80 } 81 82 static struct udevice *misc_decompress_get_device(u32 comp) 83 { 84 return misc_get_device_by_capability(comp); 85 } 86 87 static int misc_decompress_start(struct udevice *dev, unsigned long dst, 88 unsigned long src, unsigned long src_len, 89 u32 flags) 90 { 91 struct decom_param param; 92 93 param.addr_dst = dst; 94 param.addr_src = src; 95 param.flags = flags; 96 if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) { 97 param.mode = DECOM_GZIP; 98 } else { 99 printf("Unsupported decompression format.\n"); 100 return -EPERM; 101 } 102 103 param.size_src = src_len; 104 param.size_dst = misc_get_data_size(src, src_len, param.mode); 105 106 if (!param.size_src || !param.size_dst) 107 return -EINVAL; 108 109 return misc_ioctl(dev, IOCTL_REQ_START, ¶m); 110 } 111 112 static int misc_decompress_stop(struct udevice *dev) 113 { 114 return misc_ioctl(dev, IOCTL_REQ_STOP, NULL); 115 } 116 117 static bool misc_decompress_is_complete(struct udevice *dev) 118 { 119 if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL)) 120 return false; 121 else 122 return true; 123 } 124 125 static int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 comp) 126 { 127 struct decom_param param; 128 int ret; 129 130 param.mode = comp; 131 param.size_dst = 0; /* clear */ 132 133 ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, ¶m); 134 if (!ret) 135 *size = param.size_dst; 136 137 return ret; 138 } 139 140 static int misc_decompress_finish(struct udevice *dev, u32 comp) 141 { 142 int timeout = 20000; 143 144 while (!misc_decompress_is_complete(dev)) { 145 if (timeout < 0) 146 return -EIO; 147 timeout--; 148 udelay(10); 149 } 150 151 return misc_decompress_stop(dev); 152 } 153 154 int misc_decompress_cleanup(void) 155 { 156 const struct misc_ops *ops; 157 struct udevice *dev; 158 struct uclass *uc; 159 int ret; 160 u32 comp; 161 162 ret = uclass_get(UCLASS_MISC, &uc); 163 if (ret) 164 return 0; 165 166 /* use "find_" */ 167 for (uclass_find_first_device(UCLASS_MISC, &dev); 168 dev; 169 uclass_find_next_device(&dev)) { 170 ops = device_get_ops(dev); 171 if (!ops || !ops->ioctl) 172 continue; 173 else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &comp)) 174 continue; 175 else if (misc_decomp_async & comp) 176 continue; 177 178 if (misc_decomp_sync & comp) { 179 ret = misc_decompress_finish(dev, comp); 180 if (ret) { 181 printf("Failed to stop decompress: %s, ret=%d\n", 182 dev->name, ret); 183 return ret; 184 } 185 } 186 } 187 188 return 0; 189 } 190 191 int misc_decompress_process(unsigned long dst, unsigned long src, 192 unsigned long src_len, u32 comp, bool sync, 193 u64 *size, u32 flags) 194 { 195 struct udevice *dev; 196 ulong dst_org = dst; 197 u64 dst_size = 0; 198 int ret; 199 200 dev = misc_decompress_get_device(comp); 201 if (!dev) 202 return -ENODEV; 203 204 /* Wait last finish */ 205 ret = misc_decompress_finish(dev, comp); 206 if (ret) 207 return ret; 208 209 /* 210 * Check if ARCH_DMA_MINALIGN aligned, otherwise use sync action 211 * for output data memcpy. 212 */ 213 if (!IS_ALIGNED(dst, ARCH_DMA_MINALIGN)) { 214 dst_org = dst; 215 dst = ALIGN(dst, ARCH_DMA_MINALIGN); 216 sync = true; 217 } 218 219 ret = misc_decompress_start(dev, dst, src, src_len, flags); 220 if (ret) 221 return ret; 222 223 /* 224 * Wait this round finish ? 225 * 226 * If sync, return original data length after decompress done. 227 * otherwise return from compressed file information. 228 */ 229 if (sync) { 230 ret = misc_decompress_finish(dev, comp); 231 if (ret) 232 return ret; 233 234 if (size || (dst != dst_org)) { 235 ret = misc_decompress_data_size(dev, &dst_size, comp); 236 if (size) 237 *size = dst_size; 238 if (dst != dst_org) 239 memcpy((char *)dst_org, 240 (const char *)dst, dst_size); 241 } 242 } else { 243 /* 244 * setup cleanup sync flags by default if this is a sync request, 245 * unless misc_decompress_async() is called by manual. 246 * 247 * This avoid caller to forget to setup cleanup sync flags when 248 * they use a async operation, otherwise cpu jump to kernel 249 * before decompress done. 250 */ 251 misc_setup_default_sync(comp); 252 253 if (size) 254 *size = misc_get_data_size(src, src_len, comp); 255 } 256 257 return ret; 258 } 259