@@ -7,6 +7,7 @@ cflatobjs += lib/x86/smp.o
cflatobjs += lib/x86/vm.o
cflatobjs += lib/x86/fwcfg.o
cflatobjs += lib/x86/apic.o
+cflatobjs += lib/x86/pic.o
cflatobjs += lib/x86/atomic.o
cflatobjs += lib/x86/desc.o
cflatobjs += lib/x86/isr.o
@@ -70,6 +71,8 @@ $(TEST_DIR)/apic.elf: $(cstart.o) $(TEST_DIR)/apic.o
$(TEST_DIR)/ioapic.elf: $(cstart.o) $(TEST_DIR)/ioapic.o
+$(TEST_DIR)/pic.elf: $(cstart.o) $(TEST_DIR)/pic.o
+
$(TEST_DIR)/tscdeadline_latency.elf: $(cstart.o) $(TEST_DIR)/tscdeadline_latency.o
$(TEST_DIR)/init.elf: $(cstart.o) $(TEST_DIR)/init.o
@@ -7,7 +7,7 @@ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \
$(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \
$(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat \
$(TEST_DIR)/pcid.flat $(TEST_DIR)/debug.flat \
- $(TEST_DIR)/ioapic.flat
+ $(TEST_DIR)/ioapic.flat $(TEST_DIR)/pic.flat
tests += $(TEST_DIR)/svm.flat
tests += $(TEST_DIR)/vmx.flat
tests += $(TEST_DIR)/tscdeadline_latency.flat
@@ -1,6 +1,8 @@
#include "libcflat.h"
#include "apic.h"
+#include "io.h"
#include "msr.h"
+#include "pic.h"
static void *g_apic = (void *)0xfee00000;
static void *g_ioapic = (void *)0xfec00000;
@@ -12,9 +14,15 @@ struct apic_ops {
u32 (*id)(void);
};
-static void outb(unsigned char data, unsigned short port)
+void set_irq_line(unsigned line, int val)
{
- asm volatile ("out %0, %1" : : "a"(data), "d"(port));
+ outb(val, 0x2000 + line);
+}
+
+void toggle_irq_line(unsigned line)
+{
+ set_irq_line(line, 1);
+ set_irq_line(line, 0);
}
void eoi(void)
@@ -164,8 +172,10 @@ void enable_apic(void)
xapic_write(0xf0, 0x1ff); /* spurious vector register */
}
-void mask_pic_interrupts(void)
+void unmask_lvt0(void)
{
- outb(0xff, 0x21);
- outb(0xff, 0xa1);
+ int lvt0 = apic_read(APIC_LVT0);
+ lvt0 &= ~APIC_LVT_MASKED;
+ lvt0 = SET_APIC_DELIVERY_MODE(lvt0, APIC_MODE_EXTINT);
+ apic_write(APIC_LVT0, lvt0);
}
@@ -20,6 +20,8 @@ typedef struct {
void mask_pic_interrupts(void);
+void set_irq_line(unsigned line, int val);
+void toggle_irq_line(unsigned line);
void eoi(void);
void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e);
@@ -37,4 +39,5 @@ uint32_t apic_id(void);
int enable_x2apic(void);
+void unmask_lvt0(void);
#endif
new file mode 100644
@@ -0,0 +1,80 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "apic-defs.h"
+#include "io.h"
+#include "isr.h"
+#include "processor.h"
+
+#define PIC_MASTER 0x20
+#define PIC_SLAVE 0xA0
+#define PIC_MASTER_COMMAND PIC_MASTER
+#define PIC_MASTER_DATA (PIC_MASTER+1)
+#define PIC_SLAVE_COMMAND PIC_SLAVE
+#define PIC_SLAVE_DATA (PIC_SLAVE+1)
+
+#define PIC_IRQ_MASK 0x7
+#define PIC_NONSPECIFIC_EOI 0x20
+#define PIC_EOI 0x60
+
+#define PIC_MASTER_CASCADE_LINE 0x2
+
+unsigned char pic_read_data(int slave)
+{
+ unsigned char port = (slave) ? PIC_SLAVE_DATA : PIC_MASTER_DATA;
+
+ return inb(port);
+}
+
+void pic_write_data(unsigned char value, int slave)
+{
+ unsigned char port = (slave) ? PIC_SLAVE_DATA : PIC_MASTER_DATA;
+
+ outb(value, port);
+}
+
+unsigned char pic_read_command(int slave)
+{
+ unsigned char port = (slave) ? PIC_SLAVE_COMMAND : PIC_MASTER_COMMAND;
+
+ return inb(port);
+}
+
+void pic_write_command(unsigned char value, int slave)
+{
+ unsigned char port = (slave) ? PIC_SLAVE_COMMAND : PIC_MASTER_COMMAND;
+
+ outb(value, port);
+}
+
+void pic_eoi(unsigned char irq)
+{
+ if (irq >= 8) {
+ pic_write_command((irq & PIC_IRQ_MASK) | PIC_EOI, true);
+ pic_write_command(PIC_MASTER_CASCADE_LINE | PIC_EOI, false);
+ } else
+ pic_write_command(irq | PIC_EOI, false);
+}
+
+void pic_nonspecific_eoi(unsigned char irq)
+{
+ if (irq >= 8)
+ pic_write_command(PIC_NONSPECIFIC_EOI, true);
+
+ pic_write_command(PIC_NONSPECIFIC_EOI, false);
+}
+
+void set_pic_mask(unsigned char master, unsigned char slave)
+{
+ pic_write_data(master, false);
+ pic_write_data(slave, true);
+}
+
+void mask_pic_interrupts(void)
+{
+ set_pic_mask(0xff, 0xff);
+}
+
+void unmask_pic_interrupts(void)
+{
+ set_pic_mask(0x0, 0x0);
+}
new file mode 100644
@@ -0,0 +1,20 @@
+#ifndef CFLAT_PIC_H
+#define CFLAT_PIC_H
+
+#define PIC_MASTER false
+#define PIC_SLAVE true
+
+void set_pic_mask(unsigned char master, unsigned char slave);
+void mask_pic_interrupts(void);
+void unmask_pic_interrupts(void);
+
+void pic_write_data(unsigned char value, int slave);
+unsigned char pic_read_data(int slave);
+
+void pic_write_command(unsigned char value, int slave);
+unsigned char pic_read_command(int slave);
+
+void pic_eoi(unsigned char irq);
+void pic_nonspecific_eoi(unsigned char irq);
+
+#endif /* CFLAT_PIC_H */
@@ -19,17 +19,6 @@ static void set_ioapic_redir(unsigned line, unsigned vec, unsigned trig_mode)
ioapic_write_redir(line, e);
}
-static void set_irq_line(unsigned line, int val)
-{
- asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
-}
-
-static void toggle_irq_line(unsigned line)
-{
- set_irq_line(line, 1);
- set_irq_line(line, 0);
-}
-
static void ioapic_reg_version(void)
{
u8 version_offset;
new file mode 100644
@@ -0,0 +1,330 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "vm.h"
+#include "smp.h"
+#include "desc.h"
+#include "pic.h"
+#include "processor.h"
+#include "isr.h"
+
+#define MASTER_IRQ_BASE 0x08
+#define SLAVE_IRQ_BASE 0x70
+
+/* Uses GSI's 1 and 3 to avoid overlapping with the PIT and the cascade pin. */
+static volatile int g_master_pic_1;
+static volatile int g_master_pic_3;
+
+static void master_pic_1(isr_regs_t *regs)
+{
+ g_master_pic_1 = 1;
+ set_irq_line(0x01, 0x0);
+ pic_eoi(0x1);
+}
+
+static void master_pic_3(isr_regs_t *regs)
+{
+ g_master_pic_3 = 1;
+ set_irq_line(0x3, 0x0);
+ pic_eoi(0x3);
+}
+
+static void master_pic_autoeoi_1(isr_regs_t *regs)
+{
+ g_master_pic_1 = 1;
+ set_irq_line(0x01, 0x0);
+}
+
+static void master_pic_autoeoi_3(isr_regs_t *regs)
+{
+ g_master_pic_3 = 1;
+ set_irq_line(0x3, 0x0);
+}
+
+static volatile int g_slave_pic_0;
+static volatile int g_slave_pic_1;
+
+static void slave_pic_0(isr_regs_t *regs)
+{
+ g_slave_pic_0 = 1;
+ set_irq_line(0x8, 0x0);
+ pic_eoi(0x8);
+}
+
+static void slave_pic_1(isr_regs_t *regs)
+{
+ g_slave_pic_1 = 1;
+ set_irq_line(0x9, 0x0);
+ pic_eoi(0x9);
+}
+
+static void slave_pic_autoeoi_0(isr_regs_t *regs)
+{
+ g_slave_pic_0 = 1;
+ set_irq_line(0x8, 0x0);
+}
+
+static void slave_pic_autoeoi_1(isr_regs_t *regs)
+{
+ g_slave_pic_1 = 1;
+ set_irq_line(0x9, 0x0);
+}
+
+static void ignore_pit(isr_regs_t *regs)
+{
+ printf("PIT interrupt ignored\n");
+ set_irq_line(0x0, 0x0);
+ pic_eoi(0x0);
+}
+
+static void ignore_pit_autoeoi(isr_regs_t *regs)
+{
+ printf("PIT interrupt ignored\n");
+ set_irq_line(0x0, 0x0);
+}
+
+static void reset_test(void)
+{
+ g_master_pic_1 = 0;
+ g_master_pic_3 = 0;
+
+ g_slave_pic_0 = 0;
+ g_slave_pic_1 = 0;
+}
+
+static void install_default_handlers(void)
+{
+ handle_irq(MASTER_IRQ_BASE + 0x0, ignore_pit);
+
+ handle_irq(MASTER_IRQ_BASE + 0x1, master_pic_1);
+ handle_irq(MASTER_IRQ_BASE + 0x3, master_pic_3);
+
+ handle_irq(SLAVE_IRQ_BASE + 0x0, slave_pic_0);
+ handle_irq(SLAVE_IRQ_BASE + 0x1, slave_pic_1);
+}
+
+
+static void reset_pic_default(void)
+{
+ reset_test();
+ /* ICW1: reset, disable ICW4. */
+ pic_write_command(0x10, PIC_MASTER);
+ /* ICW2: Write base. */
+ pic_write_data(MASTER_IRQ_BASE, PIC_MASTER);
+ /* ICW3: Nothing. */
+ pic_write_data(0x0, PIC_MASTER);
+ /* ICW4: Skipped (could enable auto eoi, or fully nested). */
+
+ /* ICW1: reset, disable ICW4. */
+ pic_write_command(0x10, PIC_SLAVE);
+ /* ICW2: Write base. */
+ pic_write_data(SLAVE_IRQ_BASE, PIC_SLAVE);
+ /* ICW3: Nothing. */
+ pic_write_data(0x0, PIC_SLAVE);
+ /* ICW4: Skipped (could enable auto eoi, or fully nested). */
+}
+
+static void install_autoeoi_handlers(void)
+{
+ handle_irq(MASTER_IRQ_BASE + 0x0, ignore_pit_autoeoi);
+
+ handle_irq(MASTER_IRQ_BASE + 0x1, master_pic_autoeoi_1);
+ handle_irq(MASTER_IRQ_BASE + 0x3, master_pic_autoeoi_3);
+
+ handle_irq(SLAVE_IRQ_BASE + 0x0, slave_pic_autoeoi_0);
+ handle_irq(SLAVE_IRQ_BASE + 0x1, slave_pic_autoeoi_1);
+}
+
+static void reset_pic_autoeoi(void)
+{
+ reset_test();
+ /* ICW1: reset, enable ICW4. */
+ pic_write_command(0x11, PIC_MASTER);
+ /* ICW2: Write base. */
+ pic_write_data(MASTER_IRQ_BASE, PIC_MASTER);
+ /* ICW3: Nothing. */
+ pic_write_data(0x0, PIC_MASTER);
+ /* ICW4: Enable autoeoi. */
+ pic_write_data(0x2, PIC_MASTER);
+
+ /* ICW1: reset, enable ICW4. */
+ pic_write_command(0x11, PIC_SLAVE);
+ /* ICW2: Write base. */
+ pic_write_data(SLAVE_IRQ_BASE, PIC_SLAVE);
+ /* ICW3: Nothing. */
+ pic_write_data(0x0, PIC_SLAVE);
+ /* ICW4: Enable autoeoi. */
+ pic_write_data(0x2, PIC_SLAVE);
+}
+
+static void test_pic_mask(void)
+{
+ unsigned char slave, master;
+ unsigned char slave_initial = 0xef;
+ unsigned char master_initial = 0xfe;
+
+ set_pic_mask(master_initial, slave_initial);
+ master = pic_read_data(PIC_MASTER);
+ slave = pic_read_data(PIC_SLAVE);
+ report("PIC mask test",
+ master == master_initial && slave == slave_initial);
+}
+
+static void test_pic_intr(void)
+{
+ set_pic_mask(0xfd, 0xff);
+ set_irq_line(0x1, 0x1);
+ asm volatile ("nop");
+ report("Master Interrupt", g_master_pic_1 == 1);
+}
+
+static void test_pic_masked_intr(void)
+{
+ set_pic_mask(0xff, 0xff);
+ set_irq_line(0x1, 0x1);
+ asm volatile ("nop");
+ report("Master Interrupt Masked", g_master_pic_1 == 0);
+ set_irq_line(0x1, 0x0);
+}
+
+static void test_pic_unmasked_intr(void)
+{
+ set_pic_mask(0x01, 0x00);
+ set_irq_line(0x1, 0x1);
+ asm volatile ("nop");
+ report("Unmasked Master Interrupt", g_master_pic_1 == 1);
+}
+
+static void test_pic_slave_intr(void)
+{
+ set_pic_mask(0xfb, 0xfe);
+ set_irq_line(0x8, 0x1);
+ asm volatile ("nop");
+ report("Slave interrupt", g_slave_pic_0 == 1);
+}
+
+static void test_pic_slave_masked_intr(void)
+{
+ set_pic_mask(0xff, 0xff);
+ set_irq_line(0x8, 0x1);
+ asm volatile ("nop");
+ report("Slave Interrupt Masked", g_slave_pic_0 == 0);
+ set_irq_line(0x8, 0x0);
+}
+
+static void test_pic_simul_intr(void)
+{
+ set_pic_mask(0xf5, 0xff);
+ set_irq_line(0x1, 0x1);
+ set_irq_line(0x3, 0x1);
+ asm volatile ("nop");
+ report("Simul Master interrupts",
+ g_master_pic_1 == 1 && g_master_pic_3 == 1);
+}
+
+static void test_pic_unmask_simul_intr(void)
+{
+ mask_pic_interrupts();
+
+ set_irq_line(0x1, 0x1);
+ set_irq_line(0x3, 0x1);
+ set_pic_mask(0xf5, 0xff);
+ asm volatile ("nop");
+ report("Unmask two interrupts",
+ g_master_pic_1 == 1 && g_master_pic_3 == 1);
+}
+
+static void test_pic_slave_simul_intr(void)
+{
+ set_pic_mask(0xfb, 0xfc);
+ set_irq_line(0x8, 0x1);
+ set_irq_line(0x9, 0x1);
+ asm volatile ("nop");
+ report("Simul Slave interrupts",
+ g_slave_pic_0 == 1 && g_slave_pic_1 == 1);
+}
+
+static void test_pic_master_slave_simul_intr(void)
+{
+ set_pic_mask(0xf9, 0xfe);
+ set_irq_line(0x1, 0x1);
+ set_irq_line(0x8, 0x1);
+ asm volatile ("nop");
+ report("Simulataneous Master/Slave interrupts",
+ g_slave_pic_0 == 1 && g_master_pic_1 == 1);
+}
+
+int main(void)
+{
+ setup_vm();
+ smp_init();
+ setup_idt();
+
+ install_default_handlers();
+
+ reset_pic_default();
+
+ test_pic_mask();
+
+ unmask_lvt0();
+ irq_enable();
+
+ /* Interrupt tests with default mode. */
+ reset_pic_default();
+ test_pic_intr();
+
+ reset_pic_default();
+ test_pic_masked_intr();
+
+ reset_pic_default();
+ test_pic_slave_masked_intr();
+
+ reset_pic_default();
+ test_pic_simul_intr();
+
+ reset_pic_default();
+ test_pic_unmask_simul_intr();
+
+ reset_pic_default();
+ test_pic_slave_intr();
+
+ reset_pic_default();
+ test_pic_slave_simul_intr();
+
+ reset_pic_default();
+ test_pic_master_slave_simul_intr();
+
+ reset_pic_default();
+ test_pic_unmasked_intr();
+
+ /* Interrupt tests with autoeoi mode. */
+ install_autoeoi_handlers();
+
+ reset_pic_autoeoi();
+ test_pic_intr();
+
+ reset_pic_autoeoi();
+ test_pic_masked_intr();
+
+ reset_pic_autoeoi();
+ test_pic_slave_masked_intr();
+
+ reset_pic_autoeoi();
+ test_pic_simul_intr();
+
+ reset_pic_autoeoi();
+ test_pic_unmask_simul_intr();
+
+ reset_pic_autoeoi();
+ test_pic_slave_intr();
+
+ reset_pic_autoeoi();
+ test_pic_slave_simul_intr();
+
+ reset_pic_autoeoi();
+ test_pic_master_slave_simul_intr();
+
+ reset_pic_autoeoi();
+ test_pic_unmasked_intr();
+
+ return report_summary();
+}
Tests basic interrupt functionality of the PIC. Relies on the QEMU test device to raise and lower irq lines leading into the PIC. Signed-off-by: Steve Rutherford <srutherford@google.com> --- config/config-x86-common.mak | 3 + config/config-x86_64.mak | 2 +- lib/x86/apic.c | 20 ++- lib/x86/apic.h | 3 + lib/x86/pic.c | 80 +++++++++++ lib/x86/pic.h | 20 +++ x86/ioapic.c | 11 -- x86/pic.c | 330 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 452 insertions(+), 17 deletions(-) create mode 100644 lib/x86/pic.c create mode 100644 lib/x86/pic.h create mode 100644 x86/pic.c