From patchwork Wed Sep 2 20:57:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve Rutherford X-Patchwork-Id: 7113451 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B7AA69F1CD for ; Wed, 2 Sep 2015 21:00:15 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 303CF20569 for ; Wed, 2 Sep 2015 21:00:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A0F5F2055C for ; Wed, 2 Sep 2015 21:00:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932386AbbIBVAI (ORCPT ); Wed, 2 Sep 2015 17:00:08 -0400 Received: from mail-pa0-f41.google.com ([209.85.220.41]:36183 "EHLO mail-pa0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932314AbbIBVAF (ORCPT ); Wed, 2 Sep 2015 17:00:05 -0400 Received: by pacwi10 with SMTP id wi10so22641038pac.3 for ; Wed, 02 Sep 2015 14:00:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=cjH8/4/ICeJxBCUNTeoVWmJJ7c+GmsVJUA0B0sESq/E=; b=gtj5P/z41fybl05PSdgRhUb1Pe/oLArneBAnF96o/WapryX5kJERg+autRBQ2jcr09 zztQRND+ofGOdE53S/4X9r1ir9eDyY7oCDZVErTGq8JcW9b/u/DZ89NIDWELUsoTKOAV BiMRZDXLvyphmFtuoZ9M+OeJDJAaT37BL+9dZHRqa1F+ycETVNxvfhX4b+KLNzwiVFKx 4fQm7OdVrD+jpVdl//G9GSxVF7iqnj9u3HODaayd5hUab4db1NSB1lA8vn9xxdXd3+g5 3eDm25Xj6fsgIj5nhxJURYFA+pifIyKBhZR2T/digNJhjADUtUoknvOb75X9vDxX/S/W 2Cdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=cjH8/4/ICeJxBCUNTeoVWmJJ7c+GmsVJUA0B0sESq/E=; b=TYKcmA3Ao0ltEXNjU27ZzAUa7suJZjDIPAcUbBKeLtC+g6cUBeP1voBNqhHkdvV3ns thR7lVqgsU7k73EM4uSvH40NJbjmTHu/G8UwQ3x0TZ3F1bTGH9qpqonu7EedTl2Nic/R nRwxHfwXyqGSDIMlnfbt71NlmzYB7y9Ga2+yep49jAk3R3LtygVV1CGm30q02h/MRqGw L2sSVhKuWUvL7+TmrE2NxWW04R/hJex0Jz/ViAGfN8TI44Ok0HY/zR5dsSOWC5I+1n/M Oo3FHNS0B79PFVfzqJyhEaaHOdPof53fDj5qxEWHn66E1hqCUlwKYXNbPTSCmm8hAUhb lL3w== X-Gm-Message-State: ALoCoQmaRDO1e4jJFJjwUYM4C+w+91KPkdlUWBKkn84b3CrjwrdrwE2wmwDU9UYSXPgxO5nIBYAF X-Received: by 10.68.201.105 with SMTP id jz9mr59486714pbc.86.1441227605348; Wed, 02 Sep 2015 14:00:05 -0700 (PDT) Received: from entropic.kir.corp.google.com ([172.31.8.128]) by smtp.gmail.com with ESMTPSA id qc2sm22649645pbc.79.2015.09.02.14.00.04 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 02 Sep 2015 14:00:04 -0700 (PDT) From: Steve Rutherford To: kvm@vger.kernel.org Cc: gingell@google.com Subject: [PATCH] Add baseline PIC tests. Date: Wed, 2 Sep 2015 13:57:46 -0700 Message-Id: <1441227466-25589-1-git-send-email-srutherford@google.com> X-Mailer: git-send-email 2.5.0.457.gab17608 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,RCVD_IN_DNSWL_HI,T_DKIM_INVALID,T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 diff --git a/config/config-x86-common.mak b/config/config-x86-common.mak index c2f9908..a7aa17a 100644 --- a/config/config-x86-common.mak +++ b/config/config-x86-common.mak @@ -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 diff --git a/config/config-x86_64.mak b/config/config-x86_64.mak index 7d4eb34..0f2c47a 100644 --- a/config/config-x86_64.mak +++ b/config/config-x86_64.mak @@ -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 diff --git a/lib/x86/apic.c b/lib/x86/apic.c index 80b96d8..2e8e466 100644 --- a/lib/x86/apic.c +++ b/lib/x86/apic.c @@ -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); } diff --git a/lib/x86/apic.h b/lib/x86/apic.h index 216b98d..a5b19c0 100644 --- a/lib/x86/apic.h +++ b/lib/x86/apic.h @@ -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 diff --git a/lib/x86/pic.c b/lib/x86/pic.c new file mode 100644 index 0000000..e88d0b1 --- /dev/null +++ b/lib/x86/pic.c @@ -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); +} diff --git a/lib/x86/pic.h b/lib/x86/pic.h new file mode 100644 index 0000000..61e72ff --- /dev/null +++ b/lib/x86/pic.h @@ -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 */ diff --git a/x86/ioapic.c b/x86/ioapic.c index 1fe1ccc..d91aa23 100644 --- a/x86/ioapic.c +++ b/x86/ioapic.c @@ -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; diff --git a/x86/pic.c b/x86/pic.c new file mode 100644 index 0000000..af6e291 --- /dev/null +++ b/x86/pic.c @@ -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(); +}