diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/amd64/amd64/machdep.c | 1 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/proc_machdep.c | 35 | ||||
-rw-r--r-- | sys/arch/amd64/isa/i8042.c | 162 | ||||
-rw-r--r-- | sys/include/arch/amd64/cpu.h | 1 | ||||
-rw-r--r-- | sys/include/arch/amd64/isa/i8042var.h | 4 | ||||
-rw-r--r-- | sys/include/sys/sched.h | 3 | ||||
-rw-r--r-- | sys/include/sys/spinlock.h | 3 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 8 | ||||
-rw-r--r-- | sys/kern/kern_synch.c | 31 |
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); } /* |