Ripped out everything except the ability to send EOI to the PIC.
authorMichael Brown <mcb30@etherboot.org>
Thu, 4 Jan 2007 19:36:18 +0000 (19:36 +0000)
committerMichael Brown <mcb30@etherboot.org>
Thu, 4 Jan 2007 19:36:18 +0000 (19:36 +0000)
src/arch/i386/core/pic8259.c
src/arch/i386/include/pic8259.h

index 411e941..aae22ce 100644 (file)
 /*
- * Basic support for controlling the 8259 Programmable Interrupt Controllers.
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
  *
- * Initially written by Michael Brown (mcb30).
- */
-
-#include <etherboot.h>
-#include "pic8259.h"
-#include "old_realmode.h"
-
-/* State of trivial IRQ handler */
-irq_t trivial_irq_installed_on = IRQ_NONE;
-static uint16_t trivial_irq_previous_trigger_count = 0;
-
-/* The actual trivial IRQ handler
+ * This program 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 of the
+ * License, or any later version.
  *
- * Note: we depend on the C compiler not realising that we're putting
- * variables in the ".text16" section and therefore not forcing them
- * back to the ".data" section.  I don't see any reason to expect this
- * behaviour to change.
+ * This program 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.
  *
- * These must *not* be the first variables to appear in this file; the
- * first variable to appear gets the ".data" directive.
- */
-RM_FRAGMENT(_trivial_irq_handler,
-       "pushw %bx\n\t"
-       "call  1f\n1:\tpopw %bx\n\t"   /* PIC access to variables */
-       "incw  %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t" 
-       "popw  %bx\n\t" 
-       "iret\n\t" 
-       "\n\t"
-       ".globl _trivial_irq_trigger_count\n\t"
-       "_trivial_irq_trigger_count: .short 0\n\t"
-       "\n\t"
-       ".globl _trivial_irq_chain_to\n\t"
-       "_trivial_irq_chain_to: .short 0,0\n\t"
-       "\n\t"
-       ".globl _trivial_irq_chain\n\t"
-       "_trivial_irq_chain: .byte 0\n\t"
-       );
-extern volatile uint16_t _trivial_irq_trigger_count;
-extern segoff_t _trivial_irq_chain_to;
-extern int8_t _trivial_irq_chain;
-
-/* Current locations of trivial IRQ handler.  These will change at
- * runtime when relocation is used; the handler needs to be copied to
- * base memory before being installed.
- */
-void (*trivial_irq_handler)P((void)) = _trivial_irq_handler;
-uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count;
-segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to;
-uint8_t *trivial_irq_chain = &_trivial_irq_chain;
-
-/* Install a handler for the specified IRQ.  Address of previous
- * handler will be stored in previous_handler.  Enabled/disabled state
- * of IRQ will be preserved across call, therefore if the handler does
- * chaining, ensure that either (a) IRQ is disabled before call, or
- * (b) previous_handler points directly to the place that the handler
- * picks up its chain-to address.
- */
-
-int install_irq_handler ( irq_t irq, segoff_t *handler,
-                         uint8_t *previously_enabled,
-                         segoff_t *previous_handler ) {
-       segoff_t *irq_vector = IRQ_VECTOR ( irq );
-       *previously_enabled = irq_enabled ( irq );
-
-       if ( irq > IRQ_MAX ) {
-               DBG ( "Invalid IRQ number %d\n" );
-               return 0;
-       }
-
-       previous_handler->segment = irq_vector->segment;
-       previous_handler->offset = irq_vector->offset;
-       if ( *previously_enabled ) disable_irq ( irq );
-       DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx),"
-             " leaving %s\n",
-             handler->segment, handler->offset, irq, virt_to_phys(irq_vector),
-             ( *previously_enabled ? "enabled" : "disabled" ) );
-       DBG ( "...(previous handler at %hx:%hx)\n",
-             previous_handler->segment, previous_handler->offset );
-       irq_vector->segment = handler->segment;
-       irq_vector->offset = handler->offset;
-       if ( *previously_enabled ) enable_irq ( irq );
-       return 1;
-}
-
-/* Remove handler for the specified IRQ.  Routine checks that another
- * handler has not been installed that chains to handler before
- * uninstalling handler.  Enabled/disabled state of the IRQ will be
- * restored to that specified by previously_enabled.
- */
-
-int remove_irq_handler ( irq_t irq, segoff_t *handler,
-                        uint8_t *previously_enabled,
-                        segoff_t *previous_handler ) {
-       segoff_t *irq_vector = IRQ_VECTOR ( irq );
-
-       if ( irq > IRQ_MAX ) {
-               DBG ( "Invalid IRQ number %d\n" );
-               return 0;
-       }
-       if ( ( irq_vector->segment != handler->segment ) ||
-            ( irq_vector->offset != handler->offset ) ) {
-               DBG ( "Cannot remove handler for IRQ %d\n" );
-               return 0;
-       }
-
-       DBG ( "Removing handler for IRQ %d\n", irq );
-       disable_irq ( irq );
-       irq_vector->segment = previous_handler->segment;
-       irq_vector->offset = previous_handler->offset;
-       if ( *previously_enabled ) enable_irq ( irq );
-       return 1;
-}
-
-/* Install the trivial IRQ handler.  This routine installs the
- * handler, tests it and enables the IRQ.
- */
-
-int install_trivial_irq_handler ( irq_t irq ) {
-       segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
-       
-       if ( trivial_irq_installed_on != IRQ_NONE ) {
-               DBG ( "Can install trivial IRQ handler only once\n" );
-               return 0;
-       }
-       if ( SEGMENT(trivial_irq_handler) > 0xffff ) {
-               DBG ( "Trivial IRQ handler not in base memory\n" );
-               return 0;
-       }
-
-       DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq );
-       if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff,
-                                    trivial_irq_chain,
-                                    trivial_irq_chain_to ) )
-               return 0;
-       trivial_irq_installed_on = irq;
-
-       DBG ( "Testing trivial IRQ handler\n" );
-       disable_irq ( irq );
-       *trivial_irq_trigger_count = 0;
-       trivial_irq_previous_trigger_count = 0;
-       fake_irq ( irq );
-       if ( ! trivial_irq_triggered ( irq ) ) {
-               DBG ( "Installation of trivial IRQ handler failed\n" );
-               remove_trivial_irq_handler ( irq );
-               return 0;
-       }
-       /* Send EOI just in case there was a leftover interrupt */
-       send_specific_eoi ( irq );
-       DBG ( "Trivial IRQ handler installed successfully\n" );
-       enable_irq ( irq );
-       return 1;
-}
-
-/* Remove the trivial IRQ handler.
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-int remove_trivial_irq_handler ( irq_t irq ) {
-       segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler);
-
-       if ( trivial_irq_installed_on == IRQ_NONE ) return 1;
-       if ( irq != trivial_irq_installed_on ) {
-               DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; "
-                     "is installed on IRQ %d\n", irq,
-                     trivial_irq_installed_on );
-               return 0;
-       }
-
-       if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff,
-                                   trivial_irq_chain,
-                                   trivial_irq_chain_to ) )
-               return 0;
+#include <pic8259.h>
 
-       if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) {
-               DBG ( "Sending EOI for unwanted trivial IRQ\n" );
-               send_specific_eoi ( trivial_irq_installed_on );
-       }
-
-       trivial_irq_installed_on = IRQ_NONE;
-       return 1;
-}
-
-/* Safe method to detect whether or not trivial IRQ has been
- * triggered.  Using this call avoids potential race conditions.  This
- * call will return success only once per trigger.
- */
-
-int trivial_irq_triggered ( irq_t irq ) {
-       uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count;
-       int triggered = ( trivial_irq_this_trigger_count -
-                         trivial_irq_previous_trigger_count );
-       
-       /* irq is not used at present, but we have it in the API for
-        * future-proofing; in case we want the facility to have
-        * multiple trivial IRQ handlers installed simultaneously.
-        *
-        * Avoid compiler warning about unused variable.
-        */
-       if ( irq == IRQ_NONE ) {};
-       
-       trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count;
-       return triggered ? 1 : 0;
-}
-
-/* Copy trivial IRQ handler to a new location.  Typically used to copy
- * the handler into base memory; when relocation is being used we need
- * to do this before installing the handler.
+/** @file
+ *
+ * Minimal support for the 8259 Programmable Interrupt Controller
  *
- * Call with target=NULL in order to restore the handler to its
- * original location.
  */
 
