580 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * ebcdic keycode functions for s390 console drivers4 *5 * S390 version6 * Copyright IBM Corp. 20037 * 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_ignore30 31typedef void (k_handler_fn)(struct kbd_data *, unsigned char);32static k_handler_fn K_HANDLERS;33static k_handler_fn *k_handler[16] = { K_HANDLERS };34 35/* maximum values each key_handler can handle */36static 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, 039};40static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals);41 42static 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 */75struct kbd_data *76kbd_alloc(void) {77 struct kbd_data *kbd;78 int i;79 80 kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);81 if (!kbd)82 goto out;83 kbd->key_maps = kzalloc(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(ebc_key_maps[i],89 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(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(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, sizeof(fn_handler_fn *), GFP_KERNEL);108 if (!kbd->fn_handler)109 goto out_func;110 kbd->accent_table = kmemdup(ebc_accent_table,111 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 118out_fn_handler:119 kfree(kbd->fn_handler);120out_func:121 for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)122 kfree(kbd->func_table[i]);123 kfree(kbd->func_table);124out_maps:125 for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)126 kfree(kbd->key_maps[i]);127 kfree(kbd->key_maps);128out_kbd:129 kfree(kbd);130out:131 return NULL;132}133 134void135kbd_free(struct kbd_data *kbd)136{137 int i;138 139 kfree(kbd->accent_table);140 kfree(kbd->fn_handler);141 for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)142 kfree(kbd->func_table[i]);143 kfree(kbd->func_table);144 for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)145 kfree(kbd->key_maps[i]);146 kfree(kbd->key_maps);147 kfree(kbd);148}149 150/*151 * Generate ascii -> ebcdic translation table from kbd_data.152 */153void154kbd_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 0177/*178 * Generate ebcdic -> ascii translation table from kbd_data.179 */180void181kbd_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#endif203 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 */211static unsigned int212handle_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(kbd->port, d);229 return ch;230}231 232/*233 * Handle dead key.234 */235static void236k_dead(struct kbd_data *kbd, unsigned char value)237{238 value = ret_diacr[value];239 kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);240}241 242/*243 * Normal character handler.244 */245static void246k_self(struct kbd_data *kbd, unsigned char value)247{248 if (kbd->diacr)249 value = handle_diacr(kbd, value);250 kbd_put_queue(kbd->port, value);251}252 253/*254 * Special key handlers255 */256static void257k_ignore(struct kbd_data *kbd, unsigned char value)258{259}260 261/*262 * Function key handler.263 */264static void265k_fn(struct kbd_data *kbd, unsigned char value)266{267 if (kbd->func_table[value])268 kbd_puts_queue(kbd->port, kbd->func_table[value]);269}270 271static void272k_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 here284 */285static void286to_utf8(struct tty_port *port, ushort c)287{288 if (c < 0x80)289 /* 0******* */290 kbd_put_queue(port, c);291 else if (c < 0x800) {292 /* 110***** 10****** */293 kbd_put_queue(port, 0xc0 | (c >> 6));294 kbd_put_queue(port, 0x80 | (c & 0x3f));295 } else {296 /* 1110**** 10****** 10****** */297 kbd_put_queue(port, 0xe0 | (c >> 12));298 kbd_put_queue(port, 0x80 | ((c >> 6) & 0x3f));299 kbd_put_queue(port, 0x80 | (c & 0x3f));300 }301}302 303/*304 * Process keycode.305 */306void307kbd_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 else322 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(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#endif350 (*k_handler[type])(kbd, value);351 } else352 to_utf8(kbd->port, keysym);353}354 355/*356 * Ioctl stuff.357 */358static int359do_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(&tmp, user_kbe, sizeof(struct kbentry)))367 return -EFAULT;368 kb_index = (unsigned long) tmp.kb_index;369#if NR_KEYS < 256370 if (kb_index >= NR_KEYS)371 return -EINVAL;372#endif373 kb_table = (unsigned long) tmp.kb_table;374#if MAX_NR_KEYMAPS < 256375 if (kb_table >= MAX_NR_KEYMAPS)376 return -EINVAL; 377 kb_table = array_index_nospec(kb_table , MAX_NR_KEYMAPS);378#endif379 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 } else388 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(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(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 434static int435do_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 < 256446 if (kb_func >= MAX_NR_FUNC)447 return -EINVAL;448#endif449 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(u_kbs->kb_string, p, len))458 return -EFAULT;459 } else460 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(p))469 return PTR_ERR(p);470 kfree(kbd->func_table[kb_func]);471 kbd->func_table[kb_func] = p;472 break;473 }474 return 0;475}476 477int 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 have488 * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.489 */490 tty = tty_port_tty_get(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, argp, cmd, perm);500 case KDGKBSENT:501 case KDSKBSENT:502 return do_kdgkb_ioctl(kbd, 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(a->kbdiacr + i, &diacr, 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(a->kbdiacruc, kbd->accent_table,528 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(&diacr, a->kbdiacr + i, 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(kbd->accent_table, a->kbdiacruc,566 ct * sizeof(struct kbdiacruc)))567 return -EFAULT;568 return 0;569 }570 default:571 return -ENOIOCTLCMD;572 }573}574 575EXPORT_SYMBOL(kbd_ioctl);576EXPORT_SYMBOL(kbd_ascebc);577EXPORT_SYMBOL(kbd_free);578EXPORT_SYMBOL(kbd_alloc);579EXPORT_SYMBOL(kbd_keycode);580