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 "los_hilog.h"
33 #include "los_init.h"
34 #include "los_mp.h"
35 #include "los_mux.h"
36 #include "los_process_pri.h"
37 #include "los_task_pri.h"
38 #include "fs/file.h"
39 #include "fs/driver.h"
40 #include "los_vm_map.h"
41 #include "los_vm_lock.h"
42 #include "user_copy.h"
43
44 #define HILOG_BUFFER LOSCFG_HILOG_BUFFER_SIZE
45 #define DRIVER_MODE 0666
46 #define HILOG_DRIVER "/dev/hilog"
47
48 struct HiLogEntry {
49 unsigned int len;
50 unsigned int hdrSize;
51 unsigned int pid : 16;
52 unsigned int taskId : 16;
53 unsigned int sec;
54 unsigned int nsec;
55 unsigned int reserved;
56 char msg[0];
57 };
58
59 ssize_t HilogRead(struct file *filep, char __user *buf, size_t count);
60 ssize_t HilogWrite(struct file *filep, const char __user *buf, size_t count);
61 int HiLogOpen(struct file *filep);
62 int HiLogClose(struct file *filep);
63
64 static ssize_t HiLogWrite(struct file *filep, const char *buffer, size_t bufLen);
65 static ssize_t HiLogRead(struct file *filep, char *buffer, size_t bufLen);
66
67 STATIC struct file_operations_vfs g_hilogFops = {
68 HiLogOpen, /* open */
69 HiLogClose, /* close */
70 HiLogRead, /* read */
71 HiLogWrite, /* write */
72 NULL, /* seek */
73 NULL, /* ioctl */
74 NULL, /* mmap */
75 #ifndef CONFIG_DISABLE_POLL
76 NULL, /* poll */
77 #endif
78 NULL, /* unlink */
79 };
80
81 struct HiLogCharDevice {
82 int flag;
83 LosMux mtx;
84 unsigned char *buffer;
85 wait_queue_head_t wq;
86 size_t writeOffset;
87 size_t headOffset;
88 size_t size;
89 size_t count;
90 } g_hiLogDev;
91
HiLogBufferHead(void)92 static inline unsigned char *HiLogBufferHead(void)
93 {
94 return g_hiLogDev.buffer + g_hiLogDev.headOffset;
95 }
96
HiLogOpen(struct file * filep)97 int HiLogOpen(struct file *filep)
98 {
99 (void)filep;
100 return 0;
101 }
102
HiLogClose(struct file * filep)103 int HiLogClose(struct file *filep)
104 {
105 (void)filep;
106 return 0;
107 }
108
HiLogBufferInc(size_t sz)109 static void HiLogBufferInc(size_t sz)
110 {
111 if (g_hiLogDev.size + sz <= HILOG_BUFFER) {
112 g_hiLogDev.size += sz;
113 g_hiLogDev.writeOffset += sz;
114 g_hiLogDev.writeOffset %= HILOG_BUFFER;
115 g_hiLogDev.count++;
116 }
117 }
118
HiLogBufferDec(size_t sz)119 static void HiLogBufferDec(size_t sz)
120 {
121 if (g_hiLogDev.size >= sz) {
122 g_hiLogDev.size -= sz;
123 g_hiLogDev.headOffset += sz;
124 g_hiLogDev.headOffset %= HILOG_BUFFER;
125 g_hiLogDev.count--;
126 }
127 }
128
HiLogBufferCopy(unsigned char * dst,unsigned dstLen,const unsigned char * src,size_t srcLen)129 static int HiLogBufferCopy(unsigned char *dst, unsigned dstLen, const unsigned char *src, size_t srcLen)
130 {
131 int retval = -1;
132 size_t minLen = (dstLen > srcLen) ? srcLen : dstLen;
133
134 if (LOS_IsUserAddressRange((VADDR_T)(UINTPTR)dst, minLen) &&
135 LOS_IsUserAddressRange((VADDR_T)(UINTPTR)src, minLen)) {
136 return retval;
137 }
138
139 if (LOS_IsUserAddressRange((VADDR_T)(UINTPTR)dst, minLen)) {
140 retval = LOS_ArchCopyToUser(dst, src, minLen);
141 } else if (LOS_IsUserAddressRange((VADDR_T)(UINTPTR)src, minLen)) {
142 retval = LOS_ArchCopyFromUser(dst, src, minLen);
143 } else {
144 retval = memcpy_s(dst, dstLen, src, srcLen);
145 }
146 return retval;
147 }
148
HiLogReadRingBuffer(unsigned char * buffer,size_t bufLen)149 static int HiLogReadRingBuffer(unsigned char *buffer, size_t bufLen)
150 {
151 int retval;
152 size_t bufLeft = HILOG_BUFFER - g_hiLogDev.headOffset;
153 if (bufLeft > bufLen) {
154 retval = HiLogBufferCopy(buffer, bufLen, HiLogBufferHead(), bufLen);
155 } else {
156 retval = HiLogBufferCopy(buffer, bufLen, HiLogBufferHead(), bufLeft);
157 if (retval < 0) {
158 return retval;
159 }
160
161 retval = HiLogBufferCopy(buffer + bufLeft, bufLen - bufLeft, g_hiLogDev.buffer, bufLen - bufLeft);
162 }
163 return retval;
164 }
165
HiLogRead(struct file * filep,char * buffer,size_t bufLen)166 static ssize_t HiLogRead(struct file *filep, char *buffer, size_t bufLen)
167 {
168 int retval;
169 struct HiLogEntry header;
170
171 (void)filep;
172 wait_event_interruptible(g_hiLogDev.wq, (g_hiLogDev.size > 0));
173
174 (VOID)LOS_MuxAcquire(&g_hiLogDev.mtx);
175 retval = HiLogReadRingBuffer((unsigned char *)&header, sizeof(header));
176 if (retval < 0) {
177 retval = -EINVAL;
178 goto out;
179 }
180
181 if (bufLen < header.len + sizeof(header)) {
182 PRINTK("buffer too small,bufLen=%d, header.len=%d,%d\n", bufLen, header.len, header.hdrSize);
183 retval = -ENOMEM;
184 goto out;
185 }
186
187 HiLogBufferDec(sizeof(header));
188
189 retval = HiLogBufferCopy((unsigned char *)buffer, bufLen, (unsigned char *)&header, sizeof(header));
190 if (retval < 0) {
191 retval = -EINVAL;
192 goto out;
193 }
194
195 retval = HiLogReadRingBuffer((unsigned char *)(buffer + sizeof(header)), header.len);
196 if (retval < 0) {
197 retval = -EINVAL;
198 goto out;
199 }
200
201 HiLogBufferDec(header.len);
202 retval = header.len + sizeof(header);
203 out:
204 if (retval == -ENOMEM) {
205 // clean ring buffer
206 g_hiLogDev.writeOffset = 0;
207 g_hiLogDev.headOffset = 0;
208 g_hiLogDev.size = 0;
209 g_hiLogDev.count = 0;
210 }
211 (VOID)LOS_MuxRelease(&g_hiLogDev.mtx);
212 return (ssize_t)retval;
213 }
214
HiLogWriteRingBuffer(unsigned char * buffer,size_t bufLen)215 static int HiLogWriteRingBuffer(unsigned char *buffer, size_t bufLen)
216 {
217 int retval;
218 size_t bufLeft = HILOG_BUFFER - g_hiLogDev.writeOffset;
219 if (bufLen > bufLeft) {
220 retval = HiLogBufferCopy(g_hiLogDev.buffer + g_hiLogDev.writeOffset, bufLeft, buffer, bufLeft);
221 if (retval) {
222 return -1;
223 }
224 retval = HiLogBufferCopy(g_hiLogDev.buffer, HILOG_BUFFER, buffer + bufLeft, bufLen - bufLeft);
225 } else {
226 retval = HiLogBufferCopy(g_hiLogDev.buffer + g_hiLogDev.writeOffset, bufLeft, buffer, bufLen);
227 }
228 if (retval < 0) {
229 return -1;
230 }
231 return 0;
232 }
233
HiLogHeadInit(struct HiLogEntry * header,size_t len)234 static void HiLogHeadInit(struct HiLogEntry *header, size_t len)
235 {
236 struct timespec now = {0};
237 (void)clock_gettime(CLOCK_REALTIME, &now);
238
239 header->len = len;
240 header->pid = LOS_GetCurrProcessID();
241 header->taskId = LOS_CurTaskIDGet();
242 header->sec = now.tv_sec;
243 header->nsec = now.tv_nsec;
244 header->hdrSize = sizeof(struct HiLogEntry);
245 }
246
HiLogCoverOldLog(size_t bufLen)247 static void HiLogCoverOldLog(size_t bufLen)
248 {
249 int retval;
250 struct HiLogEntry header;
251 size_t totalSize = bufLen + sizeof(struct HiLogEntry);
252 static int dropLogLines = 0;
253 static int isLastTimeFull = 0;
254 int isThisTimeFull = 0;
255
256 while (totalSize + g_hiLogDev.size > HILOG_BUFFER) {
257 retval = HiLogReadRingBuffer((unsigned char *)&header, sizeof(header));
258 if (retval < 0) {
259 break;
260 }
261
262 dropLogLines++;
263 isThisTimeFull = 1;
264 isLastTimeFull = 1;
265 HiLogBufferDec(sizeof(header));
266 HiLogBufferDec(header.len);
267 }
268 if (isLastTimeFull == 1 && isThisTimeFull == 0) {
269 /* so we can only print one log if hilog ring buffer is full in a short time */
270 if (dropLogLines > 0) {
271 PRINTK("hilog ringbuffer full, drop %d line(s) log\n", dropLogLines);
272 }
273 isLastTimeFull = 0;
274 dropLogLines = 0;
275 }
276 }
277
HiLogWriteInternal(const char * buffer,size_t bufLen)278 int HiLogWriteInternal(const char *buffer, size_t bufLen)
279 {
280 struct HiLogEntry header;
281 int retval;
282 LosTaskCB *runTask = (LosTaskCB *)OsCurrTaskGet();
283
284 if ((g_hiLogDev.buffer == NULL) || (OS_INT_ACTIVE) || (runTask->taskStatus & OS_TASK_FLAG_SYSTEM_TASK)) {
285 PRINTK("%s\n", buffer);
286 return -EAGAIN;
287 }
288
289 (VOID)LOS_MuxAcquire(&g_hiLogDev.mtx);
290 HiLogCoverOldLog(bufLen);
291 HiLogHeadInit(&header, bufLen);
292
293 retval = HiLogWriteRingBuffer((unsigned char *)&header, sizeof(header));
294 if (retval) {
295 retval = -ENODATA;
296 goto out;
297 }
298 HiLogBufferInc(sizeof(header));
299
300 retval = HiLogWriteRingBuffer((unsigned char *)(buffer), header.len);
301 if (retval) {
302 retval = -ENODATA;
303 goto out;
304 }
305
306 HiLogBufferInc(header.len);
307
308 retval = header.len;
309
310 out:
311 (VOID)LOS_MuxRelease(&g_hiLogDev.mtx);
312 if (retval > 0) {
313 wake_up_interruptible(&g_hiLogDev.wq);
314 }
315 if (retval < 0) {
316 PRINTK("write fail retval=%d\n", retval);
317 }
318 return retval;
319 }
320
HiLogWrite(struct file * filep,const char * buffer,size_t bufLen)321 static ssize_t HiLogWrite(struct file *filep, const char *buffer, size_t bufLen)
322 {
323 (void)filep;
324 size_t totalBufLen = bufLen + sizeof(struct HiLogEntry);
325 if ((totalBufLen < bufLen) || (totalBufLen > HILOG_BUFFER)) {
326 PRINTK("input bufLen %lld too large\n", bufLen);
327 return -ENOMEM;
328 }
329
330 return HiLogWriteInternal(buffer, bufLen);
331 }
332
HiLogDeviceInit(void)333 static void HiLogDeviceInit(void)
334 {
335 g_hiLogDev.buffer = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, HILOG_BUFFER);
336 if (g_hiLogDev.buffer == NULL) {
337 PRINTK("In %s line %d,LOS_MemAlloc fail\n", __FUNCTION__, __LINE__);
338 }
339
340 init_waitqueue_head(&g_hiLogDev.wq);
341 LOS_MuxInit(&g_hiLogDev.mtx, NULL);
342
343 g_hiLogDev.writeOffset = 0;
344 g_hiLogDev.headOffset = 0;
345 g_hiLogDev.size = 0;
346 g_hiLogDev.count = 0;
347 }
348
OsHiLogDriverInit(VOID)349 int OsHiLogDriverInit(VOID)
350 {
351 HiLogDeviceInit();
352 return register_driver(HILOG_DRIVER, &g_hilogFops, DRIVER_MODE, NULL);
353 }
354
355 LOS_MODULE_INIT(OsHiLogDriverInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
356