cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

selection.c (3588B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/slab.h> /* for kmalloc */
      3#include <linux/consolemap.h>
      4#include <linux/interrupt.h>
      5#include <linux/sched.h>
      6#include <linux/device.h> /* for dev_warn */
      7#include <linux/selection.h>
      8#include <linux/workqueue.h>
      9#include <linux/tty.h>
     10#include <linux/tty_flip.h>
     11#include <linux/atomic.h>
     12#include <linux/console.h>
     13
     14#include "speakup.h"
     15
     16unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
     17struct vc_data *spk_sel_cons;
     18
     19struct speakup_selection_work {
     20	struct work_struct work;
     21	struct tiocl_selection sel;
     22	struct tty_struct *tty;
     23};
     24
     25static void __speakup_set_selection(struct work_struct *work)
     26{
     27	struct speakup_selection_work *ssw =
     28		container_of(work, struct speakup_selection_work, work);
     29
     30	struct tty_struct *tty;
     31	struct tiocl_selection sel;
     32
     33	sel = ssw->sel;
     34
     35	/* this ensures we copy sel before releasing the lock below */
     36	rmb();
     37
     38	/* release the lock by setting tty of the struct to NULL */
     39	tty = xchg(&ssw->tty, NULL);
     40
     41	if (spk_sel_cons != vc_cons[fg_console].d) {
     42		spk_sel_cons = vc_cons[fg_console].d;
     43		pr_warn("Selection: mark console not the same as cut\n");
     44		goto unref;
     45	}
     46
     47	console_lock();
     48	clear_selection();
     49	console_unlock();
     50
     51	set_selection_kernel(&sel, tty);
     52
     53unref:
     54	tty_kref_put(tty);
     55}
     56
     57static struct speakup_selection_work speakup_sel_work = {
     58	.work = __WORK_INITIALIZER(speakup_sel_work.work,
     59				   __speakup_set_selection)
     60};
     61
     62int speakup_set_selection(struct tty_struct *tty)
     63{
     64	/* we get kref here first in order to avoid a subtle race when
     65	 * cancelling selection work. getting kref first establishes the
     66	 * invariant that if speakup_sel_work.tty is not NULL when
     67	 * speakup_cancel_selection() is called, it must be the case that a put
     68	 * kref is pending.
     69	 */
     70	tty_kref_get(tty);
     71	if (cmpxchg(&speakup_sel_work.tty, NULL, tty)) {
     72		tty_kref_put(tty);
     73		return -EBUSY;
     74	}
     75	/* now we have the 'lock' by setting tty member of
     76	 * speakup_selection_work. wmb() ensures that writes to
     77	 * speakup_sel_work don't happen before cmpxchg() above.
     78	 */
     79	wmb();
     80
     81	speakup_sel_work.sel.xs = spk_xs + 1;
     82	speakup_sel_work.sel.ys = spk_ys + 1;
     83	speakup_sel_work.sel.xe = spk_xe + 1;
     84	speakup_sel_work.sel.ye = spk_ye + 1;
     85	speakup_sel_work.sel.sel_mode = TIOCL_SELCHAR;
     86
     87	schedule_work_on(WORK_CPU_UNBOUND, &speakup_sel_work.work);
     88
     89	return 0;
     90}
     91
     92void speakup_cancel_selection(void)
     93{
     94	struct tty_struct *tty;
     95
     96	cancel_work_sync(&speakup_sel_work.work);
     97	/* setting to null so that if work fails to run and we cancel it,
     98	 * we can run it again without getting EBUSY forever from there on.
     99	 * we need to use xchg here to avoid race with speakup_set_selection()
    100	 */
    101	tty = xchg(&speakup_sel_work.tty, NULL);
    102	if (tty)
    103		tty_kref_put(tty);
    104}
    105
    106static void __speakup_paste_selection(struct work_struct *work)
    107{
    108	struct speakup_selection_work *ssw =
    109		container_of(work, struct speakup_selection_work, work);
    110	struct tty_struct *tty = xchg(&ssw->tty, NULL);
    111
    112	paste_selection(tty);
    113	tty_kref_put(tty);
    114}
    115
    116static struct speakup_selection_work speakup_paste_work = {
    117	.work = __WORK_INITIALIZER(speakup_paste_work.work,
    118				   __speakup_paste_selection)
    119};
    120
    121int speakup_paste_selection(struct tty_struct *tty)
    122{
    123	tty_kref_get(tty);
    124	if (cmpxchg(&speakup_paste_work.tty, NULL, tty)) {
    125		tty_kref_put(tty);
    126		return -EBUSY;
    127	}
    128
    129	schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work);
    130	return 0;
    131}
    132
    133void speakup_cancel_paste(void)
    134{
    135	struct tty_struct *tty;
    136
    137	cancel_work_sync(&speakup_paste_work.work);
    138	tty = xchg(&speakup_paste_work.tty, NULL);
    139	if (tty)
    140		tty_kref_put(tty);
    141}