xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision a649240119db045e59f769176ec20a5ca009fd7c)
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 	int ret;
189 
190 	dev = misc_decompress_get_device(cap);
191 	if (!dev)
192 		return -ENODEV;
193 
194 	/* Wait last finish */
195 	ret = misc_decompress_finish(dev, cap);
196 	if (ret)
197 		return ret;
198 
199 	ret = misc_decompress_start(dev, dst, src, src_len, flags);
200 	if (ret)
201 		return ret;
202 
203 	/*
204 	 * Wait this round finish ?
205 	 *
206 	 * If sync, return original data length after decompress done.
207 	 * otherwise return from compressed file information.
208 	 */
209 	if (sync) {
210 		ret = misc_decompress_finish(dev, cap);
211 		if (ret)
212 			return ret;
213 		if (size)
214 			ret = misc_decompress_data_size(dev, size, cap);
215 	} else {
216 		if (size)
217 			*size = misc_get_data_size(src, src_len, cap);
218 	}
219 
220 	return ret;
221 }
222