• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. All rights reserved.
4  */
5 
6 #include <linux/blackbox.h>
7 #include <linux/delay.h>
8 #include <linux/vmalloc.h>
9 #include <linux/syscalls.h>
10 #include <linux/dirent.h>
11 #include <linux/pstore.h>
12 #include <linux/blackbox_storage.h>
13 #include <linux/blackbox_common.h>
14 
15 char *storage_material =
16 #ifdef CONFIG_DEF_BLACKBOX_STORAGE
17     CONFIG_DEF_BLACKBOX_STORAGE;
18 #else
19     NULL;
20 #endif
21 const struct reboot_crashlog_storage *storage_lastword __ro_after_init;
22 
23 #if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_MEMORY)
24 static DEFINE_SEMAPHORE(kmsg_sem);
25 static char *lastlog;
26 unsigned int lastlog_len;
get_log_by_memory(void * in,unsigned int inlen)27 static int get_log_by_memory(void *in, unsigned int inlen)
28 {
29     return 0;
30 }
31 
storage_log_by_memory(void * out,unsigned int outlen)32 static int storage_log_by_memory(void *out, unsigned int outlen)
33 {
34     if (unlikely(!out)) {
35         return -EINVAL;
36     }
37 
38     /* Initialized from caller. */
39     lastlog = out;
40     lastlog_len = outlen;
41     return 0;
42 }
43 
44 /* Called after storage_log_by_memory successfully. */
do_kmsg_dump(struct kmsg_dumper * dumper,enum kmsg_dump_reason reason)45 static void do_kmsg_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason)
46 {
47     struct fault_log_info *pinfo;
48 
49     if (unlikely(!lastlog)) {
50         return;
51     }
52 
53     /* get kernel log from kmsg dump module */
54     if (down_trylock(&kmsg_sem) != 0) {
55         bbox_print_err("down_trylock failed!\n");
56         return;
57     }
58     pinfo = (struct fault_log_info *)lastlog;
59     (void)kmsg_dump_get_buffer(dumper, true, lastlog + sizeof(*pinfo), lastlog_len - sizeof(*pinfo),
60                                (size_t *)&pinfo->len);
61     up(&kmsg_sem);
62 }
63 #endif
64 
65 #if defined(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK) || defined(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM)
66 #define LOG_FILE_WAIT_TIME 1000 /* unit: ms */
67 #define RETRY_MAX_COUNT 10
68 #define PSTORE_MOUNT_POINT "/sys/fs/pstore/"
69 #define FILE_LIMIT (0660)
70 
is_pstore_part_ready(char * pstore_file)71 static bool is_pstore_part_ready(char *pstore_file)
72 {
73     const char *cur_name = NULL;
74     struct dentry *root_dentry;
75     struct dentry *cur_dentry;
76     struct file *filp = NULL;
77     char *full_path = NULL;
78     bool is_ready = false;
79 
80     if (unlikely(!pstore_file)) {
81         return -EINVAL;
82     }
83     memset(pstore_file, 0, sizeof(*pstore_file));
84 
85     filp = file_open(PSTORE_MOUNT_POINT, O_RDONLY, 0);
86     if (IS_ERR(filp)) {
87         bbox_print_err("open %s failed! err is [%ld]\n", PSTORE_MOUNT_POINT, PTR_ERR(filp));
88         return -EBADF;
89     }
90 
91     full_path = vmalloc(PATH_MAX_LEN);
92     if (!full_path) {
93         goto __out;
94     }
95 
96     root_dentry = filp->f_path.dentry;
97     list_for_each_entry(cur_dentry, &root_dentry->d_subdirs, d_child)
98     {
99         cur_name = cur_dentry->d_name.name;
100 
101         memset(full_path, 0, PATH_MAX_LEN);
102         snprintf(full_path, PATH_MAX_LEN - 1, "%s%s", PSTORE_MOUNT_POINT, cur_name);
103 
104         if (S_ISREG(d_inode(cur_dentry)->i_mode) && !strncmp(cur_name, "blackbox", strlen("blackbox"))) {
105             is_ready = true;
106             if (strcmp(full_path, pstore_file) > 0) {
107                 strncpy(pstore_file, full_path, strlen(full_path));
108             }
109         }
110     }
111 
112     if (is_ready && strlen(pstore_file)) {
113         bbox_print_info("get pstore file name %s successfully!\n", pstore_file);
114     }
115 
116 __out:
117     file_close(filp);
118     vfree(full_path);
119 
120     return is_ready;
121 }
122 
get_log_by_pstore(void * in,unsigned int inlen)123 static int get_log_by_pstore(void *in, unsigned int inlen)
124 {
125     char pstore_file[PATH_MAX_LEN];
126     struct file *filp = NULL;
127     char *pathname = NULL;
128     mm_segment_t old_fs;
129     void *pbuf = NULL;
130     loff_t pos = 0;
131     static int retry;
132     int ret = -1;
133 
134     memset(pstore_file, 0, PATH_MAX_LEN);
135     while (!is_pstore_part_ready((char *)&pstore_file)) {
136         msleep(LOG_FILE_WAIT_TIME);
137         retry++;
138         if (retry >= RETRY_MAX_COUNT) {
139             return -ENOENT;
140         }
141     }
142 
143     if (likely(in)) {
144         filp = file_open(pstore_file, O_RDONLY, FILE_LIMIT);
145         if (IS_ERR(filp)) {
146             bbox_print_err("open %s failed! err is [%ld]\n", pstore_file, PTR_ERR(filp));
147             return -EBADF;
148         }
149         memset(in, 0, inlen);
150         pbuf = in;
151 
152         old_fs = get_fs();
153         set_fs(KERNEL_DS);
154 
155         ret = vfs_read(filp, pbuf, inlen, &pos);
156         if (ret < 0) {
157             pathname = getfullpath(filp);
158             bbox_print_err("read %s failed! err is [%d]\n", pathname ? pathname : "", ret);
159             goto __error;
160         }
161 
162         set_fs(old_fs);
163         file_close(filp);
164         file_delete(filp);
165         return 0;
166     }
167 
168     return -EBADF;
169 __error:
170     set_fs(old_fs);
171     file_close(filp);
172     return -EIO;
173 }
174 #endif
175 
176 const struct reboot_crashlog_storage storage_lastwords[] = {
177 #if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_MEMORY)
178     {
179         .get_log = get_log_by_memory,
180         .storage_log = storage_log_by_memory,
181         .blackbox_dump = do_kmsg_dump,
182         .material = "memory",
183     },
184 #endif
185 #if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_BLK)
186     {
187         .get_log = get_log_by_pstore,
188         .blackbox_dump = pstore_blackbox_dump,
189         .material = "pstore_blk",
190     },
191 #endif
192 #if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_PSTORE_RAM)
193     {
194         .get_log = get_log_by_pstore,
195         .blackbox_dump = pstore_blackbox_dump,
196         .material = "pstore_ram",
197     },
198 #endif
199 #if IS_ENABLED(CONFIG_DEF_BLACKBOX_STORAGE_BY_RAW_PARTITION)
200     {
201         .material = "raw_partition",
202     },
203 #endif
204     {}};
205