>
> +void lapic_update_timer(void)
> +{
> +    /* Timer decrements until zero and then calls this on every interrupt
> */
> +    lapic_timer_val += calibrated_ticks;
> +}
> +
> +void
> +lapic_enable_timer(void)
> +{
> +    spl_t s;
> +
> +    s = sploff();
> +    asm("cli");
> +
> +    /* Set up counter */
> +    lapic->init_count.r = calibrated_ticks;
> +    lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16;
> +
> +    /* Set the timer to interrupt periodically */
> +    lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC;
> +
> +    /* Some buggy hardware requires this set again */
> +    lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16;
> +
> +    /* Unmask the timer irq */
> +    ioapic_toggle(0, IOAPIC_MASK_ENABLED);
> +
> +    splon(s);
> +    printf("LAPIC timer configured\n");
> +}


A simple question: why is the lapic timer implemented in the ioapic.c file?
Couldn't be better to move it to a new lapic file?

Excuse me the inconvenience.

El vie, 26 mar 2021 a las 10:50, Damien Zammit (<[email protected]>)
escribió:

> Defaults to --enable-apic
>
> To build with SMP and APIC support, use for example:
>
> --enable-ncpus=4
>
> Otherwise to build with PIC only and no SMP:
>
> --enable-apic=no
> ---
>  i386/Makefrag.am                 |  13 +-
>  i386/configfrag.ac               |  12 ++
>  i386/i386/apic.c                 |  34 ++-
>  i386/i386/apic.h                 |  74 ++++++-
>  i386/i386/fpu.c                  |   2 +-
>  i386/i386/irq.c                  |   4 +
>  i386/i386/irq.h                  |   6 +-
>  i386/i386/locore.S               |  13 ++
>  i386/i386/pic.h                  |   4 +-
>  i386/i386/pit.c                  |  38 +++-
>  i386/i386/pit.h                  |   2 +
>  i386/i386at/acpi_parse_apic.c    |  19 +-
>  i386/i386at/acpi_parse_apic.h    |   2 +-
>  i386/i386at/autoconf.c           |   2 +-
>  i386/i386at/idt.h                |  17 +-
>  i386/i386at/int_init.c           |   8 +-
>  i386/i386at/interrupt.S          |  16 +-
>  i386/i386at/ioapic.c             | 345 +++++++++++++++++++++++++++++++
>  i386/i386at/kd_mouse.c           |   2 +-
>  i386/i386at/model_dep.c          |  21 +-
>  linux/dev/arch/i386/kernel/irq.c |  26 +--
>  21 files changed, 594 insertions(+), 66 deletions(-)
>  create mode 100644 i386/i386at/ioapic.c
>
> diff --git a/i386/Makefrag.am b/i386/Makefrag.am
> index 73df45f4..de3cddc8 100644
> --- a/i386/Makefrag.am
> +++ b/i386/Makefrag.am
> @@ -57,7 +57,6 @@ libkernel_a_SOURCES += \
>         i386/i386at/kdsoft.h \
>         i386/i386at/mem.c \
>         i386/i386at/mem.h \
> -       i386/i386at/pic_isa.c \
>         i386/i386at/rtc.c \
>         i386/i386at/rtc.h
>  endif
> @@ -155,6 +154,16 @@ EXTRA_DIST += \
>         i386/i386/mach_i386.srv
>
>  if PLATFORM_at
> +if enable_apic
> +libkernel_a_SOURCES += \
> +       i386/i386at/ioapic.c
> +else
> +libkernel_a_SOURCES += \
> +       i386/i386/pic.c \
> +       i386/i386/pic.h \
> +       i386/i386at/pic_isa.c
> +endif
> +
>  libkernel_a_SOURCES += \
>         i386/i386/apic.h \
>         i386/i386/apic.c \
> @@ -163,8 +172,6 @@ libkernel_a_SOURCES += \
>         i386/i386/io_map.c \
>         i386/i386/irq.c \
>         i386/i386/irq.h \
> -       i386/i386/pic.c \
> -       i386/i386/pic.h \
>         i386/i386/pit.c \
>         i386/i386/pit.h
>  endif
> diff --git a/i386/configfrag.ac b/i386/configfrag.ac
> index bf4af110..9a39ccbb 100644
> --- a/i386/configfrag.ac
> +++ b/i386/configfrag.ac
> @@ -92,6 +92,18 @@ if [ x"$enable_lpr" = xyes ]; then]
>    AM_CONDITIONAL([enable_lpr], [false])
>  [fi]
>
> +AC_ARG_ENABLE([apic],
> +  AS_HELP_STRING([--enable-apic], [LAPIC/IOAPIC support (ix86-only);
> enabled by default]))
> +[case $host_platform:$host_cpu in
> +  *:i?86)
> +    enable_apic=${enable_apic-yes};;
> +esac
> +if [ x"$enable_apic" = xyes ]; then]
> +  AC_DEFINE([APIC], [1], [APIC support])
> +  AM_CONDITIONAL([enable_apic], [true])
> +[else]
> +  AM_CONDITIONAL([enable_apic], [false])
> +[fi]
>
>  [case $host_platform:$host_cpu in
>    xen:i?86)
> diff --git a/i386/i386/apic.c b/i386/i386/apic.c
> index f0b4a153..099f80a7 100644
> --- a/i386/i386/apic.c
> +++ b/i386/i386/apic.c
> @@ -95,6 +95,19 @@ apic_add_irq_override(IrqOverrideData irq_over)
>      apic_data.nirqoverride++;
>  }
>
> +IrqOverrideData *
> +acpi_get_irq_override(uint8_t gsi)
> +{
> +    int i;
> +
> +    for (i = 0; i < apic_data.nirqoverride; i++) {
> +        if (apic_data.irq_override_list[i].gsi == gsi) {
> +            return &apic_data.irq_override_list[i];
> +        }
> +    }
> +    return NULL;
> +}
> +
>  /*
>   * apic_get_cpu_apic_id: returns the apic_id of a cpu.
>   * Receives as input the kernel ID of a CPU.
> @@ -118,16 +131,14 @@ apic_get_lapic(void)
>  /*
>   * apic_get_ioapic: returns the IOAPIC identified by its kernel ID.
>   * Receives as input the IOAPIC's Kernel ID.
> - * Returns a ioapic_data structure with the IOAPIC's data.
> + * Returns a ioapic_data structure pointer with the IOAPIC's data.
>   */
> -struct IoApicData
> +struct IoApicData *
>  apic_get_ioapic(int kernel_id)
>  {
> -    IoApicData io_apic = {};
> -
>      if (kernel_id < MAX_IOAPICS)
> -        return apic_data.ioapic_list[kernel_id];
> -    return io_apic;
> +        return &apic_data.ioapic_list[kernel_id];
> +    return (struct IoApicData *)0;
>  }
>
>  /* apic_get_numcpus: returns the current number of cpus. */
> @@ -204,18 +215,21 @@ void apic_print_info(void)
>      uint16_t lapic_id;
>      uint16_t ioapic_id;
>
> -    IoApicData ioapic;
> +    IoApicData *ioapic;
>
>      printf("CPUS:\n");
>      for (i = 0; i < ncpus; i++) {
>          lapic_id = apic_get_cpu_apic_id(i);
> -        printf(" CPU %d - APIC ID %x\n", i, lapic_id);
> +        printf(" CPU %d - APIC ID %x - addr=0x%p\n", i, lapic_id,
> apic_get_lapic());
>      }
>
>      printf("IOAPICS:\n");
>      for (i = 0; i < nioapics; i++) {
>          ioapic = apic_get_ioapic(i);
> -        ioapic_id = ioapic.apic_id;
> -        printf(" IOAPIC %d - APIC ID %x\n", i, ioapic_id);
> +        if (!ioapic) {
> +            printf("ERROR: invalid IOAPIC ID %x\n", i);
> +        }
> +        ioapic_id = ioapic->apic_id;
> +        printf(" IOAPIC %d - APIC ID %x - addr=0x%p\n", i, ioapic_id,
> ioapic->ioapic);
>      }
>  }
> diff --git a/i386/i386/apic.h b/i386/i386/apic.h
> index e2d2c508..f62f2ef9 100644
> --- a/i386/i386/apic.h
> +++ b/i386/i386/apic.h
> @@ -28,8 +28,8 @@
>  #include <stdint.h>
>
>  typedef struct ApicReg {
> -        unsigned r;    /* the actual register */
> -        unsigned p[3]; /* pad to the next 128-bit boundary */
> +        uint32_t r;    /* the actual register */
> +        uint32_t p[3]; /* pad to the next 128-bit boundary */
>  } ApicReg;
>
>  typedef struct ApicIoUnit {
> @@ -37,6 +37,27 @@ typedef struct ApicIoUnit {
>          ApicReg window;
>  } ApicIoUnit;
>
> +struct ioapic_route_entry {
> +    uint32_t vector      : 8,
> +            delvmode    : 3, /* 000=fixed 001=lowest 111=ExtInt */
> +            destmode    : 1, /* 0=physical 1=logical */
> +            delvstatus  : 1,
> +            polarity    : 1, /* 0=activehigh 1=activelow */
> +            irr         : 1,
> +            trigger     : 1, /* 0=edge 1=level */
> +            mask        : 1, /* 0=enabled 1=disabled */
> +            reserved1   : 15;
> +    uint32_t reserved2   : 24,
> +            dest        : 8;
> +} __attribute__ ((packed));
> +
> +union ioapic_route_entry_union {
> +    struct {
> +       uint32_t lo;
> +       uint32_t hi;
> +    };
> +    struct ioapic_route_entry both;
> +};
>
>  typedef struct ApicLocalUnit {
>          ApicReg reserved0;               /* 0x000 */
> @@ -82,11 +103,14 @@ typedef struct ApicLocalUnit {
>  typedef struct IoApicData {
>          uint8_t  apic_id;
>          uint32_t addr;
> -        uint32_t base;
> +        uint32_t gsi_base;
> +        ApicIoUnit *ioapic;
>  } IoApicData;
>
>  #define APIC_IRQ_OVERRIDE_ACTIVE_LOW 2
> +#define APIC_IRQ_OVERRIDE_POLARITY_MASK 1
>  #define APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED 8
> +#define APIC_IRQ_OVERRIDE_TRIGGER_MASK 4
>
>  typedef struct IrqOverrideData {
>          uint8_t  bus;
> @@ -112,14 +136,27 @@ void apic_add_cpu(uint16_t apic_id);
>  void apic_lapic_init(ApicLocalUnit* lapic_ptr);
>  void apic_add_ioapic(struct IoApicData);
>  void apic_add_irq_override(struct IrqOverrideData irq_over);
> +IrqOverrideData *acpi_get_irq_override(uint8_t gsi);
>  uint16_t apic_get_cpu_apic_id(int kernel_id);
>  volatile ApicLocalUnit* apic_get_lapic(void);
> -struct IoApicData apic_get_ioapic(int kernel_id);
> +struct IoApicData *apic_get_ioapic(int kernel_id);
>  uint8_t apic_get_numcpus(void);
>  uint8_t apic_get_num_ioapics(void);
>  uint16_t apic_get_current_cpu(void);
>  void apic_print_info(void);
>  int apic_refit_cpulist(void);
> +void picdisable(void);
> +void lapic_eoi(void);
> +void lapic_enable_timer(void);
> +void ioapic_mask_irqs(void);
> +void ioapic_toggle(int pin, int mask);
> +void ioapic_configure(void);
> +
> +extern int curr_pic_mask;
> +extern void intnull(int unit);
> +extern volatile ApicLocalUnit* lapic;
> +extern inline void mask_irq (unsigned int irq_nr);
> +extern inline void unmask_irq (unsigned int irq_nr);
>
>  #endif
>
> @@ -128,6 +165,35 @@ int apic_refit_cpulist(void);
>  #define APIC_IO_REDIR_LOW(int_pin)     (0x10+(int_pin)*2)
>  #define APIC_IO_REDIR_HIGH(int_pin)    (0x11+(int_pin)*2)
>
> +#define IMCR_SELECT    0x22
> +#define IMCR_DATA      0x23
> +#define MODE_IMCR      0x70
> +# define IMCR_USE_PIC  0
> +# define IMCR_USE_APIC 1
> +
> +#define LAPIC_ENABLE                   0x100
> +#define LAPIC_NMI                      0x400
> +#define LAPIC_DISABLE                  0x10000
> +#define LAPIC_TIMER_PERIODIC           0x20000
> +#define LAPIC_TIMER_DIVIDE_2           0
> +#define LAPIC_TIMER_DIVIDE_4           1
> +#define LAPIC_TIMER_DIVIDE_8           2
> +#define LAPIC_TIMER_DIVIDE_16          3
> +#define LAPIC_TIMER_BASEDIV            0x100000
> +
> +#define NINTR                          24
> +#define IOAPIC_FIXED                   0
> +#define IOAPIC_PHYSICAL                0
> +#define IOAPIC_LOGICAL                 1
> +#define IOAPIC_NMI                     4
> +#define IOAPIC_EXTINT                  7
> +#define IOAPIC_ACTIVE_HIGH             0
> +#define IOAPIC_ACTIVE_LOW              1
> +#define IOAPIC_EDGE_TRIGGERED          0
> +#define IOAPIC_LEVEL_TRIGGERED         1
> +#define IOAPIC_MASK_ENABLED            0
> +#define IOAPIC_MASK_DISABLED           1
> +
>  /* Set or clear a bit in a 255-bit APIC mask register.
>     These registers are spread through eight 32-bit registers.  */
>  #define APIC_SET_MASK_BIT(reg, bit) \
> diff --git a/i386/i386/fpu.c b/i386/i386/fpu.c
> index a8459d65..cdfe264b 100644
> --- a/i386/i386/fpu.c
> +++ b/i386/i386/fpu.c
> @@ -51,7 +51,7 @@
>  #include <i386/thread.h>
>  #include <i386/fpu.h>
>  #include <i386/pio.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
>  #include <i386/locore.h>
>  #include <i386/trap.h>
>  #include "cpu_number.h"
> diff --git a/i386/i386/irq.c b/i386/i386/irq.c
> index 35681191..8f576982 100644
> --- a/i386/i386/irq.c
> +++ b/i386/i386/irq.c
> @@ -62,6 +62,10 @@ __enable_irq (irq_t irq_nr)
>
>  struct irqdev irqtab = {
>    "irq", irq_eoi, &main_intr_queue, 0,
> +#ifdef APIC
> +  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
> 20, 21, 22, 23},
> +#else
>    {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
> +#endif
>  };
>
> diff --git a/i386/i386/irq.h b/i386/i386/irq.h
> index d48a8e92..72bbe57b 100644
> --- a/i386/i386/irq.h
> +++ b/i386/i386/irq.h
> @@ -15,7 +15,11 @@
>  #ifndef _I386_IRQ_H
>  #define _I386_IRQ_H
>
> -#include <i386/pic.h>
> +#ifdef APIC
> +# include <i386/apic.h>
> +#else
> +# include <i386/pic.h>
> +#endif
>
>  typedef unsigned int irq_t;
>
> diff --git a/i386/i386/locore.S b/i386/i386/locore.S
> index bee3630c..4b8c8319 100644
> --- a/i386/i386/locore.S
> +++ b/i386/i386/locore.S
> @@ -607,6 +607,7 @@ ENTRY(call_continuation)
>         jmp     *%eax                           /* goto continuation */
>
>
> +/* IOAPIC has 24 interrupts, put spurious in the same array */
>
>  #define INTERRUPT(n)                           \
>         .data   2                               ;\
> @@ -621,6 +622,7 @@ ENTRY(call_continuation)
>         .data   2
>  DATA(int_entry_table)
>         .text
> +/* Legacy emulated interrupts */
>  INTERRUPT(0)
>  INTERRUPT(1)
>  INTERRUPT(2)
> @@ -637,6 +639,17 @@ INTERRUPT(12)
>  INTERRUPT(13)
>  INTERRUPT(14)
>  INTERRUPT(15)
> +/* PCI interrupts PIRQ A-H */
> +INTERRUPT(16)
> +INTERRUPT(17)
> +INTERRUPT(18)
> +INTERRUPT(19)
> +INTERRUPT(20)
> +INTERRUPT(21)
> +INTERRUPT(22)
> +INTERRUPT(23)
> +/* Spurious interrupt hack, set irq number to vect number */
> +INTERRUPT(255)
>
>  /* XXX handle NMI - at least print a warning like Linux does.  */
>
> diff --git a/i386/i386/pic.h b/i386/i386/pic.h
> index 6434bf08..0ccf1c9a 100644
> --- a/i386/i386/pic.h
> +++ b/i386/i386/pic.h
> @@ -96,7 +96,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  */
>
>  #if    defined(AT386) || defined(ATX86_64)
> -#define        PICM_VECTBASE           0x40
> +#define        PICM_VECTBASE           0x20
>  #define PICS_VECTBASE          PICM_VECTBASE + 0x08
>  #endif /* defined(AT386) */
>
> @@ -176,6 +176,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  #define READ_IR_ONRD           0x00
>  #define READ_IS_ONRD           0x01
>
> +#define PIC_MASK_ZERO          0x00
> +
>  #ifndef __ASSEMBLER__
>  extern void picinit (void);
>  extern int curr_pic_mask;
> diff --git a/i386/i386/pit.c b/i386/i386/pit.c
> index 4e3feeec..125036f7 100644
> --- a/i386/i386/pit.c
> +++ b/i386/i386/pit.c
> @@ -51,7 +51,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>
>  #include <kern/mach_clock.h>
>  #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#include <machine/irq.h>
>  #include <i386/pit.h>
>  #include <i386/pio.h>
>  #include <kern/cpu_number.h>
> @@ -66,14 +66,44 @@ int pit0_mode = PIT_C0|PIT_SQUAREMODE|PIT_READMODE ;
>  unsigned int clknumb = CLKNUM;         /* interrupt interval for timer 0
> */
>
>  void
> -clkstart(void)
> +pit_prepare_sleep(int hz)
>  {
> -       unsigned char   byte;
> -       unsigned long s;
> +    /* Prepare to sleep for 1/hz seconds */
> +    int val = 0;
> +    int lsb, msb;
> +
> +    val = (inb(0x61) & 0xfd) | 0x1;
> +    outb(0x61, val);
> +    outb(0x43, 0xb2);
> +    val = CLKNUM / hz;
> +    lsb = val & 0xff;
> +    msb = val >> 8;
> +    outb(0x42, lsb);
> +    val = inb(0x60);
> +    outb(0x42, msb);
>
> +    /* Start counting down */
> +    val = inb(0x61) & 0xfe;
> +    outb(0x61, val); /* Gate low */
> +    val |= 0x1;
> +    outb(0x61, val); /* Gate high */
> +}
> +
> +void
> +pit_sleep(void)
> +{
> +    /* Wait until counter reaches zero */
> +    while ((inb(0x61) & 0x20) == 0);
> +}
> +
> +void
> +clkstart(void)
> +{
>         if (cpu_number() != 0)
>                 /* Only one PIT initialization is needed */
>                 return;
> +       unsigned long s;
> +       unsigned char   byte;
>
>         s = sploff();         /* disable interrupts */
>
> diff --git a/i386/i386/pit.h b/i386/i386/pit.h
> index e004c37c..6be7a9d4 100644
> --- a/i386/i386/pit.h
> +++ b/i386/i386/pit.h
> @@ -82,5 +82,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  #endif /* AT386 */
>
>  extern void clkstart(void);
> +extern void pit_prepare_sleep(int hz);
> +extern void pit_sleep(void);
>
>  #endif /* _I386_PIT_H_ */
> diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c
> index 9dbc6825..449dba71 100644
> --- a/i386/i386at/acpi_parse_apic.c
> +++ b/i386/i386at/acpi_parse_apic.c
> @@ -280,7 +280,7 @@ acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n)
>      /* Search APIC entries in rsdt table. */
>      for (int i = 0; i < acpi_rsdt_n; i++) {
>          descr_header = (struct acpi_dhdr*)
> kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr),
> -
> VM_PROT_READ | VM_PROT_WRITE);
> +
> VM_PROT_READ);
>
>          /* Check if the entry contains an APIC. */
>          check_signature = acpi_check_signature(descr_header->signature,
> ACPI_APIC_SIG, 4*sizeof(uint8_t));
> @@ -326,8 +326,10 @@ acpi_apic_add_ioapic(struct acpi_apic_ioapic
> *ioapic_entry)
>      /* Fill IOAPIC structure with its main fields */
>      io_apic.apic_id = ioapic_entry->apic_id;
>      io_apic.addr = ioapic_entry->addr;
> -    io_apic.base = ioapic_entry->base;
> -
> +    io_apic.gsi_base = ioapic_entry->gsi_base;
> +    io_apic.ioapic = (ApicIoUnit
> *)kmem_map_aligned_table(ioapic_entry->addr,
> +
> sizeof(ApicIoUnit),
> +                                                          VM_PROT_READ |
> VM_PROT_WRITE);
>      /* Insert IOAPIC in the list. */
>      apic_add_ioapic(io_apic);
>  }
> @@ -414,6 +416,8 @@ acpi_apic_parse_table(struct acpi_apic *apic)
>              acpi_apic_add_irq_override(irq_override_entry);
>              break;
>
> +        default:
> +            break;
>          }
>
>          /* Get next APIC entry. */
> @@ -446,7 +450,7 @@ static int
>  acpi_apic_setup(struct acpi_apic *apic)
>  {
>      int apic_checksum;
> -    ApicLocalUnit* lapic;
> +    ApicLocalUnit* lapic_unit;
>      uint8_t ncpus, nioapics;
>
>      /* Check the checksum of the APIC */
> @@ -456,12 +460,13 @@ acpi_apic_setup(struct acpi_apic *apic)
>          return ACPI_BAD_CHECKSUM;
>
>      /* map common lapic address */
> -    lapic = kmem_map_aligned_table(apic->lapic_addr,
> sizeof(ApicLocalUnit), VM_PROT_READ);
> +    lapic_unit = kmem_map_aligned_table(apic->lapic_addr,
> sizeof(ApicLocalUnit),
> +                                        VM_PROT_READ | VM_PROT_WRITE);
>
> -    if (lapic == NULL)
> +    if (lapic_unit == NULL)
>          return ACPI_NO_LAPIC;
>
> -    apic_lapic_init(lapic);
> +    apic_lapic_init(lapic_unit);
>      acpi_apic_parse_table(apic);
>
>      ncpus = apic_get_numcpus();
> diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h
> index d071da4f..97a59a2e 100644
> --- a/i386/i386at/acpi_parse_apic.h
> +++ b/i386/i386at/acpi_parse_apic.h
> @@ -139,7 +139,7 @@ struct acpi_apic_ioapic {
>      uint8_t     apic_id;
>      uint8_t     reserved;
>      uint32_t    addr;
> -    uint32_t    base;
> +    uint32_t    gsi_base;
>  } __attribute__((__packed__));
>
>  /*
> diff --git a/i386/i386at/autoconf.c b/i386/i386at/autoconf.c
> index 151e3fd2..0b1251f5 100644
> --- a/i386/i386at/autoconf.c
> +++ b/i386/i386at/autoconf.c
> @@ -26,7 +26,7 @@
>
>  #include <kern/printf.h>
>  #include <mach/std_types.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
>  #include <i386/ipl.h>
>  #include <chips/busses.h>
>
> diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h
> index 56e6296c..637f8b44 100644
> --- a/i386/i386at/idt.h
> +++ b/i386/i386at/idt.h
> @@ -24,13 +24,18 @@
>  #ifndef _I386AT_IDT_
>  #define _I386AT_IDT_
>
> -/* On a standard PC, we only need 16 interrupt vectors,
> -   because that's all the PIC hardware supports.  */
> -/* XX But for some reason we program the PIC
> -   to use vectors 0x40-0x4f rather than 0x20-0x2f.  Fix.  */
> -#define IDTSZ (0x20+0x20+0x10)
> +/* There are 256 interrupt vectors on x86,
> + * the first 32 are taken by cpu faults */
> +#define IDTSZ (0x100)
>
> -#define PIC_INT_BASE 0x40
> +/* PIC now sits (unused) at 0x20-0x2f */
> +#define PIC_INT_BASE 0x20
> +
> +/* IOAPIC sits at 0x30-0x47 */
> +#define IOAPIC_INT_BASE 0x30
> +
> +/* IOAPIC spurious interrupt vector set to 0xff */
> +#define IOAPIC_SPURIOUS_BASE 0xff
>
>  #include <i386/idt-gen.h>
>
> diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c
> index 43daad8b..3c96c589 100644
> --- a/i386/i386at/int_init.c
> +++ b/i386/i386at/int_init.c
> @@ -31,9 +31,13 @@ void int_init(void)
>  {
>         int i;
>
> -       for (i = 0; i < 16; i++)
> -               fill_idt_gate(PIC_INT_BASE + i,
> +       for (i = 0; i < 24; i++)
> +               fill_idt_gate(IOAPIC_INT_BASE + i,
>                               int_entry_table[i], KERNEL_CS,
>                               ACC_PL_K|ACC_INTR_GATE, 0);
> +
> +       fill_idt_gate(IOAPIC_SPURIOUS_BASE,
> +                             int_entry_table[24], KERNEL_CS,
> +                             ACC_PL_K|ACC_INTR_GATE, 0);
>  }
>
> diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S
> index 23a2e582..50e0391d 100644
> --- a/i386/i386at/interrupt.S
> +++ b/i386/i386at/interrupt.S
> @@ -16,7 +16,11 @@
>  #include <mach/machine/asm.h>
>
>  #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#ifdef APIC
> +# include <i386/apic.h>
> +#else
> +# include <i386/pic.h>
> +#endif
>  #include <i386/i386asm.h>
>
>  #define READ_ISR       (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD)
> @@ -29,6 +33,8 @@
>  ENTRY(interrupt)
>         pushl   %eax                    /* save irq number */
>         movl    %eax,%ecx               /* copy irq number */
> +       cmpl    $255,%eax               /* was this a spurious intr? */
> +       je      2f                      /* if so, null handler */
>         shll    $2,%ecx                 /* irq * 4 */
>         call    spl7                    /* set ipl */
>         movl    EXT(iunit)(%ecx),%edx   /* get device unit number */
> @@ -38,10 +44,9 @@ ENTRY(interrupt)
>         addl    $4,%esp                 /* pop unit number */
>         call    splx_cli                /* restore previous ipl */
>         addl    $4,%esp                 /* pop previous ipl */
> -
>         cli                             /* XXX no more nested interrupts */
>         popl    %ecx                    /* restore irq number */
> -
> +#ifndef APIC
>         movl    $1,%eax
>         shll    %cl,%eax                /* get corresponding IRQ mask */
>         orl     EXT(curr_pic_mask),%eax /* add current mask */
> @@ -78,4 +83,9 @@ ENTRY(interrupt)
>         outb    %al,$(PIC_MASTER_OCW)   /* unmask master */
>  2:
>         ret
> +#else
> +2:
> +       call    EXT(lapic_eoi)          /* EOI */
> +       ret                             /* return */
> +#endif
>  END(interrupt)
> diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c
> new file mode 100644
> index 00000000..b13eb1b3
> --- /dev/null
> +++ b/i386/i386at/ioapic.c
> @@ -0,0 +1,345 @@
> +/*
> + * Copyright (C) 2019 Free Software Foundation, Inc.
> + *
> + * This file is part of GNU Mach.
> + *
> + * GNU Mach is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2, or (at your option)
> + * any later version.
> + *
> + * GNU Mach is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
> + */
> +
> +#include <sys/types.h>
> +#include <i386/ipl.h>
> +#include <machine/irq.h>
> +#include <i386/fpu.h>
> +#include <i386/hardclock.h>
> +#include <i386at/kd.h>
> +#include <i386at/idt.h>
> +#include <i386/pio.h>
> +#include <i386/pit.h>
> +#include <mach/machine.h>
> +#include <kern/printf.h>
> +
> +uint32_t lapic_timer_val = 0;
> +uint32_t calibrated_ticks = 0;
> +
> +spl_t  curr_ipl;
> +int    curr_pic_mask;
> +
> +int    iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
> 15,
> +                       16, 17, 18, 19, 20, 21, 22, 23};
> +
> +void (*ivect[NINTR])() = {
> +       /* 00 */        hardclock,      /* always */
> +       /* 01 */        kdintr,         /* kdintr, ... */
> +       /* 02 */        intnull,
> +       /* 03 */        intnull,        /* lnpoll, comintr, ... */
> +
> +       /* 04 */        intnull,        /* comintr, ... */
> +       /* 05 */        intnull,        /* comintr, wtintr, ... */
> +       /* 06 */        intnull,        /* fdintr, ... */
> +       /* 07 */        intnull,        /* qdintr, ... */
> +
> +       /* 08 */        intnull,
> +       /* 09 */        intnull,        /* ether */
> +       /* 10 */        intnull,
> +       /* 11 */        intnull,
> +
> +       /* 12 */        intnull,
> +       /* 13 */        fpintr,         /* always */
> +       /* 14 */        intnull,        /* hdintr, ... */
> +       /* 15 */        intnull,        /* ??? */
> +
> +       /* 16 */        intnull,        /* PIRQA */
> +       /* 17 */        intnull,        /* PIRQB */
> +       /* 18 */        intnull,        /* PIRQC */
> +       /* 19 */        intnull,        /* PIRQD */
> +       /* 20 */        intnull,        /* PIRQE */
> +       /* 21 */        intnull,        /* PIRQF */
> +       /* 22 */        intnull,        /* PIRQG */
> +       /* 23 */        intnull,        /* PIRQH */
> +};
> +
> +void
> +picdisable(void)
> +{
> +       asm("cli");
> +
> +       /*
> +       ** Disable PIC
> +       */
> +       outb ( 0xa1, 0xff );
> +       outb ( 0x21, 0xff );
> +
> +       /*
> +       ** Route interrupts through IOAPIC
> +       */
> +       outb(IMCR_SELECT, MODE_IMCR);
> +       outb(IMCR_DATA, IMCR_USE_APIC);
> +}
> +
> +void
> +intnull(int unit_dev)
> +{
> +    printf("intnull(%d)\n", unit_dev);
> +}
> +
> +static uint32_t
> +ioapic_read(uint8_t id, uint8_t reg)
> +{
> +    volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic;
> +    ioapic->select.r = reg;
> +    return ioapic->window.r;
> +}
> +
> +static void
> +ioapic_write(uint8_t id, uint8_t reg, uint32_t value)
> +{
> +    volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic;
> +    ioapic->select.r = reg;
> +    ioapic->window.r = value;
> +}
> +
> +static struct ioapic_route_entry
> +ioapic_read_entry(int apic, int pin)
> +{
> +    union ioapic_route_entry_union entry;
> +
> +    entry.lo = ioapic_read(apic, APIC_IO_REDIR_LOW(pin));
> +    entry.hi = ioapic_read(apic, APIC_IO_REDIR_HIGH(pin));
> +
> +    return entry.both;
> +}
> +
> +/* Write the high word first because mask bit is in low word */
> +static void
> +ioapic_write_entry(int apic, int pin, struct ioapic_route_entry e)
> +{
> +    union ioapic_route_entry_union entry = {{0, 0}};
> +
> +    entry.both = e;
> +    ioapic_write(apic, APIC_IO_REDIR_HIGH(pin), entry.hi);
> +    ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo);
> +}
> +
> +/* When toggling the interrupt via mask, write low word only */
> +static void
> +ioapic_toggle_entry(int apic, int pin, int mask)
> +{
> +    union ioapic_route_entry_union entry;
> +
> +    entry.both = ioapic_read_entry(apic, pin);
> +    entry.both.mask = mask & 0x1;
> +    ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo);
> +}
> +
> +static void
> +cpu_rdmsr(uint32_t msr, uint32_t *lo, uint32_t *hi)
> +{
> +   __asm__ __volatile__("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr));
> +}
> +
> +static void
> +cpu_wrmsr(uint32_t msr, uint32_t lo, uint32_t hi)
> +{
> +   __asm__ __volatile__("wrmsr" : : "a"(lo), "d"(hi), "c"(msr));
> +}
> +
> +static void
> +global_enable_apic(void)
> +{
> +    uint32_t lo = 0;
> +    uint32_t hi = 0;
> +    uint32_t msr = 0x1b;
> +
> +    cpu_rdmsr(msr, &lo, &hi);
> +
> +    if (!(lo & (1 << 11))) {
> +        lo |= (1 << 11);
> +        cpu_wrmsr(msr, lo, hi);
> +    }
> +}
> +
> +static uint32_t
> +pit_measure_apic_hz(void)
> +{
> +    uint32_t start = 0xffffffff;
> +
> +    /* Prepare accurate delay for 1/100 seconds */
> +    pit_prepare_sleep(100);
> +
> +    /* Set APIC timer */
> +    lapic->init_count.r = start;
> +
> +    /* zZz */
> +    pit_sleep();
> +
> +    /* Stop APIC timer */
> +    lapic->lvt_timer.r = LAPIC_DISABLE;
> +
> +    return start - lapic->cur_count.r;
> +}
> +
> +void lapic_update_timer(void)
> +{
> +    /* Timer decrements until zero and then calls this on every interrupt
> */
> +    lapic_timer_val += calibrated_ticks;
> +}
> +
> +void
> +lapic_enable_timer(void)
> +{
> +    spl_t s;
> +
> +    s = sploff();
> +    asm("cli");
> +
> +    /* Set up counter */
> +    lapic->init_count.r = calibrated_ticks;
> +    lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16;
> +
> +    /* Set the timer to interrupt periodically */
> +    lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC;
> +
> +    /* Some buggy hardware requires this set again */
> +    lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16;
> +
> +    /* Unmask the timer irq */
> +    ioapic_toggle(0, IOAPIC_MASK_ENABLED);
> +
> +    splon(s);
> +    printf("LAPIC timer configured\n");
> +}
> +
> +void
> +ioapic_toggle(int pin, int mask)
> +{
> +    int apic = 0;
> +    ioapic_toggle_entry(apic, pin, mask);
> +}
> +
> +void
> +ioapic_mask_irqs(void)
> +{
> +    int i, bitmask = 0x1;
> +
> +    for (i = 0; i < NINTR; i++, bitmask<<=1) {
> +        if (curr_pic_mask & bitmask) {
> +            ioapic_toggle(i, IOAPIC_MASK_DISABLED);
> +        } else {
> +            ioapic_toggle(i, IOAPIC_MASK_ENABLED);
> +        }
> +    }
> +}
> +
> +void
> +lapic_eoi(void)
> +{
> +    lapic_update_timer();
> +    lapic->eoi.r = 0;
> +}
> +
> +void
> +unmask_irq(unsigned int irq)
> +{
> +    ioapic_toggle(irq, IOAPIC_MASK_ENABLED);
> +}
> +
> +void
> +mask_irq(unsigned int irq)
> +{
> +    ioapic_toggle(irq, IOAPIC_MASK_DISABLED);
> +}
> +
> +static unsigned int
> +override_irq(IrqOverrideData *override, union ioapic_route_entry_union
> *entry)
> +{
> +    if (override->flags & APIC_IRQ_OVERRIDE_POLARITY_MASK) {
> +        entry->both.polarity = (override->flags &
> APIC_IRQ_OVERRIDE_ACTIVE_LOW) ?
> +                               IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH;
> +    }
> +    if (override->flags & APIC_IRQ_OVERRIDE_TRIGGER_MASK) {
> +        entry->both.trigger = (override->flags &
> APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ?
> +                              IOAPIC_LEVEL_TRIGGERED :
> IOAPIC_EDGE_TRIGGERED;
> +    }
> +    return override->gsi;
> +}
> +
> +void
> +ioapic_configure(void)
> +{
> +    /* Assume first IO APIC maps to GSI base 0 */
> +    int gsi, apic = 0, bsp = 0, pin;
> +    IrqOverrideData *irq_over;
> +
> +    /* Disable IOAPIC interrupts and set spurious interrupt */
> +    lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE;
> +
> +    union ioapic_route_entry_union entry = {{0, 0}};
> +
> +    entry.both.delvmode = IOAPIC_FIXED;
> +    entry.both.destmode = IOAPIC_PHYSICAL;
> +    entry.both.mask = IOAPIC_MASK_DISABLED;
> +    entry.both.dest = apic_get_cpu_apic_id(bsp);
> +
> +    /* ISA legacy IRQs */
> +    entry.both.polarity = IOAPIC_ACTIVE_HIGH;
> +    entry.both.trigger = IOAPIC_EDGE_TRIGGERED;
> +
> +    for (pin = 0; pin < 16; pin++) {
> +        gsi = pin;
> +        if ((irq_over = acpi_get_irq_override(pin))) {
> +            gsi = override_irq(irq_over, &entry);
> +        }
> +        entry.both.vector = IOAPIC_INT_BASE + gsi;
> +        ioapic_write_entry(apic, pin, entry.both);
> +    }
> +
> +    /* PCI IRQs PIRQ A-H */
> +    entry.both.polarity = IOAPIC_ACTIVE_LOW;
> +    entry.both.trigger = IOAPIC_LEVEL_TRIGGERED;
> +
> +    for (pin = 16; pin < 24; pin++) {
> +        gsi = pin;
> +        if ((irq_over = acpi_get_irq_override(pin))) {
> +            gsi = override_irq(irq_over, &entry);
> +        }
> +        entry.both.vector = IOAPIC_INT_BASE + gsi;
> +        ioapic_write_entry(apic, pin, entry.both);
> +    }
> +
> +    /* Start the IO APIC receiving interrupts */
> +    lapic->dest_format.r = 0xffffffff; /* flat model */
> +    lapic->logical_dest.r = 0x00000000;        /* default, but we use
> physical */
> +    lapic->lvt_timer.r = LAPIC_DISABLE;
> +    lapic->lvt_performance_monitor.r = LAPIC_NMI;
> +    lapic->lvt_lint0.r = LAPIC_DISABLE;
> +    lapic->lvt_lint1.r = LAPIC_DISABLE;
> +    lapic->task_pri.r = 0;
> +
> +    global_enable_apic();
> +
> +    /* Enable IOAPIC interrupts */
> +    lapic->spurious_vector.r |= LAPIC_ENABLE;
> +
> +    /* Set one-shot timer */
> +    lapic->divider_config.r = LAPIC_TIMER_DIVIDE_16;
> +    lapic->lvt_timer.r = IOAPIC_INT_BASE;
> +
> +    /* Measure number of APIC timer ticks in 10ms */
> +    calibrated_ticks = pit_measure_apic_hz();
> +
> +    /* Set up counter later */
> +    lapic->lvt_timer.r = LAPIC_DISABLE;
> +    printf("IOAPIC 0 configured\n");
> +}
> diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c
> index 2995587c..4b883ba8 100644
> --- a/i386/i386at/kd_mouse.c
> +++ b/i386/i386at/kd_mouse.c
> @@ -72,7 +72,7 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  #include <device/io_req.h>
>  #include <device/subrs.h>
>  #include <i386/ipl.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
>  #include <i386/pio.h>
>  #include <chips/busses.h>
>  #include <i386at/com.h>
> diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
> index 1e98c5c3..17d0638d 100644
> --- a/i386/i386at/model_dep.c
> +++ b/i386/i386at/model_dep.c
> @@ -59,7 +59,6 @@
>  #include <i386/ldt.h>
>  #include <i386/machspl.h>
>  #include <i386/mp_desc.h>
> -#include <i386/pic.h>
>  #include <i386/pit.h>
>  #include <i386/pmap.h>
>  #include <i386/proc_reg.h>
> @@ -75,6 +74,7 @@
>  #include <i386at/kd.h>
>  #include <i386at/rtc.h>
>  #include <i386at/model_dep.h>
> +#include <machine/irq.h>
>
>  #ifdef MACH_XEN
>  #include <xen/console.h>
> @@ -169,17 +169,20 @@ void machine_init(void)
>  #ifdef MACH_HYP
>         hyp_init();
>  #else  /* MACH_HYP */
> +
> +#if (NCPUS > 1) && defined(APIC)
> +       smp_init();
> +       ioapic_configure();
> +       lapic_enable_timer();
> +       unmask_irq(1);
> +#endif /* NCPUS > 1 */
> +
>  #ifdef LINUX_DEV
>         /*
>          * Initialize Linux drivers.
>          */
>         linux_init();
>  #endif
> -
> -#if NCPUS > 1
> -       smp_init();
> -#endif /* NCPUS > 1 */
> -
>         /*
>          * Find the devices
>          */
> @@ -356,7 +359,11 @@ i386at_init(void)
>          * Initialize the PIC prior to any possible call to an spl.
>          */
>  #ifndef        MACH_HYP
> +# ifdef APIC
> +       picdisable();
> +# else
>         picinit();
> +# endif
>  #else  /* MACH_HYP */
>         hyp_intrinit();
>  #endif /* MACH_HYP */
> @@ -682,7 +689,9 @@ timemmap(dev, off, prot)
>  void
>  startrtclock(void)
>  {
> +#ifndef APIC
>         clkstart();
> +#endif
>  }
>
>  void
> diff --git a/linux/dev/arch/i386/kernel/irq.c
> b/linux/dev/arch/i386/kernel/irq.c
> index 06889e58..656c1470 100644
> --- a/linux/dev/arch/i386/kernel/irq.c
> +++ b/linux/dev/arch/i386/kernel/irq.c
> @@ -29,7 +29,7 @@
>  #include <kern/assert.h>
>
>  #include <i386/spl.h>
> -#include <i386/pic.h>
> +#include <i386/irq.h>
>  #include <i386/pit.h>
>
>  #define MACH_INCLUDE
> @@ -84,13 +84,7 @@ struct linux_action
>    user_intr_t *user_intr;
>  };
>
> -static struct linux_action *irq_action[16] =
> -{
> -  NULL, NULL, NULL, NULL,
> -  NULL, NULL, NULL, NULL,
> -  NULL, NULL, NULL, NULL,
> -  NULL, NULL, NULL, NULL
> -};
> +static struct linux_action *irq_action[NINTR] = {0};
>
>  /*
>   * Generic interrupt handler for Linux devices.
> @@ -232,7 +226,7 @@ install_user_intr_handler (struct irqdev *dev, int id,
> unsigned long flags,
>
>    unsigned int irq = dev->irq[id];
>
> -  assert (irq < 16);
> +  assert (irq < NINTR);
>
>    /* Test whether the irq handler has been set */
>    // TODO I need to protect the array when iterating it.
> @@ -279,7 +273,7 @@ request_irq (unsigned int irq, void (*handler) (int,
> void *, struct pt_regs *),
>    struct linux_action *action;
>    int retval;
>
> -  assert (irq < 16);
> +  assert (irq < NINTR);
>
>    if (!handler)
>      return -EINVAL;
> @@ -315,7 +309,7 @@ free_irq (unsigned int irq, void *dev_id)
>    struct linux_action *action, **p;
>    unsigned long flags;
>
> -  if (irq > 15)
> +  if (irq >= NINTR)
>      panic ("free_irq: bad irq number");
>
>    for (p = irq_action + irq; (action = *p) != NULL; p = &action->next)
> @@ -354,7 +348,7 @@ probe_irq_on (void)
>    /*
>     * Allocate all available IRQs.
>     */
> -  for (i = 15; i > 0; i--)
> +  for (i = NINTR - 1; i > 0; i--)
>      {
>        if (!irq_action[i] && ivect[i] == intnull)
>         {
> @@ -387,7 +381,7 @@ probe_irq_off (unsigned long irqs)
>    /*
>     * Disable unnecessary IRQs.
>     */
> -  for (i = 15; i > 0; i--)
> +  for (i = NINTR - 1; i > 0; i--)
>      {
>        if (!irq_action[i] && ivect[i] == intnull)
>         {
> @@ -427,7 +421,7 @@ reserve_mach_irqs (void)
>  {
>    unsigned int i;
>
> -  for (i = 0; i < 16; i++)
> +  for (i = 0; i < NINTR; i++)
>      {
>        if (ivect[i] != intnull)
>         /* This dummy action does not specify SA_SHIRQ, so
> @@ -720,13 +714,15 @@ init_IRQ (void)
>     */
>    (void) splhigh ();
>
> +#ifndef APIC
>    /*
>     * Program counter 0 of 8253 to interrupt hz times per second.
>     */
>    outb_p (PIT_C0 | PIT_SQUAREMODE | PIT_READMODE, PITCTL_PORT);
>    outb_p (latch & 0xff, PITCTR0_PORT);
>    outb (latch >> 8, PITCTR0_PORT);
> -
> +#endif
> +
>    /*
>     * Install our clock interrupt handler.
>     */
> --
> 2.30.1
>
>
>

Reply via email to