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 cap) 67 { 68 if (cap == DECOM_GZIP) 69 return *(u32 *)(src + len - 4); 70 71 return 0; 72 } 73 74 struct udevice *misc_decompress_get_device(u32 capability) 75 { 76 return misc_get_device_by_capability(capability); 77 } 78 79 int misc_decompress_start(struct udevice *dev, unsigned long dst, 80 unsigned long src, unsigned long src_len) 81 { 82 struct decom_param param; 83 84 param.addr_dst = dst; 85 param.addr_src = src; 86 if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) { 87 param.mode = DECOM_GZIP; 88 } else { 89 printf("Unsupported decompression format.\n"); 90 return -EPERM; 91 } 92 93 param.size_src = src_len; 94 param.size_dst = misc_get_data_size(src, src_len, param.mode); 95 96 if (!param.size_src || !param.size_dst) 97 return -EINVAL; 98 99 return misc_ioctl(dev, IOCTL_REQ_START, ¶m); 100 } 101 102 int misc_decompress_stop(struct udevice *dev) 103 { 104 return misc_ioctl(dev, IOCTL_REQ_STOP, NULL); 105 } 106 107 bool misc_decompress_is_complete(struct udevice *dev) 108 { 109 if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL)) 110 return false; 111 else 112 return true; 113 } 114 115 int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap) 116 { 117 struct decom_param param; 118 int ret; 119 120 param.mode = cap; 121 param.size_dst = 0; /* clear */ 122 123 ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, ¶m); 124 if (!ret) 125 *size = param.size_dst; 126 127 return ret; 128 } 129 130 static int misc_decompress_finish(struct udevice *dev, u32 cap) 131 { 132 int timeout = 10000; 133 134 while (!misc_decompress_is_complete(dev)) { 135 if (timeout < 0) 136 return -EIO; 137 timeout--; 138 udelay(10); 139 } 140 141 return misc_decompress_stop(dev); 142 } 143 144 int misc_decompress_cleanup(void) 145 { 146 const struct misc_ops *ops; 147 struct udevice *dev; 148 struct uclass *uc; 149 int ret; 150 u32 cap; 151 152 ret = uclass_get(UCLASS_MISC, &uc); 153 if (ret) 154 return 0; 155 156 /* use "find_" */ 157 for (uclass_find_first_device(UCLASS_MISC, &dev); 158 dev; 159 uclass_find_next_device(&dev)) { 160 ops = device_get_ops(dev); 161 if (!ops || !ops->ioctl) 162 continue; 163 else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap)) 164 continue; 165 else if (misc_decomp_async & cap) 166 continue; 167 168 if (misc_decomp_sync & cap) { 169 ret = misc_decompress_finish(dev, cap); 170 if (ret) { 171 printf("Failed to stop decompress: %s, ret=%d\n", 172 dev->name, ret); 173 return ret; 174 } 175 } 176 } 177 178 return 0; 179 } 180 181 int misc_decompress_process(unsigned long dst, unsigned long src, 182 unsigned long src_len, u32 cap, bool sync, 183 u64 *size) 184 { 185 struct udevice *dev; 186 int ret; 187 188 dev = misc_decompress_get_device(cap); 189 if (!dev) 190 return -ENODEV; 191 192 /* Wait last finish */ 193 ret = misc_decompress_finish(dev, cap); 194 if (ret) 195 return ret; 196 197 ret = misc_decompress_start(dev, dst, src, src_len); 198 if (ret) 199 return ret; 200 201 /* 202 * Wait this round finish ? 203 * 204 * If sync, return original data length after decompress done. 205 * otherwise return from compressed file information. 206 */ 207 if (sync) { 208 ret = misc_decompress_finish(dev, cap); 209 if (ret) 210 return ret; 211 if (size) 212 ret = misc_decompress_data_size(dev, size, cap); 213 } else { 214 if (size) 215 *size = misc_get_data_size(src, src_len, cap); 216 } 217 218 return ret; 219 } 220