summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/amd64/machdep.c1
-rw-r--r--sys/arch/amd64/amd64/proc_machdep.c35
-rw-r--r--sys/arch/amd64/isa/i8042.c162
-rw-r--r--sys/include/arch/amd64/cpu.h1
-rw-r--r--sys/include/arch/amd64/isa/i8042var.h4
-rw-r--r--sys/include/sys/sched.h3
-rw-r--r--sys/include/sys/spinlock.h3
-rw-r--r--sys/kern/kern_exit.c8
-rw-r--r--sys/kern/kern_synch.c31
9 files changed, 192 insertions, 56 deletions
diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c
index 94b2d18..72de150 100644
--- a/sys/arch/amd64/amd64/machdep.c
+++ b/sys/arch/amd64/amd64/machdep.c
@@ -609,6 +609,7 @@ cpu_startup(struct cpu_info *ci)
try_mitigate_spectre();
ci->online = 1;
+ ci->preempt = 1;
cpu_get_info(ci);
cpu_enable_smep();
diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c
index a1d6563..82b4e4f 100644
--- a/sys/arch/amd64/amd64/proc_machdep.c
+++ b/sys/arch/amd64/amd64/proc_machdep.c
@@ -264,6 +264,36 @@ sched_switch_to(struct trapframe *tf, struct proc *td)
}
/*
+ * Enable or disable preemption on the current
+ * processor
+ *
+ * @enable: Enable preemption if true
+ */
+void
+sched_preempt_set(bool enable)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return;
+ }
+
+ ci->preempt = enable;
+}
+
+bool
+sched_preemptable(void)
+{
+ struct cpu_info *ci = this_cpu();
+
+ if (ci == NULL) {
+ return false;
+ }
+
+ return ci->preempt;
+}
+
+/*
* Perform a context switch.
*/
void
@@ -273,6 +303,11 @@ md_sched_switch(struct trapframe *tf)
struct cpu_info *ci;
ci = this_cpu();
+ if (!ci->preempt) {
+ sched_oneshot(false);
+ return;
+ }
+
td = ci->curtd;
mi_sched_switch(td);
diff --git a/sys/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c
index 3ae645d..095f1f4 100644
--- a/sys/arch/amd64/isa/i8042.c
+++ b/sys/arch/amd64/isa/i8042.c
@@ -67,7 +67,28 @@
#define IO_NOP() inb(0x80)
-static struct spinlock data_lock;
+struct i8042_databuf {
+ uint8_t data[8];
+ size_t len;
+};
+
+/*
+ * This table allows the lookup of extended
+ * scancode bytes.
+ *
+ * XXX: Excludes the 0xE0 byte
+ */
+static struct i8042_databuf i8042_etab[] = {
+ [ I8042_XSC_ENDPR] = {
+ .data = { 0x4F },
+ .len = 1
+ },
+ [I8042_XSC_ENDRL] = {
+ .data = { 0xCF },
+ .len = 1
+ }
+};
+
static struct spinlock isr_lock;
static bool shift_key = false;
static bool capslock = false;
@@ -80,7 +101,7 @@ static bool is_init = false;
static void i8042_ibuf_wait(void);
static int dev_send(bool aux, uint8_t data);
static int i8042_kb_getc(uint8_t sc, char *chr);
-static void i8042_drain(void);
+static void i8042_drain(struct i8042_databuf *res);
static char keytab[] = {
'\0', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
@@ -141,15 +162,28 @@ i8042_ibuf_wait(void)
/*
* Drain i8042 internal data registers.
+ *
+ * @res: Pointer for read data to be buffered to
+ *
+ * XXX: The 'res' argument is NULLable
*/
static void
-i8042_drain(void)
+i8042_drain(struct i8042_databuf *res)
{
- spinlock_acquire(&data_lock);
while (ISSET(inb(I8042_STATUS), I8042_OBUFF)) {
- inb(I8042_DATA);
+ if (res == NULL) {
+ inb(I8042_DATA);
+ continue;
+ }
+
+ if (res->len >= sizeof(res->data)) {
+ pr_error("data recieved from i8042 is too big\n");
+ break;
+ }
+
+ res->data[res->len++] = inb(I8042_DATA);
+ tmr.msleep(10);
}
- spinlock_release(&data_lock);
}
/*
@@ -271,6 +305,78 @@ i8042_en_intr(void)
}
/*
+ * Toggle the capslock and LED
+ */
+static void
+capslock_toggle(void)
+{
+ /*
+ * In case we are holding the caps lock button down,
+ * we don't want it to be spam toggled as that would
+ * be pretty strange looking and probably annoying.
+ */
+ if (!capslock_released) {
+ return;
+ }
+
+ capslock_released = false;
+ capslock = !capslock;
+
+ if (!capslock) {
+ kbd_set_leds(0);
+ } else {
+ kbd_set_leds(I8042_LED_CAPS);
+ }
+}
+
+/*
+ * Dump extended data buffer
+ *
+ * @buf: Data
+ */
+static void
+i8042_ext_dump(struct i8042_databuf *buf)
+{
+ if (buf == NULL) {
+ return;
+ }
+
+ for (int i = 0; i < buf->len; ++i) {
+ kprintf(OMIT_TIMESTAMP "%x", buf->data[i]);
+ }
+
+ kprintf(OMIT_TIMESTAMP "\n");
+}
+
+/*
+ * Used internally by i8042_kb_getc() to acquire
+ * a key from an extended scancode
+ *
+ * @buf: Scancode buf
+ * @chr: Char res
+ *
+ * Returns the extended scancode type on success,
+ * otherwise a less than zero value (see I8042_XSC_*)
+ */
+static int
+i8042_kb_getxc(struct i8042_databuf *buf, char *chr)
+{
+ size_t nelem = NELEM(i8042_etab);
+ struct i8042_databuf *buf_tmp;
+ size_t len;
+
+ for (int i = 0; i < nelem; ++i) {
+ buf_tmp = &i8042_etab[i];
+ len = buf_tmp->len;
+ if (memcmp(buf->data, buf_tmp->data, len) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
* Convert scancode to character
*
* @sc: Scancode
@@ -282,6 +388,8 @@ static int
i8042_kb_getc(uint8_t sc, char *chr)
{
bool release = ISSET(sc, BIT(7));
+ struct i8042_databuf buf = {0};
+ int x_type;
switch (sc) {
case 0x76:
@@ -289,23 +397,7 @@ i8042_kb_getc(uint8_t sc, char *chr)
return 0;
/* Caps lock [press] */
case 0x3A:
- /*
- * In case we are holding the caps lock button down,
- * we don't want it to be spam toggled as that would
- * be pretty strange looking and probably annoying.
- */
- if (!capslock_released) {
- return -EAGAIN;
- }
-
- capslock_released = false;
- capslock = !capslock;
-
- if (!capslock) {
- kbd_set_leds(0);
- } else {
- kbd_set_leds(I8042_LED_CAPS);
- }
+ capslock_toggle();
return -EAGAIN;
/* Caps lock [release] */
case 0xBA:
@@ -322,6 +414,26 @@ i8042_kb_getc(uint8_t sc, char *chr)
shift_key = false;
}
return -EAGAIN;
+ /* Extended byte */
+ case 0xE0:
+ /*
+ * Most keyboards have extended scancodes which
+ * consist of multiple bytes to represent certain
+ * special keys. We'll need to give the controller
+ * about 10 ms to refill its buffer.
+ */
+ tmr.msleep(10);
+ i8042_drain(&buf);
+ x_type = i8042_kb_getxc(&buf, chr);
+
+ /* Did we implement it? */
+ if (x_type < 0) {
+ pr_error("unknown xsc: ");
+ i8042_ext_dump(&buf);
+ return -EAGAIN;
+ }
+
+ return -1;
}
if (release) {
@@ -440,7 +552,7 @@ i8042_init(void)
*/
if (!ISSET(quirks, I8042_HOSTILE) && !I8042_POLL) {
/* Enable interrupts */
- i8042_drain();
+ i8042_drain(NULL);
i8042_en_intr();
} else if (ISSET(quirks, I8042_HOSTILE) || I8042_POLL) {
spawn(&polltd, i8042_sync_loop, NULL, 0, NULL);
@@ -448,7 +560,7 @@ i8042_init(void)
}
i8042_write(I8042_CMD, I8042_ENABLE_PORT0);
- i8042_drain();
+ i8042_drain(NULL);
is_init = true;
return 0;
}
diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h
index 6ed675e..cf073fe 100644
--- a/sys/include/arch/amd64/cpu.h
+++ b/sys/include/arch/amd64/cpu.h
@@ -58,6 +58,7 @@ struct cpu_info {
uint32_t apicid;
uint32_t feat;
uint32_t vendor; /* Vendor (see CPU_VENDOR_*) */
+ uint8_t preempt : 1; /* CPU is preemptable */
uint8_t ipi_dispatch : 1; /* 1: IPIs being dispatched */
uint8_t ipi_id;
ipi_pend_t ipi_pending[N_IPIVEC];
diff --git a/sys/include/arch/amd64/isa/i8042var.h b/sys/include/arch/amd64/isa/i8042var.h
index 13c3095..9619920 100644
--- a/sys/include/arch/amd64/isa/i8042var.h
+++ b/sys/include/arch/amd64/isa/i8042var.h
@@ -74,6 +74,10 @@
#define I8042_LED_NUM BIT(1)
#define I8042_LED_CAPS BIT(2)
+/* Extended scancode types */
+#define I8042_XSC_ENDPR 0 /* End pressed */
+#define I8042_XSC_ENDRL 1 /* End released */
+
/* Quirks */
#define I8042_HOSTILE BIT(0) /* If EC likes throwing NMIs */
diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h
index 8b0ba02..7bba9df 100644
--- a/sys/include/sys/sched.h
+++ b/sys/include/sys/sched.h
@@ -66,6 +66,9 @@ struct sched_stat {
void sched_stat(struct sched_stat *statp);
void sched_init(void);
+void sched_preempt_set(bool enable);
+bool sched_preemptable(void);
+
void sched_yield(void);
void sched_suspend(struct proc *td, const struct timeval *tv);
void sched_detach(struct proc *td);
diff --git a/sys/include/sys/spinlock.h b/sys/include/sys/spinlock.h
index 140addc..b416152 100644
--- a/sys/include/sys/spinlock.h
+++ b/sys/include/sys/spinlock.h
@@ -44,9 +44,6 @@ void spinlock_release(struct spinlock *lock);
int spinlock_try_acquire(struct spinlock *lock);
int spinlock_usleep(struct spinlock *lock, size_t usec_max);
-/* System-wide locking (be careful!!) */
-int syslock(void);
-void sysrel(void);
#endif
#endif /* !_SYS_SPINLOCK_H_ */
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index 9377eed..af697d7 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -190,6 +190,14 @@ exit1(struct proc *td, int flags)
* and do not return.
*/
if (target_pid == curpid) {
+ /*
+ * If the thread is exiting on a core that is not
+ * preemptable, something is not right.
+ */
+ if (__unlikely(!sched_preemptable())) {
+ panic("exit1: cpu %d not preemptable\n", ci->id);
+ }
+
ci->curtd = NULL;
if (parent->pid == 0)
sched_enter();
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 497aff7..7660f1f 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -42,9 +42,6 @@
#define pr_trace(fmt, ...) kprintf("synch: " fmt, ##__VA_ARGS__)
#define pr_error(...) pr_trace(__VA_ARGS__)
-/* XXX: Be very careful with this */
-static struct spinlock __syslock;
-
/*
* Returns 0 on success, returns non-zero value
* on timeout/failure.
@@ -84,6 +81,7 @@ spinlock_usleep(struct spinlock *lock, size_t usec_max)
void
spinlock_acquire(struct spinlock *lock)
{
+ sched_preempt_set(false);
while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE)) {
md_pause();
}
@@ -110,37 +108,14 @@ spinlock_try_acquire(struct spinlock *lock)
return 1;
}
- while (__atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE));
- return 0;
+ return __atomic_test_and_set(&lock->lock, __ATOMIC_ACQUIRE);
}
void
spinlock_release(struct spinlock *lock)
{
__atomic_clear(&lock->lock, __ATOMIC_RELEASE);
-}
-
-/*
- * Attempt to hold the system-wide lock, returns 1
- * if already held.
- *
- * XXX: Only use for CRITICAL code sections.
- */
-int
-syslock(void)
-{
- return spinlock_try_acquire(&__syslock);
-}
-
-/*
- * Release the system-wide lock
- *
- * XXX: Only use for CRITICAL code sections.
- */
-void
-sysrel(void)
-{
- spinlock_release(&__syslock);
+ sched_preempt_set(true);
}
/*