1 /*
2  * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3  * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this list of
9  *    conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12  *    of conditions and the following disclaimer in the documentation and/or other materials
13  *    provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16  *    to endorse or promote products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "hievent_driver.h"
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <fs/driver.h>
38 #include <linux/list.h>
39 #include <linux/wait.h>
40 #include <semaphore.h>
41 #include <stdio.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 
45 #include "los_init.h"
46 #include "los_memory.h"
47 #include "los_mp.h"
48 #include "los_mux.h"
49 #include "los_process_pri.h"
50 #include "los_task_pri.h"
51 #include "los_vm_lock.h"
52 #include "los_vm_map.h"
53 #include "poll.h"
54 #include "user_copy.h"
55 
56 #define HIEVENT_LOG_BUFFER 1024
57 #define DRIVER_MODE 0666
58 
59 struct HieventEntry {
60     uint16_t len;
61     uint16_t hdrSize;
62     int32_t pid;
63     int32_t tid;
64     int32_t sec;
65     int32_t nsec;
66     char msg[0];
67 };
68 
69 struct HieventCharDevice {
70     int flag;
71     LosMux mtx;
72     unsigned char *buffer;
73     wait_queue_head_t wq;
74     size_t writeOffset;
75     size_t headOffset;
76     size_t size;
77     size_t count;
78 } g_hieventDev;
79 
HieventBufferHead(void)80 static inline unsigned char *HieventBufferHead(void)
81 {
82     if (g_hieventDev.headOffset > HIEVENT_LOG_BUFFER) {
83         g_hieventDev.headOffset = g_hieventDev.headOffset % HIEVENT_LOG_BUFFER;
84     }
85     return g_hieventDev.buffer + g_hieventDev.headOffset;
86 }
87 
HieventOpen(struct file * filep)88 int HieventOpen(struct file *filep)
89 {
90     (void)filep;
91     return 0;
92 }
93 
HieventClose(struct file * filep)94 int HieventClose(struct file *filep)
95 {
96     (void)filep;
97     return 0;
98 }
99 
HieventBufferInc(size_t sz)100 static void HieventBufferInc(size_t sz)
101 {
102     if (g_hieventDev.size + sz <= HIEVENT_LOG_BUFFER) {
103         g_hieventDev.size += sz;
104         g_hieventDev.writeOffset += sz;
105         g_hieventDev.writeOffset %= HIEVENT_LOG_BUFFER;
106         g_hieventDev.count++;
107     }
108 }
109 
HieventBufferDec(size_t sz)110 static void HieventBufferDec(size_t sz)
111 {
112     if (g_hieventDev.size >= sz) {
113         g_hieventDev.size -= sz;
114         g_hieventDev.headOffset += sz;
115         g_hieventDev.headOffset %= HIEVENT_LOG_BUFFER;
116         g_hieventDev.count--;
117     }
118 }
119 
HieventBufferCopy(unsigned char * dst,unsigned dstLen,unsigned char * src,size_t srcLen)120 static int HieventBufferCopy(unsigned char *dst, unsigned dstLen,
121                              unsigned char *src, size_t srcLen)
122 {
123     int retval = -1;
124 
125     size_t minLen = dstLen > srcLen ? srcLen : dstLen;
126 
127     if (LOS_IsUserAddressRange((vaddr_t)(uintptr_t)dst, minLen) &&
128         LOS_IsUserAddressRange((vaddr_t)(uintptr_t)src, minLen)) {
129         return retval;
130     }
131 
132     if (LOS_IsUserAddressRange((vaddr_t)(uintptr_t)dst, minLen)) {
133         retval = LOS_ArchCopyToUser(dst, src, minLen);
134     } else if (LOS_IsUserAddressRange((vaddr_t)(uintptr_t)src, minLen)) {
135         retval = LOS_ArchCopyFromUser(dst, src, minLen);
136     } else {
137         retval = memcpy_s(dst, dstLen, src, srcLen);
138     }
139     return retval;
140 }
141 
HieventReadRingBuffer(unsigned char * buffer,size_t bufLen)142 static int HieventReadRingBuffer(unsigned char *buffer, size_t bufLen)
143 {
144     int retval;
145     size_t bufLeft = HIEVENT_LOG_BUFFER - g_hieventDev.headOffset;
146     if (bufLeft > bufLen) {
147         retval = HieventBufferCopy(buffer, bufLen, HieventBufferHead(), bufLen);
148     } else {
149         retval = HieventBufferCopy(buffer, bufLen, HieventBufferHead(), bufLeft);
150         if (retval < 0) {
151             return retval;
152         }
153 
154         retval = HieventBufferCopy(buffer + bufLeft, bufLen - bufLeft,
155                                    g_hieventDev.buffer, bufLen - bufLeft);
156     }
157     return retval;
158 }
159 
HieventRead(struct file * filep,char * buffer,size_t bufLen)160 static ssize_t HieventRead(struct file *filep, char *buffer, size_t bufLen)
161 {
162     ssize_t retval;
163     struct HieventEntry header;
164 
165     (void)filep;
166 
167     wait_event_interruptible(g_hieventDev.wq, (g_hieventDev.size > 0));
168 
169     (VOID)LOS_MuxAcquire(&g_hieventDev.mtx);
170 
171     retval = HieventReadRingBuffer((unsigned char *)&header, sizeof(header));
172     if (retval < 0) {
173         retval = -EINVAL;
174         goto out;
175     }
176 
177     if (bufLen < header.len + sizeof(header)) {
178         PRINT_ERR("buffer too small\n");
179         retval = -ENOMEM;
180         goto out;
181     }
182 
183     HieventBufferDec(sizeof(header));
184 
185     retval = HieventBufferCopy((unsigned char *)buffer, bufLen,
186                                (unsigned char *)&header, sizeof(header));
187     if (retval < 0) {
188         retval = -EINVAL;
189         goto out;
190     }
191 
192     retval = HieventReadRingBuffer((unsigned char *)(buffer + sizeof(header)),
193                                    header.len);
194     if (retval < 0) {
195         retval = -EINVAL;
196         goto out;
197     }
198 
199     HieventBufferDec(header.len);
200 
201     retval = header.len + sizeof(header);
202 out:
203     if (retval == -ENOMEM) {
204         // clean ring buffer
205         g_hieventDev.writeOffset = 0;
206         g_hieventDev.headOffset = 0;
207         g_hieventDev.size = 0;
208         g_hieventDev.count = 0;
209     }
210     (VOID)LOS_MuxRelease(&g_hieventDev.mtx);
211     return retval;
212 }
213 
HieventWriteRingBuffer(unsigned char * buffer,size_t bufLen)214 static int HieventWriteRingBuffer(unsigned char *buffer, size_t bufLen)
215 {
216     int retval;
217     size_t bufLeft = HIEVENT_LOG_BUFFER - g_hieventDev.writeOffset;
218     if (bufLen > bufLeft) {
219         retval = HieventBufferCopy(g_hieventDev.buffer + g_hieventDev.writeOffset,
220                                    bufLeft, buffer, bufLeft);
221         if (retval) {
222             return -1;
223         }
224         retval = HieventBufferCopy(g_hieventDev.buffer, HIEVENT_LOG_BUFFER,
225                                    buffer + bufLeft, bufLen - bufLeft);
226     } else {
227         retval = HieventBufferCopy(g_hieventDev.buffer + g_hieventDev.writeOffset,
228                                    bufLeft, buffer, bufLen);
229     }
230     if (retval < 0) {
231         return -1;
232     }
233     return 0;
234 }
235 
HieventHeadInit(struct HieventEntry * header,size_t len)236 static void HieventHeadInit(struct HieventEntry *header, size_t len)
237 {
238     struct timespec now;
239 
240     clock_gettime(CLOCK_REALTIME, &now);
241 
242     header->len = len;
243     header->pid = LOS_GetCurrProcessID();
244     header->tid = 0;
245     header->sec = now.tv_sec;
246     header->nsec = now.tv_nsec;
247     header->hdrSize = sizeof(struct HieventEntry);
248 }
249 
HieventCoverOldLog(size_t bufLen)250 static void HieventCoverOldLog(size_t bufLen)
251 {
252     int retval;
253     struct HieventEntry header;
254     size_t totalSize = bufLen + sizeof(struct HieventEntry);
255 
256     while (totalSize + g_hieventDev.size > HIEVENT_LOG_BUFFER) {
257         retval = HieventReadRingBuffer((unsigned char *)&header, sizeof(header));
258         if (retval < 0) {
259             break;
260         }
261 
262         /* let count decrease twice */
263         HieventBufferDec(sizeof(header));
264         HieventBufferDec(header.len);
265     }
266 }
267 
HieventWriteInternal(const char * buffer,size_t bufLen)268 int HieventWriteInternal(const char *buffer, size_t bufLen)
269 {
270     struct HieventEntry header;
271     int retval;
272 
273     if (bufLen < sizeof(int) ||
274         bufLen > HIEVENT_LOG_BUFFER - sizeof(struct HieventEntry)) {
275         return -EINVAL;
276     }
277 
278     (VOID)LOS_MuxAcquire(&g_hieventDev.mtx);
279 
280     /* need userspace use writev */
281     if (LOS_IsUserAddressRange((vaddr_t)(uintptr_t)buffer, bufLen)) {
282         retval = -EINVAL;
283         goto out;
284     }
285 
286     int checkCode = *((int *)buffer);
287 
288     if (checkCode != CHECK_CODE) {
289         retval = -EINVAL;
290         goto out;
291     }
292 
293     HieventCoverOldLog(bufLen);
294 
295     HieventHeadInit(&header, bufLen - sizeof(int));
296 
297     retval = HieventWriteRingBuffer((unsigned char *)&header, sizeof(header));
298     if (retval) {
299         retval = -EINVAL;
300         goto out;
301     }
302     HieventBufferInc(sizeof(header));
303 
304     retval = HieventWriteRingBuffer((unsigned char *)(buffer + sizeof(int)),
305                                     header.len);
306     if (retval) {
307         retval = -EINVAL;
308         goto out;
309     }
310 
311     HieventBufferInc(header.len);
312 
313     retval = header.len;
314 
315 out:
316     (VOID)LOS_MuxRelease(&g_hieventDev.mtx);
317     if (retval > 0) {
318         wake_up_interruptible(&g_hieventDev.wq);
319     }
320     return retval;
321 }
322 
HieventWrite(struct file * filep,const char * buffer,size_t bufLen)323 static ssize_t HieventWrite(struct file *filep,
324                             const char *buffer, size_t bufLen)
325 {
326     (void)filep;
327     return HieventWriteInternal(buffer, bufLen);
328 }
329 
HieventPoll(struct file * filep,poll_table * fds)330 static int HieventPoll(struct file *filep, poll_table *fds)
331 {
332     (void)filep;
333 
334     wait_event_interruptible(g_hieventDev.wq, (g_hieventDev.size > 0));
335 
336     return (POLLOUT | POLLWRNORM);
337 }
338 
HieventIoctl(struct file * filep,int cmd,unsigned long arg)339 static int HieventIoctl(struct file *filep, int cmd, unsigned long arg)
340 {
341     // not support ioctl in liteos now
342     (void)filep;
343     (void)cmd;
344     (void)arg;
345     return 0;
346 }
347 
348 static struct file_operations_vfs g_hieventFops = {
349     .open  = HieventOpen,   /* open */
350     .close = HieventClose,  /* close */
351     .read  = HieventRead,   /* read */
352     .write = HieventWrite,  /* write */
353     .seek  = NULL,          /* seek */
354     .ioctl = HieventIoctl,  /* ioctl */
355     .mmap  = NULL,          /* mmap */
356     .poll  = HieventPoll,   /* poll */
357 };
358 
HieventDeviceInit(void)359 static int HieventDeviceInit(void)
360 {
361     g_hieventDev.buffer = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR,
362                                        HIEVENT_LOG_BUFFER);
363     if (g_hieventDev.buffer == NULL) {
364         return -ENOMEM;
365     }
366 
367     init_waitqueue_head(&g_hieventDev.wq);
368     (void)LOS_MuxInit(&g_hieventDev.mtx, NULL);
369 
370     g_hieventDev.writeOffset = 0;
371     g_hieventDev.headOffset = 0;
372     g_hieventDev.size = 0;
373     g_hieventDev.count = 0;
374     return 0;
375 }
376 
HieventInit(void)377 int HieventInit(void)
378 {
379     int ret = HieventDeviceInit();
380     if (ret != 0) {
381         return ret;
382     }
383 
384     register_driver("/dev/hwlog_exception", &g_hieventFops,
385                     DRIVER_MODE, &g_hieventDev);
386     return 0;
387 }
388 
389 LOS_MODULE_INIT(HieventInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
390