xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision 258d2dcb26879538abc00fc64a8a34c45c046465)
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, &param);
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, &param);
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