Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2015-2018, Intel Corporation.
4 */
5
6#define pr_fmt(fmt) "kcs-bmc: " fmt
7
8#include <linux/errno.h>
9#include <linux/io.h>
10#include <linux/ipmi_bmc.h>
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/poll.h>
14#include <linux/sched.h>
15#include <linux/slab.h>
16
17#include "kcs_bmc.h"
18
19#define KCS_MSG_BUFSIZ 1000
20
21#define KCS_ZERO_DATA 0
22
23
24/* IPMI 2.0 - Table 9-1, KCS Interface Status Register Bits */
25#define KCS_STATUS_STATE(state) (state << 6)
26#define KCS_STATUS_STATE_MASK GENMASK(7, 6)
27#define KCS_STATUS_CMD_DAT BIT(3)
28#define KCS_STATUS_SMS_ATN BIT(2)
29#define KCS_STATUS_IBF BIT(1)
30#define KCS_STATUS_OBF BIT(0)
31
32/* IPMI 2.0 - Table 9-2, KCS Interface State Bits */
33enum kcs_states {
34 IDLE_STATE = 0,
35 READ_STATE = 1,
36 WRITE_STATE = 2,
37 ERROR_STATE = 3,
38};
39
40/* IPMI 2.0 - Table 9-3, KCS Interface Control Codes */
41#define KCS_CMD_GET_STATUS_ABORT 0x60
42#define KCS_CMD_WRITE_START 0x61
43#define KCS_CMD_WRITE_END 0x62
44#define KCS_CMD_READ_BYTE 0x68
45
46static inline u8 read_data(struct kcs_bmc *kcs_bmc)
47{
48 return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.idr);
49}
50
51static inline void write_data(struct kcs_bmc *kcs_bmc, u8 data)
52{
53 kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.odr, data);
54}
55
56static inline u8 read_status(struct kcs_bmc *kcs_bmc)
57{
58 return kcs_bmc->io_inputb(kcs_bmc, kcs_bmc->ioreg.str);
59}
60
61static inline void write_status(struct kcs_bmc *kcs_bmc, u8 data)
62{
63 kcs_bmc->io_outputb(kcs_bmc, kcs_bmc->ioreg.str, data);
64}
65
66static void update_status_bits(struct kcs_bmc *kcs_bmc, u8 mask, u8 val)
67{
68 u8 tmp = read_status(kcs_bmc);
69
70 tmp &= ~mask;
71 tmp |= val & mask;
72
73 write_status(kcs_bmc, tmp);
74}
75
76static inline void set_state(struct kcs_bmc *kcs_bmc, u8 state)
77{
78 update_status_bits(kcs_bmc, KCS_STATUS_STATE_MASK,
79 KCS_STATUS_STATE(state));
80}
81
82static void kcs_force_abort(struct kcs_bmc *kcs_bmc)
83{
84 set_state(kcs_bmc, ERROR_STATE);
85 read_data(kcs_bmc);
86 write_data(kcs_bmc, KCS_ZERO_DATA);
87
88 kcs_bmc->phase = KCS_PHASE_ERROR;
89 kcs_bmc->data_in_avail = false;
90 kcs_bmc->data_in_idx = 0;
91}
92
93static void kcs_bmc_handle_data(struct kcs_bmc *kcs_bmc)
94{
95 u8 data;
96
97 switch (kcs_bmc->phase) {
98 case KCS_PHASE_WRITE_START:
99 kcs_bmc->phase = KCS_PHASE_WRITE_DATA;
100 /* fall through */
101
102 case KCS_PHASE_WRITE_DATA:
103 if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
104 set_state(kcs_bmc, WRITE_STATE);
105 write_data(kcs_bmc, KCS_ZERO_DATA);
106 kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
107 read_data(kcs_bmc);
108 } else {
109 kcs_force_abort(kcs_bmc);
110 kcs_bmc->error = KCS_LENGTH_ERROR;
111 }
112 break;
113
114 case KCS_PHASE_WRITE_END_CMD:
115 if (kcs_bmc->data_in_idx < KCS_MSG_BUFSIZ) {
116 set_state(kcs_bmc, READ_STATE);
117 kcs_bmc->data_in[kcs_bmc->data_in_idx++] =
118 read_data(kcs_bmc);
119 kcs_bmc->phase = KCS_PHASE_WRITE_DONE;
120 kcs_bmc->data_in_avail = true;
121 wake_up_interruptible(&kcs_bmc->queue);
122 } else {
123 kcs_force_abort(kcs_bmc);
124 kcs_bmc->error = KCS_LENGTH_ERROR;
125 }
126 break;
127
128 case KCS_PHASE_READ:
129 if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len)
130 set_state(kcs_bmc, IDLE_STATE);
131
132 data = read_data(kcs_bmc);
133 if (data != KCS_CMD_READ_BYTE) {
134 set_state(kcs_bmc, ERROR_STATE);
135 write_data(kcs_bmc, KCS_ZERO_DATA);
136 break;
137 }
138
139 if (kcs_bmc->data_out_idx == kcs_bmc->data_out_len) {
140 write_data(kcs_bmc, KCS_ZERO_DATA);
141 kcs_bmc->phase = KCS_PHASE_IDLE;
142 break;
143 }
144
145 write_data(kcs_bmc,
146 kcs_bmc->data_out[kcs_bmc->data_out_idx++]);
147 break;
148
149 case KCS_PHASE_ABORT_ERROR1:
150 set_state(kcs_bmc, READ_STATE);
151 read_data(kcs_bmc);
152 write_data(kcs_bmc, kcs_bmc->error);
153 kcs_bmc->phase = KCS_PHASE_ABORT_ERROR2;
154 break;
155
156 case KCS_PHASE_ABORT_ERROR2:
157 set_state(kcs_bmc, IDLE_STATE);
158 read_data(kcs_bmc);
159 write_data(kcs_bmc, KCS_ZERO_DATA);
160 kcs_bmc->phase = KCS_PHASE_IDLE;
161 break;
162
163 default:
164 kcs_force_abort(kcs_bmc);
165 break;
166 }
167}
168
169static void kcs_bmc_handle_cmd(struct kcs_bmc *kcs_bmc)
170{
171 u8 cmd;
172
173 set_state(kcs_bmc, WRITE_STATE);
174 write_data(kcs_bmc, KCS_ZERO_DATA);
175
176 cmd = read_data(kcs_bmc);
177 switch (cmd) {
178 case KCS_CMD_WRITE_START:
179 kcs_bmc->phase = KCS_PHASE_WRITE_START;
180 kcs_bmc->error = KCS_NO_ERROR;
181 kcs_bmc->data_in_avail = false;
182 kcs_bmc->data_in_idx = 0;
183 break;
184
185 case KCS_CMD_WRITE_END:
186 if (kcs_bmc->phase != KCS_PHASE_WRITE_DATA) {
187 kcs_force_abort(kcs_bmc);
188 break;
189 }
190
191 kcs_bmc->phase = KCS_PHASE_WRITE_END_CMD;
192 break;
193
194 case KCS_CMD_GET_STATUS_ABORT:
195 if (kcs_bmc->error == KCS_NO_ERROR)
196 kcs_bmc->error = KCS_ABORTED_BY_COMMAND;
197
198 kcs_bmc->phase = KCS_PHASE_ABORT_ERROR1;
199 kcs_bmc->data_in_avail = false;
200 kcs_bmc->data_in_idx = 0;
201 break;
202
203 default:
204 kcs_force_abort(kcs_bmc);
205 kcs_bmc->error = KCS_ILLEGAL_CONTROL_CODE;
206 break;
207 }
208}
209
210int kcs_bmc_handle_event(struct kcs_bmc *kcs_bmc)
211{
212 unsigned long flags;
213 int ret = -ENODATA;
214 u8 status;
215
216 spin_lock_irqsave(&kcs_bmc->lock, flags);
217
218 status = read_status(kcs_bmc);
219 if (status & KCS_STATUS_IBF) {
220 if (!kcs_bmc->running)
221 kcs_force_abort(kcs_bmc);
222 else if (status & KCS_STATUS_CMD_DAT)
223 kcs_bmc_handle_cmd(kcs_bmc);
224 else
225 kcs_bmc_handle_data(kcs_bmc);
226
227 ret = 0;
228 }
229
230 spin_unlock_irqrestore(&kcs_bmc->lock, flags);
231
232 return ret;
233}
234EXPORT_SYMBOL(kcs_bmc_handle_event);
235
236static inline struct kcs_bmc *to_kcs_bmc(struct file *filp)
237{
238 return container_of(filp->private_data, struct kcs_bmc, miscdev);
239}
240
241static int kcs_bmc_open(struct inode *inode, struct file *filp)
242{
243 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
244 int ret = 0;
245
246 spin_lock_irq(&kcs_bmc->lock);
247 if (!kcs_bmc->running)
248 kcs_bmc->running = 1;
249 else
250 ret = -EBUSY;
251 spin_unlock_irq(&kcs_bmc->lock);
252
253 return ret;
254}
255
256static __poll_t kcs_bmc_poll(struct file *filp, poll_table *wait)
257{
258 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
259 __poll_t mask = 0;
260
261 poll_wait(filp, &kcs_bmc->queue, wait);
262
263 spin_lock_irq(&kcs_bmc->lock);
264 if (kcs_bmc->data_in_avail)
265 mask |= EPOLLIN;
266 spin_unlock_irq(&kcs_bmc->lock);
267
268 return mask;
269}
270
271static ssize_t kcs_bmc_read(struct file *filp, char __user *buf,
272 size_t count, loff_t *ppos)
273{
274 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
275 bool data_avail;
276 size_t data_len;
277 ssize_t ret;
278
279 if (!(filp->f_flags & O_NONBLOCK))
280 wait_event_interruptible(kcs_bmc->queue,
281 kcs_bmc->data_in_avail);
282
283 mutex_lock(&kcs_bmc->mutex);
284
285 spin_lock_irq(&kcs_bmc->lock);
286 data_avail = kcs_bmc->data_in_avail;
287 if (data_avail) {
288 data_len = kcs_bmc->data_in_idx;
289 memcpy(kcs_bmc->kbuffer, kcs_bmc->data_in, data_len);
290 }
291 spin_unlock_irq(&kcs_bmc->lock);
292
293 if (!data_avail) {
294 ret = -EAGAIN;
295 goto out_unlock;
296 }
297
298 if (count < data_len) {
299 pr_err("channel=%u with too large data : %zu\n",
300 kcs_bmc->channel, data_len);
301
302 spin_lock_irq(&kcs_bmc->lock);
303 kcs_force_abort(kcs_bmc);
304 spin_unlock_irq(&kcs_bmc->lock);
305
306 ret = -EOVERFLOW;
307 goto out_unlock;
308 }
309
310 if (copy_to_user(buf, kcs_bmc->kbuffer, data_len)) {
311 ret = -EFAULT;
312 goto out_unlock;
313 }
314
315 ret = data_len;
316
317 spin_lock_irq(&kcs_bmc->lock);
318 if (kcs_bmc->phase == KCS_PHASE_WRITE_DONE) {
319 kcs_bmc->phase = KCS_PHASE_WAIT_READ;
320 kcs_bmc->data_in_avail = false;
321 kcs_bmc->data_in_idx = 0;
322 } else {
323 ret = -EAGAIN;
324 }
325 spin_unlock_irq(&kcs_bmc->lock);
326
327out_unlock:
328 mutex_unlock(&kcs_bmc->mutex);
329
330 return ret;
331}
332
333static ssize_t kcs_bmc_write(struct file *filp, const char __user *buf,
334 size_t count, loff_t *ppos)
335{
336 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
337 ssize_t ret;
338
339 /* a minimum response size '3' : netfn + cmd + ccode */
340 if (count < 3 || count > KCS_MSG_BUFSIZ)
341 return -EINVAL;
342
343 mutex_lock(&kcs_bmc->mutex);
344
345 if (copy_from_user(kcs_bmc->kbuffer, buf, count)) {
346 ret = -EFAULT;
347 goto out_unlock;
348 }
349
350 spin_lock_irq(&kcs_bmc->lock);
351 if (kcs_bmc->phase == KCS_PHASE_WAIT_READ) {
352 kcs_bmc->phase = KCS_PHASE_READ;
353 kcs_bmc->data_out_idx = 1;
354 kcs_bmc->data_out_len = count;
355 memcpy(kcs_bmc->data_out, kcs_bmc->kbuffer, count);
356 write_data(kcs_bmc, kcs_bmc->data_out[0]);
357 ret = count;
358 } else {
359 ret = -EINVAL;
360 }
361 spin_unlock_irq(&kcs_bmc->lock);
362
363out_unlock:
364 mutex_unlock(&kcs_bmc->mutex);
365
366 return ret;
367}
368
369static long kcs_bmc_ioctl(struct file *filp, unsigned int cmd,
370 unsigned long arg)
371{
372 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
373 long ret = 0;
374
375 spin_lock_irq(&kcs_bmc->lock);
376
377 switch (cmd) {
378 case IPMI_BMC_IOCTL_SET_SMS_ATN:
379 update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
380 KCS_STATUS_SMS_ATN);
381 break;
382
383 case IPMI_BMC_IOCTL_CLEAR_SMS_ATN:
384 update_status_bits(kcs_bmc, KCS_STATUS_SMS_ATN,
385 0);
386 break;
387
388 case IPMI_BMC_IOCTL_FORCE_ABORT:
389 kcs_force_abort(kcs_bmc);
390 break;
391
392 default:
393 ret = -EINVAL;
394 break;
395 }
396
397 spin_unlock_irq(&kcs_bmc->lock);
398
399 return ret;
400}
401
402static int kcs_bmc_release(struct inode *inode, struct file *filp)
403{
404 struct kcs_bmc *kcs_bmc = to_kcs_bmc(filp);
405
406 spin_lock_irq(&kcs_bmc->lock);
407 kcs_bmc->running = 0;
408 kcs_force_abort(kcs_bmc);
409 spin_unlock_irq(&kcs_bmc->lock);
410
411 return 0;
412}
413
414static const struct file_operations kcs_bmc_fops = {
415 .owner = THIS_MODULE,
416 .open = kcs_bmc_open,
417 .read = kcs_bmc_read,
418 .write = kcs_bmc_write,
419 .release = kcs_bmc_release,
420 .poll = kcs_bmc_poll,
421 .unlocked_ioctl = kcs_bmc_ioctl,
422};
423
424struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
425{
426 struct kcs_bmc *kcs_bmc;
427
428 kcs_bmc = devm_kzalloc(dev, sizeof(*kcs_bmc) + sizeof_priv, GFP_KERNEL);
429 if (!kcs_bmc)
430 return NULL;
431
432 dev_set_name(dev, "ipmi-kcs%u", channel);
433
434 spin_lock_init(&kcs_bmc->lock);
435 kcs_bmc->channel = channel;
436
437 mutex_init(&kcs_bmc->mutex);
438 init_waitqueue_head(&kcs_bmc->queue);
439
440 kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
441 kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
442 kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
443 if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
444 return NULL;
445
446 kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
447 kcs_bmc->miscdev.name = dev_name(dev);
448 kcs_bmc->miscdev.fops = &kcs_bmc_fops;
449
450 return kcs_bmc;
451}
452EXPORT_SYMBOL(kcs_bmc_alloc);
453
454MODULE_LICENSE("GPL v2");
455MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
456MODULE_DESCRIPTION("KCS BMC to handle the IPMI request from system software");