1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #if defined(CONFIG_KDP_INTERACTIVE_DEBUGGING)
30
31 #include <mach/kern_return.h>
32 #include <os/base.h>
33 #include <kern/assert.h>
34 #include <kern/kalloc.h>
35 #include <kern/misc_protos.h>
36 #include <kdp/kdp_core.h>
37 #include <kdp/kdp_out_stage.h>
38 #include <kdp/processor_core.h>
39 #include <kdp/output_stages/output_stages.h>
40 #include <libkern/compression/compression.h>
41
42 // One page seems to be a sweet-spot for both buffers; we tend to dump a page or less at a time so
43 // smaller sizes start incurring overhead for the loop, and larger sizes waste more memory without
44 // a noticable increase in throughput.
45 //
46 // Having a buffer full of zeroes wastes a bit of memory, but we need some way to pass zeroes to
47 // Compression when we are asked to pad the stream.
48 #define DST_BUF_SIZE PAGE_SIZE
49 #define ZERO_BUF_SIZE PAGE_SIZE
50
51 struct lz4_stage_data {
52 compression_stream_t stream;
53 compression_algorithm_t algorithm;
54 uint8_t *dst_buf;
55 const uint8_t *zero_buf;
56 bool reset_failed;
57 };
58
59 static void
lz4_stage_reset(struct kdp_output_stage * stage)60 lz4_stage_reset(struct kdp_output_stage *stage)
61 {
62 struct lz4_stage_data *data;
63 compression_status_t status;
64
65 assert(compression_ki_ptr);
66
67 assert(stage);
68 assert(stage->kos_initialized);
69
70 data = stage->kos_data;
71 assert(data);
72
73 status = compression_ki_ptr->compression_stream_reinit(&data->stream,
74 COMPRESSION_STREAM_ENCODE, data->algorithm);
75 if (COMPRESSION_STATUS_OK != status) {
76 data->reset_failed = true;
77 }
78
79 data->stream.dst_ptr = data->dst_buf;
80 data->stream.dst_size = DST_BUF_SIZE;
81
82 stage->kos_bypass = false;
83 stage->kos_bytes_written = 0;
84 }
85
86 static kern_return_t
lz4_stage_stream(struct lz4_stage_data * data,struct kdp_output_stage * next_stage,char * corename,const uint8_t * src_buf,size_t src_buf_size,uint64_t * written)87 lz4_stage_stream(struct lz4_stage_data *data, struct kdp_output_stage *next_stage, char *corename,
88 const uint8_t *src_buf, size_t src_buf_size, uint64_t *written)
89 {
90 bool finalize;
91 compression_status_t status;
92 kern_return_t ret;
93 size_t produced;
94
95 assert(compression_ki_ptr);
96
97 assert(data);
98 assert(next_stage);
99 assert(written);
100
101 finalize = !src_buf && !src_buf_size;
102 assert((src_buf && src_buf_size) || finalize);
103
104 data->stream.src_ptr = src_buf;
105 data->stream.src_size = src_buf_size;
106
107 // Every time we process more bytes, the stream source and destination pointers
108 // and sizes are adjusted.
109 do {
110 status = compression_ki_ptr->compression_stream_process(&data->stream,
111 finalize ? COMPRESSION_STREAM_FINALIZE : 0);
112 if (COMPRESSION_STATUS_ERROR == status) {
113 return KERN_FAILURE;
114 }
115
116 // The difference between the original destination buffer size and the size of the
117 // remaining space tells us how many bytes were produced.
118 produced = DST_BUF_SIZE - data->stream.dst_size;
119 // Pass along those bytes to the next stage so that we empty the destination buffer.
120 ret = next_stage->kos_funcs.kosf_outproc(next_stage, KDP_DATA, corename,
121 produced, data->dst_buf);
122 if (KERN_SUCCESS != ret) {
123 return ret;
124 }
125 *written += produced;
126 data->stream.dst_ptr = data->dst_buf;
127 data->stream.dst_size = DST_BUF_SIZE;
128 // Continue processing while the source buffer is non-empty, or we are finalizing
129 // and there are still bytes being produced in the destination buffer.
130 } while (data->stream.src_size || (finalize && COMPRESSION_STATUS_END != status));
131
132 if (finalize) {
133 return next_stage->kos_funcs.kosf_outproc(next_stage, KDP_DATA, corename, 0, NULL);
134 }
135
136 return KERN_SUCCESS;
137 }
138
139 static kern_return_t
lz4_stage_outproc(struct kdp_output_stage * stage,unsigned int request,char * corename,uint64_t length,void * panic_data)140 lz4_stage_outproc(struct kdp_output_stage *stage, unsigned int request,
141 char *corename, uint64_t length, void *panic_data)
142 {
143 struct lz4_stage_data *data;
144 struct kdp_output_stage *next_stage;
145 kern_return_t ret;
146 size_t pad_length;
147 size_t zero_size;
148
149 assert(stage);
150 assert(stage->kos_initialized);
151
152 data = stage->kos_data;
153 assert(data);
154
155 next_stage = STAILQ_NEXT(stage, kos_next);
156 assert(next_stage);
157
158 if (data->reset_failed) {
159 return KERN_FAILURE;
160 }
161
162 if (KDP_SEEK == request) {
163 stage->kos_bypass = true;
164 }
165
166 if (stage->kos_bypass || KDP_DATA != request) {
167 return next_stage->kos_funcs.kosf_outproc(next_stage, request, corename, length,
168 panic_data);
169 }
170
171 if (panic_data) {
172 // Write panic data to the stream.
173 return lz4_stage_stream(data, next_stage, corename, panic_data, (size_t)length,
174 &stage->kos_bytes_written);
175 } else {
176 if (length) {
177 // Pad the stream with zeroes.
178 pad_length = (size_t)length;
179 do {
180 zero_size = MIN(pad_length, ZERO_BUF_SIZE);
181 ret = lz4_stage_stream(data, next_stage, corename, data->zero_buf,
182 zero_size, &stage->kos_bytes_written);
183 if (KERN_SUCCESS != ret) {
184 return ret;
185 }
186 pad_length -= zero_size;
187 } while (pad_length);
188 return KERN_SUCCESS;
189 } else {
190 // Finalize the stream.
191 return lz4_stage_stream(data, next_stage, corename, NULL, 0, &stage->kos_bytes_written);
192 }
193 }
194 }
195
196 static void
lz4_stage_free(struct kdp_output_stage * stage)197 lz4_stage_free(struct kdp_output_stage *stage)
198 {
199 struct lz4_stage_data *data;
200
201 assert(compression_ki_ptr);
202
203 assert(stage);
204 assert(stage->kos_initialized);
205
206 data = stage->kos_data;
207 assert(data);
208
209 kfree_data(data->dst_buf, DST_BUF_SIZE);
210
211 compression_ki_ptr->compression_stream_destroy(&data->stream);
212
213 kfree_type(typeof(*data), data);
214
215 stage->kos_initialized = false;
216 }
217
218 kern_return_t
lz4_stage_initialize(struct kdp_output_stage * stage)219 lz4_stage_initialize(struct kdp_output_stage *stage)
220 {
221 struct lz4_stage_data *data;
222 compression_status_t status;
223
224 assert(compression_ki_ptr);
225
226 assert(stage);
227 assert(!stage->kos_initialized);
228 assert(!stage->kos_data);
229
230 data = kalloc_type(typeof(*data), Z_WAITOK);
231 assert(data);
232
233 data->algorithm = COMPRESSION_LZ4;
234
235 data->dst_buf = kalloc_data(DST_BUF_SIZE, Z_WAITOK);
236 assert(data->dst_buf);
237
238 data->zero_buf = kalloc_data(ZERO_BUF_SIZE, Z_WAITOK | Z_ZERO);
239 assert(data->zero_buf);
240
241 data->reset_failed = false;
242
243 status = compression_ki_ptr->compression_stream_init(&data->stream, COMPRESSION_STREAM_ENCODE,
244 data->algorithm);
245 if (COMPRESSION_STATUS_ERROR == status) {
246 return KERN_FAILURE;
247 }
248 data->stream.dst_ptr = data->dst_buf;
249 data->stream.dst_size = DST_BUF_SIZE;
250
251 stage->kos_data = data;
252 stage->kos_data_size = sizeof(*data);
253
254 stage->kos_funcs.kosf_reset = lz4_stage_reset;
255 stage->kos_funcs.kosf_outproc = lz4_stage_outproc;
256 stage->kos_funcs.kosf_free = lz4_stage_free;
257
258 stage->kos_initialized = true;
259
260 return KERN_SUCCESS;
261 }
262
263 static void
lz4_stage_registration_callback(void)264 lz4_stage_registration_callback(void)
265 {
266 kern_return_t ret = kdp_core_handle_lz4_available();
267 if (KERN_SUCCESS != ret) {
268 printf("(%s) Failed to handle availability of LZ4. Error 0x%x\n", __func__, ret);
269 }
270 }
271
272 void
lz4_stage_monitor_availability(void)273 lz4_stage_monitor_availability(void)
274 {
275 compression_interface_set_registration_callback(lz4_stage_registration_callback);
276 }
277
278 #endif /* defined(CONFIG_KDP_INTERACTIVE_DEBUGGING) */
279