-int copy_trivial_irq_handler ( void *target, size_t target_size ) {
-       irq_t currently_installed_on = trivial_irq_installed_on;
-       uint32_t offset = ( target == NULL ? 0 :
-                           target - (void*)_trivial_irq_handler );
-
-       if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) {
-               DBG ( "Insufficient space to copy trivial IRQ handler\n" );
-               return 0;
-       }
-
-       if ( currently_installed_on != IRQ_NONE ) {
-               DBG ("WARNING: relocating trivial IRQ handler while in use\n");
-               if ( ! remove_trivial_irq_handler ( currently_installed_on ) )
-                       return 0;
-       }
-
-       /* Do the actual copy */
-       if ( target != NULL ) {
-               DBG ( "Copying trivial IRQ handler to %hx:%hx\n",
-                     SEGMENT(target), OFFSET(target) );
-               memcpy ( target, _trivial_irq_handler,
-                        TRIVIAL_IRQ_HANDLER_SIZE );
-       } else {
-               DBG ( "Restoring trivial IRQ handler to original location\n" );
-       }
-       /* Update all the pointers to structures within the handler */
-       trivial_irq_handler = ( void (*)P((void)) )
-               ( (void*)_trivial_irq_handler + offset );
-       trivial_irq_trigger_count = (uint16_t*)
-               ( (void*)&_trivial_irq_trigger_count + offset );
-       trivial_irq_chain_to = (segoff_t*)
-               ( (void*)&_trivial_irq_chain_to + offset );
-       trivial_irq_chain = (uint8_t*)
-               ( (void*)&_trivial_irq_chain + offset );
-
-       if ( currently_installed_on != IRQ_NONE ) {
-               if ( ! install_trivial_irq_handler ( currently_installed_on ) )
-                       return 0;
-       }
-       return 1;
-}
-
-/* Send non-specific EOI(s).  This seems to be inherently unsafe.
+/**
+ * Send non-specific EOI(s)
+ *
+ * @v irq              IRQ number
+ *
+ * This seems to be inherently unsafe.
  */
