1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ebcdic keycode functions for s390 console drivers |
4 | * |
5 | * S390 version |
6 | * Copyright IBM Corp. 2003 |
7 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/sched/signal.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/sysrq.h> |
14 | |
15 | #include <linux/consolemap.h> |
16 | #include <linux/kbd_kern.h> |
17 | #include <linux/kbd_diacr.h> |
18 | #include <linux/uaccess.h> |
19 | |
20 | #include "keyboard.h" |
21 | |
22 | /* |
23 | * Handler Tables. |
24 | */ |
25 | #define K_HANDLERS\ |
26 | k_self, k_fn, k_spec, k_ignore,\ |
27 | k_dead, k_ignore, k_ignore, k_ignore,\ |
28 | k_ignore, k_ignore, k_ignore, k_ignore,\ |
29 | k_ignore, k_ignore, k_ignore, k_ignore |
30 | |
31 | typedef void (k_handler_fn)(struct kbd_data *, unsigned char); |
32 | static k_handler_fn K_HANDLERS; |
33 | static k_handler_fn *k_handler[16] = { K_HANDLERS }; |
34 | |
35 | /* maximum values each key_handler can handle */ |
36 | static const int kbd_max_vals[] = { |
37 | 255, ARRAY_SIZE(func_table) - 1, NR_FN_HANDLER - 1, 0, |
38 | NR_DEAD - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
39 | }; |
40 | static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals); |
41 | |
42 | static const unsigned char ret_diacr[NR_DEAD] = { |
43 | '`', /* dead_grave */ |
44 | '\'', /* dead_acute */ |
45 | '^', /* dead_circumflex */ |
46 | '~', /* dead_tilda */ |
47 | '"', /* dead_diaeresis */ |
48 | ',', /* dead_cedilla */ |
49 | '_', /* dead_macron */ |
50 | 'U', /* dead_breve */ |
51 | '.', /* dead_abovedot */ |
52 | '*', /* dead_abovering */ |
53 | '=', /* dead_doubleacute */ |
54 | 'c', /* dead_caron */ |
55 | 'k', /* dead_ogonek */ |
56 | 'i', /* dead_iota */ |
57 | '#', /* dead_voiced_sound */ |
58 | 'o', /* dead_semivoiced_sound */ |
59 | '!', /* dead_belowdot */ |
60 | '?', /* dead_hook */ |
61 | '+', /* dead_horn */ |
62 | '-', /* dead_stroke */ |
63 | ')', /* dead_abovecomma */ |
64 | '(', /* dead_abovereversedcomma */ |
65 | ':', /* dead_doublegrave */ |
66 | 'n', /* dead_invertedbreve */ |
67 | ';', /* dead_belowcomma */ |
68 | '$', /* dead_currency */ |
69 | '@', /* dead_greek */ |
70 | }; |
71 | |
72 | /* |
73 | * Alloc/free of kbd_data structures. |
74 | */ |
75 | struct kbd_data * |
76 | kbd_alloc(void) { |
77 | struct kbd_data *kbd; |
78 | int i; |
79 | |
80 | kbd = kzalloc(size: sizeof(struct kbd_data), GFP_KERNEL); |
81 | if (!kbd) |
82 | goto out; |
83 | kbd->key_maps = kzalloc(size: sizeof(ebc_key_maps), GFP_KERNEL); |
84 | if (!kbd->key_maps) |
85 | goto out_kbd; |
86 | for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) { |
87 | if (ebc_key_maps[i]) { |
88 | kbd->key_maps[i] = kmemdup(p: ebc_key_maps[i], |
89 | size: sizeof(u_short) * NR_KEYS, |
90 | GFP_KERNEL); |
91 | if (!kbd->key_maps[i]) |
92 | goto out_maps; |
93 | } |
94 | } |
95 | kbd->func_table = kzalloc(size: sizeof(ebc_func_table), GFP_KERNEL); |
96 | if (!kbd->func_table) |
97 | goto out_maps; |
98 | for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) { |
99 | if (ebc_func_table[i]) { |
100 | kbd->func_table[i] = kstrdup(s: ebc_func_table[i], |
101 | GFP_KERNEL); |
102 | if (!kbd->func_table[i]) |
103 | goto out_func; |
104 | } |
105 | } |
106 | kbd->fn_handler = |
107 | kcalloc(NR_FN_HANDLER, size: sizeof(fn_handler_fn *), GFP_KERNEL); |
108 | if (!kbd->fn_handler) |
109 | goto out_func; |
110 | kbd->accent_table = kmemdup(p: ebc_accent_table, |
111 | size: sizeof(struct kbdiacruc) * MAX_DIACR, |
112 | GFP_KERNEL); |
113 | if (!kbd->accent_table) |
114 | goto out_fn_handler; |
115 | kbd->accent_table_size = ebc_accent_table_size; |
116 | return kbd; |
117 | |
118 | out_fn_handler: |
119 | kfree(objp: kbd->fn_handler); |
120 | out_func: |
121 | for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) |
122 | kfree(objp: kbd->func_table[i]); |
123 | kfree(objp: kbd->func_table); |
124 | out_maps: |
125 | for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) |
126 | kfree(objp: kbd->key_maps[i]); |
127 | kfree(objp: kbd->key_maps); |
128 | out_kbd: |
129 | kfree(objp: kbd); |
130 | out: |
131 | return NULL; |
132 | } |
133 | |
134 | void |
135 | kbd_free(struct kbd_data *kbd) |
136 | { |
137 | int i; |
138 | |
139 | kfree(objp: kbd->accent_table); |
140 | kfree(objp: kbd->fn_handler); |
141 | for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) |
142 | kfree(objp: kbd->func_table[i]); |
143 | kfree(objp: kbd->func_table); |
144 | for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) |
145 | kfree(objp: kbd->key_maps[i]); |
146 | kfree(objp: kbd->key_maps); |
147 | kfree(objp: kbd); |
148 | } |
149 | |
150 | /* |
151 | * Generate ascii -> ebcdic translation table from kbd_data. |
152 | */ |
153 | void |
154 | kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc) |
155 | { |
156 | unsigned short *keymap, keysym; |
157 | int i, j, k; |
158 | |
159 | memset(ascebc, 0x40, 256); |
160 | for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) { |
161 | keymap = kbd->key_maps[i]; |
162 | if (!keymap) |
163 | continue; |
164 | for (j = 0; j < NR_KEYS; j++) { |
165 | k = ((i & 1) << 7) + j; |
166 | keysym = keymap[j]; |
167 | if (KTYP(keysym) == (KT_LATIN | 0xf0) || |
168 | KTYP(keysym) == (KT_LETTER | 0xf0)) |
169 | ascebc[KVAL(keysym)] = k; |
170 | else if (KTYP(keysym) == (KT_DEAD | 0xf0)) |
171 | ascebc[ret_diacr[KVAL(keysym)]] = k; |
172 | } |
173 | } |
174 | } |
175 | |
176 | #if 0 |
177 | /* |
178 | * Generate ebcdic -> ascii translation table from kbd_data. |
179 | */ |
180 | void |
181 | kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc) |
182 | { |
183 | unsigned short *keymap, keysym; |
184 | int i, j, k; |
185 | |
186 | memset(ebcasc, ' ', 256); |
187 | for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) { |
188 | keymap = kbd->key_maps[i]; |
189 | if (!keymap) |
190 | continue; |
191 | for (j = 0; j < NR_KEYS; j++) { |
192 | keysym = keymap[j]; |
193 | k = ((i & 1) << 7) + j; |
194 | if (KTYP(keysym) == (KT_LATIN | 0xf0) || |
195 | KTYP(keysym) == (KT_LETTER | 0xf0)) |
196 | ebcasc[k] = KVAL(keysym); |
197 | else if (KTYP(keysym) == (KT_DEAD | 0xf0)) |
198 | ebcasc[k] = ret_diacr[KVAL(keysym)]; |
199 | } |
200 | } |
201 | } |
202 | #endif |
203 | |
204 | /* |
205 | * We have a combining character DIACR here, followed by the character CH. |
206 | * If the combination occurs in the table, return the corresponding value. |
207 | * Otherwise, if CH is a space or equals DIACR, return DIACR. |
208 | * Otherwise, conclude that DIACR was not combining after all, |
209 | * queue it and return CH. |
210 | */ |
211 | static unsigned int |
212 | handle_diacr(struct kbd_data *kbd, unsigned int ch) |
213 | { |
214 | int i, d; |
215 | |
216 | d = kbd->diacr; |
217 | kbd->diacr = 0; |
218 | |
219 | for (i = 0; i < kbd->accent_table_size; i++) { |
220 | if (kbd->accent_table[i].diacr == d && |
221 | kbd->accent_table[i].base == ch) |
222 | return kbd->accent_table[i].result; |
223 | } |
224 | |
225 | if (ch == ' ' || ch == d) |
226 | return d; |
227 | |
228 | kbd_put_queue(port: kbd->port, ch: d); |
229 | return ch; |
230 | } |
231 | |
232 | /* |
233 | * Handle dead key. |
234 | */ |
235 | static void |
236 | k_dead(struct kbd_data *kbd, unsigned char value) |
237 | { |
238 | value = ret_diacr[value]; |
239 | kbd->diacr = (kbd->diacr ? handle_diacr(kbd, ch: value) : value); |
240 | } |
241 | |
242 | /* |
243 | * Normal character handler. |
244 | */ |
245 | static void |
246 | k_self(struct kbd_data *kbd, unsigned char value) |
247 | { |
248 | if (kbd->diacr) |
249 | value = handle_diacr(kbd, ch: value); |
250 | kbd_put_queue(port: kbd->port, ch: value); |
251 | } |
252 | |
253 | /* |
254 | * Special key handlers |
255 | */ |
256 | static void |
257 | k_ignore(struct kbd_data *kbd, unsigned char value) |
258 | { |
259 | } |
260 | |
261 | /* |
262 | * Function key handler. |
263 | */ |
264 | static void |
265 | k_fn(struct kbd_data *kbd, unsigned char value) |
266 | { |
267 | if (kbd->func_table[value]) |
268 | kbd_puts_queue(port: kbd->port, cp: kbd->func_table[value]); |
269 | } |
270 | |
271 | static void |
272 | k_spec(struct kbd_data *kbd, unsigned char value) |
273 | { |
274 | if (value >= NR_FN_HANDLER) |
275 | return; |
276 | if (kbd->fn_handler[value]) |
277 | kbd->fn_handler[value](kbd); |
278 | } |
279 | |
280 | /* |
281 | * Put utf8 character to tty flip buffer. |
282 | * UTF-8 is defined for words of up to 31 bits, |
283 | * but we need only 16 bits here |
284 | */ |
285 | static void |
286 | to_utf8(struct tty_port *port, ushort c) |
287 | { |
288 | if (c < 0x80) |
289 | /* 0******* */ |
290 | kbd_put_queue(port, ch: c); |
291 | else if (c < 0x800) { |
292 | /* 110***** 10****** */ |
293 | kbd_put_queue(port, ch: 0xc0 | (c >> 6)); |
294 | kbd_put_queue(port, ch: 0x80 | (c & 0x3f)); |
295 | } else { |
296 | /* 1110**** 10****** 10****** */ |
297 | kbd_put_queue(port, ch: 0xe0 | (c >> 12)); |
298 | kbd_put_queue(port, ch: 0x80 | ((c >> 6) & 0x3f)); |
299 | kbd_put_queue(port, ch: 0x80 | (c & 0x3f)); |
300 | } |
301 | } |
302 | |
303 | /* |
304 | * Process keycode. |
305 | */ |
306 | void |
307 | kbd_keycode(struct kbd_data *kbd, unsigned int keycode) |
308 | { |
309 | unsigned short keysym; |
310 | unsigned char type, value; |
311 | |
312 | if (!kbd) |
313 | return; |
314 | |
315 | if (keycode >= 384) |
316 | keysym = kbd->key_maps[5][keycode - 384]; |
317 | else if (keycode >= 256) |
318 | keysym = kbd->key_maps[4][keycode - 256]; |
319 | else if (keycode >= 128) |
320 | keysym = kbd->key_maps[1][keycode - 128]; |
321 | else |
322 | keysym = kbd->key_maps[0][keycode]; |
323 | |
324 | type = KTYP(keysym); |
325 | if (type >= 0xf0) { |
326 | type -= 0xf0; |
327 | if (type == KT_LETTER) |
328 | type = KT_LATIN; |
329 | value = KVAL(keysym); |
330 | #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ |
331 | if (kbd->sysrq) { |
332 | if (kbd->sysrq == K(KT_LATIN, '-')) { |
333 | kbd->sysrq = 0; |
334 | handle_sysrq(key: value); |
335 | return; |
336 | } |
337 | if (value == '-') { |
338 | kbd->sysrq = K(KT_LATIN, '-'); |
339 | return; |
340 | } |
341 | /* Incomplete sysrq sequence. */ |
342 | (*k_handler[KTYP(kbd->sysrq)])(kbd, KVAL(kbd->sysrq)); |
343 | kbd->sysrq = 0; |
344 | } else if ((type == KT_LATIN && value == '^') || |
345 | (type == KT_DEAD && ret_diacr[value] == '^')) { |
346 | kbd->sysrq = K(type, value); |
347 | return; |
348 | } |
349 | #endif |
350 | (*k_handler[type])(kbd, value); |
351 | } else |
352 | to_utf8(port: kbd->port, c: keysym); |
353 | } |
354 | |
355 | /* |
356 | * Ioctl stuff. |
357 | */ |
358 | static int |
359 | do_kdsk_ioctl(struct kbd_data *kbd, struct kbentry __user *user_kbe, |
360 | int cmd, int perm) |
361 | { |
362 | struct kbentry tmp; |
363 | unsigned long kb_index, kb_table; |
364 | ushort *key_map, val, ov; |
365 | |
366 | if (copy_from_user(to: &tmp, from: user_kbe, n: sizeof(struct kbentry))) |
367 | return -EFAULT; |
368 | kb_index = (unsigned long) tmp.kb_index; |
369 | #if NR_KEYS < 256 |
370 | if (kb_index >= NR_KEYS) |
371 | return -EINVAL; |
372 | #endif |
373 | kb_table = (unsigned long) tmp.kb_table; |
374 | #if MAX_NR_KEYMAPS < 256 |
375 | if (kb_table >= MAX_NR_KEYMAPS) |
376 | return -EINVAL; |
377 | kb_table = array_index_nospec(kb_table , MAX_NR_KEYMAPS); |
378 | #endif |
379 | |
380 | switch (cmd) { |
381 | case KDGKBENT: |
382 | key_map = kbd->key_maps[kb_table]; |
383 | if (key_map) { |
384 | val = U(key_map[kb_index]); |
385 | if (KTYP(val) >= KBD_NR_TYPES) |
386 | val = K_HOLE; |
387 | } else |
388 | val = (kb_index ? K_HOLE : K_NOSUCHMAP); |
389 | return put_user(val, &user_kbe->kb_value); |
390 | case KDSKBENT: |
391 | if (!perm) |
392 | return -EPERM; |
393 | if (!kb_index && tmp.kb_value == K_NOSUCHMAP) { |
394 | /* disallocate map */ |
395 | key_map = kbd->key_maps[kb_table]; |
396 | if (key_map) { |
397 | kbd->key_maps[kb_table] = NULL; |
398 | kfree(objp: key_map); |
399 | } |
400 | break; |
401 | } |
402 | |
403 | if (KTYP(tmp.kb_value) >= KBD_NR_TYPES) |
404 | return -EINVAL; |
405 | if (KVAL(tmp.kb_value) > kbd_max_vals[KTYP(tmp.kb_value)]) |
406 | return -EINVAL; |
407 | |
408 | if (!(key_map = kbd->key_maps[kb_table])) { |
409 | int j; |
410 | |
411 | key_map = kmalloc(size: sizeof(plain_map), |
412 | GFP_KERNEL); |
413 | if (!key_map) |
414 | return -ENOMEM; |
415 | kbd->key_maps[kb_table] = key_map; |
416 | for (j = 0; j < NR_KEYS; j++) |
417 | key_map[j] = U(K_HOLE); |
418 | } |
419 | ov = U(key_map[kb_index]); |
420 | if (tmp.kb_value == ov) |
421 | break; /* nothing to do */ |
422 | /* |
423 | * Attention Key. |
424 | */ |
425 | if (((ov == K_SAK) || (tmp.kb_value == K_SAK)) && |
426 | !capable(CAP_SYS_ADMIN)) |
427 | return -EPERM; |
428 | key_map[kb_index] = U(tmp.kb_value); |
429 | break; |
430 | } |
431 | return 0; |
432 | } |
433 | |
434 | static int |
435 | do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs, |
436 | int cmd, int perm) |
437 | { |
438 | unsigned char kb_func; |
439 | char *p; |
440 | int len; |
441 | |
442 | /* Get u_kbs->kb_func. */ |
443 | if (get_user(kb_func, &u_kbs->kb_func)) |
444 | return -EFAULT; |
445 | #if MAX_NR_FUNC < 256 |
446 | if (kb_func >= MAX_NR_FUNC) |
447 | return -EINVAL; |
448 | #endif |
449 | |
450 | switch (cmd) { |
451 | case KDGKBSENT: |
452 | p = kbd->func_table[kb_func]; |
453 | if (p) { |
454 | len = strlen(p); |
455 | if (len >= sizeof(u_kbs->kb_string)) |
456 | len = sizeof(u_kbs->kb_string) - 1; |
457 | if (copy_to_user(to: u_kbs->kb_string, from: p, n: len)) |
458 | return -EFAULT; |
459 | } else |
460 | len = 0; |
461 | if (put_user('\0', u_kbs->kb_string + len)) |
462 | return -EFAULT; |
463 | break; |
464 | case KDSKBSENT: |
465 | if (!perm) |
466 | return -EPERM; |
467 | p = strndup_user(u_kbs->kb_string, sizeof(u_kbs->kb_string)); |
468 | if (IS_ERR(ptr: p)) |
469 | return PTR_ERR(ptr: p); |
470 | kfree(objp: kbd->func_table[kb_func]); |
471 | kbd->func_table[kb_func] = p; |
472 | break; |
473 | } |
474 | return 0; |
475 | } |
476 | |
477 | int kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg) |
478 | { |
479 | struct tty_struct *tty; |
480 | void __user *argp; |
481 | unsigned int ct; |
482 | int perm; |
483 | |
484 | argp = (void __user *)arg; |
485 | |
486 | /* |
487 | * To have permissions to do most of the vt ioctls, we either have |
488 | * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. |
489 | */ |
490 | tty = tty_port_tty_get(port: kbd->port); |
491 | /* FIXME this test is pretty racy */ |
492 | perm = current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG); |
493 | tty_kref_put(tty); |
494 | switch (cmd) { |
495 | case KDGKBTYPE: |
496 | return put_user(KB_101, (char __user *)argp); |
497 | case KDGKBENT: |
498 | case KDSKBENT: |
499 | return do_kdsk_ioctl(kbd, user_kbe: argp, cmd, perm); |
500 | case KDGKBSENT: |
501 | case KDSKBSENT: |
502 | return do_kdgkb_ioctl(kbd, u_kbs: argp, cmd, perm); |
503 | case KDGKBDIACR: |
504 | { |
505 | struct kbdiacrs __user *a = argp; |
506 | struct kbdiacr diacr; |
507 | int i; |
508 | |
509 | if (put_user(kbd->accent_table_size, &a->kb_cnt)) |
510 | return -EFAULT; |
511 | for (i = 0; i < kbd->accent_table_size; i++) { |
512 | diacr.diacr = kbd->accent_table[i].diacr; |
513 | diacr.base = kbd->accent_table[i].base; |
514 | diacr.result = kbd->accent_table[i].result; |
515 | if (copy_to_user(to: a->kbdiacr + i, from: &diacr, n: sizeof(struct kbdiacr))) |
516 | return -EFAULT; |
517 | } |
518 | return 0; |
519 | } |
520 | case KDGKBDIACRUC: |
521 | { |
522 | struct kbdiacrsuc __user *a = argp; |
523 | |
524 | ct = kbd->accent_table_size; |
525 | if (put_user(ct, &a->kb_cnt)) |
526 | return -EFAULT; |
527 | if (copy_to_user(to: a->kbdiacruc, from: kbd->accent_table, |
528 | n: ct * sizeof(struct kbdiacruc))) |
529 | return -EFAULT; |
530 | return 0; |
531 | } |
532 | case KDSKBDIACR: |
533 | { |
534 | struct kbdiacrs __user *a = argp; |
535 | struct kbdiacr diacr; |
536 | int i; |
537 | |
538 | if (!perm) |
539 | return -EPERM; |
540 | if (get_user(ct, &a->kb_cnt)) |
541 | return -EFAULT; |
542 | if (ct >= MAX_DIACR) |
543 | return -EINVAL; |
544 | kbd->accent_table_size = ct; |
545 | for (i = 0; i < ct; i++) { |
546 | if (copy_from_user(to: &diacr, from: a->kbdiacr + i, n: sizeof(struct kbdiacr))) |
547 | return -EFAULT; |
548 | kbd->accent_table[i].diacr = diacr.diacr; |
549 | kbd->accent_table[i].base = diacr.base; |
550 | kbd->accent_table[i].result = diacr.result; |
551 | } |
552 | return 0; |
553 | } |
554 | case KDSKBDIACRUC: |
555 | { |
556 | struct kbdiacrsuc __user *a = argp; |
557 | |
558 | if (!perm) |
559 | return -EPERM; |
560 | if (get_user(ct, &a->kb_cnt)) |
561 | return -EFAULT; |
562 | if (ct >= MAX_DIACR) |
563 | return -EINVAL; |
564 | kbd->accent_table_size = ct; |
565 | if (copy_from_user(to: kbd->accent_table, from: a->kbdiacruc, |
566 | n: ct * sizeof(struct kbdiacruc))) |
567 | return -EFAULT; |
568 | return 0; |
569 | } |
570 | default: |
571 | return -ENOIOCTLCMD; |
572 | } |
573 | } |
574 | |
575 | EXPORT_SYMBOL(kbd_ioctl); |
576 | EXPORT_SYMBOL(kbd_ascebc); |
577 | EXPORT_SYMBOL(kbd_free); |
578 | EXPORT_SYMBOL(kbd_alloc); |
579 | EXPORT_SYMBOL(kbd_keycode); |
580 | |