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