-
-void send_nonspecific_eoi ( irq_t irq ) {
+static inline void send_nonspecific_eoi ( unsigned int irq ) {
        DBG ( "Sending non-specific EOI for IRQ %d\n", irq );
        if ( irq >= IRQ_PIC_CUTOFF ) {
                outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR );
@@ -265,61 +39,25 @@ void send_nonspecific_eoi ( irq_t irq ) {
        outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR );
 }
 
-/* Send specific EOI(s).
+/**
+ * Send specific EOI(s)
+ *
+ * @v irq              IRQ number
  */
-
-void send_specific_eoi ( irq_t irq ) {
+static inline void send_specific_eoi ( unsigned int irq ) {
        DBG ( "Sending specific EOI for IRQ %d\n", irq );
-       outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
+       outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) );
        if ( irq >= IRQ_PIC_CUTOFF ) {
-               outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
-                      ICR_REG(CHAINED_IRQ) );
+               outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ),
+                      ICR_REG ( CHAINED_IRQ ) );
        }
 }
 
-/* Fake an IRQ
- */
-
-void fake_irq ( irq_t irq ) {
-       struct {
-               uint16_t int_number;
-       } PACKED in_stack;
-
-       /* Convert IRQ to INT number:
-        *
-        * subb $0x08,%cl       Invert bit 3, set bits 4-7 iff irq < 8
-        * xorb $0x70,%cl       Invert bits 4-6
-        * andb $0x7f,%cl       Clear bit 7
-        *
-        * No, it's not the most intuitive method, but I was proud to
-        * get it down to three lines of assembler when this routine
-        * was originally implemented in pcbios.S.
-        */
-       in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f;
-
-       RM_FRAGMENT(rm_fake_irq,
-               "popw %ax\n\t"          /* %ax = INT number */
-               "call 1f\n1:\tpop %bx\n\t"
-               "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/
-               "\n2:\tint $0x00\n\t"               /* ..in this instruction */
-       );
-
-       real_call ( rm_fake_irq, &in_stack, NULL );
-}
-
-/* Dump current 8259 status: enabled IRQs and handler addresses.
+/**
+ * Send End-Of-Interrupt to the PIC
+ *
+ * @v irq              IRQ number
  */
-
-#ifdef DEBUG_IRQ
-void dump_irq_status ( void ) {
-       int irq = 0;
-       
-       for ( irq = 0; irq < 16; irq++ ) {
-               if ( irq_enabled ( irq ) ) {
-                       printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
-                                IRQ_VECTOR(irq)->segment,
-                                IRQ_VECTOR(irq)->offset );
-               }
-       }
+void send_eoi ( unsigned int irq ) {
+       send_specific_eoi ( irq );
 }
-#endif
index d3f9b87..0c501a9 100644 (file)
 /* For segoff_t */
 #include "realmode.h"
 
-#define IRQ_PIC_CUTOFF (8)
+#define IRQ_PIC_CUTOFF 8
 
 /* 8259 register locations */
