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