-#define PIC1_ICW1 (0x20)
-#define PIC1_OCW2 (0x20)
-#define PIC1_OCW3 (0x20)
-#define PIC1_ICR (0x20)
-#define PIC1_IRR (0x20)
-#define PIC1_ISR (0x20)
-#define PIC1_ICW2 (0x21)
-#define PIC1_ICW3 (0x21)
-#define PIC1_ICW4 (0x21)
-#define PIC1_IMR (0x21)
-#define PIC2_ICW1 (0xa0)
-#define PIC2_OCW2 (0xa0)
-#define PIC2_OCW3 (0xa0)
-#define PIC2_ICR (0xa0)
-#define PIC2_IRR (0xa0)
-#define PIC2_ISR (0xa0)
-#define PIC2_ICW2 (0xa1)
-#define PIC2_ICW3 (0xa1)
-#define PIC2_ICW4 (0xa1)
-#define PIC2_IMR (0xa1)
+#define PIC1_ICW1 0x20
+#define PIC1_OCW2 0x20
+#define PIC1_OCW3 0x20
+#define PIC1_ICR 0x20
+#define PIC1_IRR 0x20
+#define PIC1_ISR 0x20
+#define PIC1_ICW2 0x21
+#define PIC1_ICW3 0x21
+#define PIC1_ICW4 0x21
+#define PIC1_IMR 0x21
+#define PIC2_ICW1 0xa0
+#define PIC2_OCW2 0xa0
+#define PIC2_OCW3 0xa0
+#define PIC2_ICR 0xa0
+#define PIC2_IRR 0xa0
+#define PIC2_ISR 0xa0
+#define PIC2_ICW2 0xa1
+#define PIC2_ICW3 0xa1
+#define PIC2_ICW4 0xa1
+#define PIC2_IMR 0xa1
 
 /* Register command values */
-#define OCW3_ID (0x08)
-#define OCW3_READ_IRR (0x03)
-#define OCW3_READ_ISR (0x02)
-#define ICR_EOI_NON_SPECIFIC (0x20)
-#define ICR_EOI_NOP (0x40)
-#define ICR_EOI_SPECIFIC (0x60)
-#define ICR_EOI_SET_PRIORITY (0xc0)
+#define OCW3_ID 0x08
+#define OCW3_READ_IRR 0x03
+#define OCW3_READ_ISR 0x02
+#define ICR_EOI_NON_SPECIFIC 0x20
+#define ICR_EOI_NOP 0x40
+#define ICR_EOI_SPECIFIC 0x60
+#define ICR_EOI_SET_PRIORITY 0xc0
 
 /* Macros to enable/disable IRQs */
 #define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR )
 #define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) )
 
 /* Macros for acknowledging IRQs */
-#define ICR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
-#define ICR_VALUE(x) ( (x) % IRQ_PIC_CUTOFF )
+#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR )
+#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF )
 #define CHAINED_IRQ 2
 
 /* Utility macros to convert IRQ numbers to INT numbers and INT vectors  */
-#define IRQ_INT(x) ( (x)<IRQ_PIC_CUTOFF ? (x)+0x08 : (x)-IRQ_PIC_CUTOFF+0x70 )
-#define INT_VECTOR(x) ( (segoff_t*) phys_to_virt( 4 * (x) ) )
-#define IRQ_VECTOR(x) ( INT_VECTOR ( IRQ_INT(x) ) )
+#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f )
 
 /* Other constants */
-typedef uint8_t irq_t;
-#define IRQ_MAX (15)
-#define IRQ_NONE (0xff)
+#define IRQ_MAX 15
+#define IRQ_NONE -1U
 
 /* Function prototypes
  */
-int install_irq_handler ( irq_t irq, segoff_t *handler,
-                         uint8_t *previously_enabled,
-                         segoff_t *previous_handler );
-int remove_irq_handler ( irq_t irq, segoff_t *handler,
-                        uint8_t *previously_enabled,
-                        segoff_t *previous_handler );
-int install_trivial_irq_handler ( irq_t irq );
-int remove_trivial_irq_handler ( irq_t irq );
-int trivial_irq_triggered ( irq_t irq );
-int copy_trivial_irq_handler ( void *target, size_t target_size );
-void send_non_specific_eoi ( irq_t irq );
-void send_specific_eoi ( irq_t irq );
-void fake_irq ( irq_t irq );
-#ifdef DEBUG_IRQ
-void dump_irq_status ( void );
-#else
-#define dump_irq_status()
-#endif
-
-/* Other code (e.g. undi.c) needs to know the size of the trivial IRQ
- * handler code, so we put prototypes and the size macro here.
- */
-extern void _trivial_irq_handler ( void );
-extern char _trivial_irq_handler_size[];
-#define TRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(_trivial_irq_handler)
+void send_eoi ( unsigned int irq );
 
 #endif /* PIC8259_H */