X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fdevices%2F8259a.c;h=2e305e9a88d5b86d111e2dcd7c27c9fed4007c11;hb=159356d2478ab200cda0a0aa726d8f1bfa4ffeeb;hp=60d61808ab22bf47964c0c6e1a7643dc8c27cef7;hpb=e91412faf338dbe40aad6f4df64b98642438d510;p=palacios.git diff --git a/palacios/src/devices/8259a.c b/palacios/src/devices/8259a.c index 60d6180..2e305e9 100644 --- a/palacios/src/devices/8259a.c +++ b/palacios/src/devices/8259a.c @@ -1,9 +1,464 @@ -#include +#include +#include +#include #include +typedef enum {RESET, ICW1, ICW2, ICW3, ICW4, READY} pic_state_t; + +static const uint_t MASTER_PORT1 = 0x20; +static const uint_t MASTER_PORT2 = 0x21; +static const uint_t SLAVE_PORT1 = 0xA0; +static const uint_t SLAVE_PORT2 = 0xA1; + +#define IS_OCW2(x) (((x & 0x18) >> 3) == 0x0) +#define IS_OCW3(x) (((x & 0x18) >> 3) == 0x1) + +struct icw1 { + uint_t ic4 : 1; // ICW4 has to be read + uint_t sngl : 1; // single (only one PIC) + uint_t adi : 1; // call address interval + uint_t ltim : 1; // level interrupt mode + uint_t one : 1; + uint_t rsvd : 3; +}; + + +struct icw2 { + uint_t rsvd : 3; + uint_t vector : 5; +}; + + +// Each bit that is set indicates that the IR input has a slave +struct icw3_master { + uint_t S0 : 1; + uint_t S1 : 1; + uint_t S2 : 1; + uint_t S3 : 1; + uint_t S4 : 1; + uint_t S5 : 1; + uint_t S6 : 1; + uint_t S7 : 1; +}; + +// The ID is the Slave device ID +struct icw3_slave { + uint_t id : 3; + uint_t zeroes : 5; +}; + +struct icw4 { + uint_t uPM : 1; // 1=x86 + uint_t AEOI : 1; // Automatic End of Interrupt + uint_t M_S : 1; // only if buffered 1=master,0=slave + uint_t BUF : 1; // buffered mode + uint_t SFNM : 1; // special fully nexted mode + uint_t zeroes : 3; +}; + + +struct ocw1 { + uint_t m0 : 1; + uint_t m1 : 1; + uint_t m2 : 1; + uint_t m3 : 1; + uint_t m4 : 1; + uint_t m5 : 1; + uint_t m6 : 1; + uint_t m7 : 1; +}; + +struct ocw2 { + uint_t level : 3; + uint_t cw_code : 2; // should be 00 + uint_t EOI : 1; + uint_t SL : 1; + uint_t R : 1; +}; + +struct ocw3 { + uint_t RIS : 1; + uint_t RR : 1; + uint_t P : 1; + uint_t cw_code : 2; // should be 01 + uint_t smm : 1; + uint_t esmm : 1; + uint_t zero2 : 1; +}; + + +struct pic_internal { + + + char master_irr; + char slave_irr; + + char master_isr; + char slave_isr; + + char master_icw1; + char master_icw2; + char master_icw3; + char master_icw4; + + + char slave_icw1; + char slave_icw2; + char slave_icw3; + char slave_icw4; + + + char master_imr; + char slave_imr; + char master_ocw2; + char master_ocw3; + char slave_ocw2; + char slave_ocw3; + + pic_state_t master_state; + pic_state_t slave_state; +}; + + + +static int pic_raise_intr(void * private_data, int irq, int error_code) { + struct pic_internal * state = (struct pic_internal*)private_data; + + if (irq == 2) { + irq = 9; + } + + if (irq <= 7) { + state->master_irr |= 0x1 << irq; + } else if ((irq > 7) && (irq < 16)) { + state->slave_irr |= 0x1 << (irq - 7); + } else { + return -1; + } + + return 0; +} + +static int pic_intr_pending(void * private_data) { + struct pic_internal * state = (struct pic_internal*)private_data; + + if ((state->master_irr & ~(state->master_imr)) || + (state->slave_irr & ~(state->slave_imr))) { + return 1; + } + + return 0; +} + +static int pic_get_intr_number(void * private_data) { + struct pic_internal * state = (struct pic_internal*)private_data; + int i; + + for (i = 0; i < 16; i++) { + if (i <= 7) { + if (((state->master_irr & ~(state->master_imr)) >> i) == 0x1) { + return i; + } + } else { + if (((state->slave_irr & ~(state->slave_imr)) >> i) == 0x1) { + return i; + } + } + } + + return 0; +} + + +static struct intr_ctrl_ops intr_ops = { + .intr_pending = pic_intr_pending, + .get_intr_number = pic_get_intr_number, + .raise_intr = pic_raise_intr +}; + + + + + + + +int read_master_port1(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + if (length != 1) { + //error + } + + if ((state->master_ocw3 & 0x3) == 0x2) { + *(char *)dst = state->master_irr; + } else if ((state->master_ocw3 & 0x3) == 0x3) { + *(char *)dst = state->master_isr; + } else { + *(char *)dst = 0; + } + + return 1; +} + +int read_master_port2(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + if (length != 1) { + // error + } + + *(char *)dst = state->master_imr; + + return 1; + +} + +int read_slave_port1(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + if (length != 1) { + // error + } + + if ((state->slave_ocw3 & 0x3) == 0x2) { + *(char*)dst = state->slave_irr; + } else if ((state->slave_ocw3 & 0x3) == 0x3) { + *(char *)dst = state->slave_isr; + } else { + *(char *)dst = 0; + } + + return 1; +} + +int read_slave_port2(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + if (length != 1) { + // error + } + + *(char *)dst = state->slave_imr; + + return 1; +} + + +int write_master_port1(ushort_t port, void * src, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + char cw = *(char *)src; + + if (length != 1) { + // error + } + + if (state->master_state == ICW1) { + state->master_icw1 = cw; + state->master_state = ICW2; + } else if (state->master_state == READY) { + if (IS_OCW2(cw)) { + state->master_ocw2 = cw; + } else if (IS_OCW3(cw)) { + state->master_ocw3 = cw; + } else { + // error + } + } else { + // error + } + + return 1; +} + +int write_master_port2(ushort_t port, void * src, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + char cw = *(char *)src; + + if (length != 1) { + //error + } + + if (state->master_state == ICW2) { + struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1); + + state->master_icw2 = cw; + + if (cw1->sngl == 0) { + state->master_state = ICW3; + } else if (cw1->ic4 == 1) { + state->master_state = ICW4; + } else { + state->master_state = READY; + } + + } else if (state->master_state == ICW3) { + struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1); + + state->master_icw3 = cw; + + if (cw1->ic4 == 1) { + state->master_state = ICW4; + } else { + state->master_state = READY; + } + + } else if (state->master_state == ICW4) { + state->master_icw4 = cw; + state->master_state = READY; + } else if (state->master_state == READY) { + state->master_imr = cw; + } else { + // error + } + + return 1; +} + +int write_slave_port1(ushort_t port, void * src, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + char cw = *(char *)src; + + if (length != 1) { + // error + } + + if (state->slave_state == ICW1) { + state->slave_icw1 = cw; + state->slave_state = ICW2; + } else if (state->slave_state == READY) { + if (IS_OCW2(cw)) { + state->slave_ocw2 = cw; + } else if (IS_OCW3(cw)) { + state->slave_ocw3 = cw; + } else { + // error + } + } else { + // error + } + + return 1; +} + +int write_slave_port2(ushort_t port, void * src, uint_t length, struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + char cw = *(char *)src; + + if (length != 1) { + //error + } + + if (state->slave_state == ICW2) { + struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1); + + state->slave_icw2 = cw; + + if (cw1->sngl == 0) { + state->slave_state = ICW3; + } else if (cw1->ic4 == 1) { + state->slave_state = ICW4; + } else { + state->slave_state = READY; + } + + } else if (state->slave_state == ICW3) { + struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1); + + state->slave_icw3 = cw; + + if (cw1->ic4 == 1) { + state->slave_state = ICW4; + } else { + state->slave_state = READY; + } + + } else if (state->slave_state == ICW4) { + state->slave_icw4 = cw; + state->slave_state = READY; + } else if (state->slave_state == READY) { + state->slave_imr = cw; + } else { + // error + } + + return 1; +} + + + + + + + + +int pic_init(struct vm_device * dev) { + struct pic_internal * state = (struct pic_internal*)dev->private_data; + + set_intr_controller(dev->vm, &intr_ops, state); + + state->master_irr = 0; + state->master_isr = 0; + state->master_icw1 = 0; + state->master_icw2 = 0; + state->master_icw3 = 0; + state->master_icw4 = 0; + state->master_imr = 0; + state->master_ocw2 = 0; + state->master_ocw3 = 0x2; + state->master_state = ICW1; + + + state->slave_irr = 0; + state->slave_isr = 0; + state->slave_icw1 = 0; + state->slave_icw2 = 0; + state->slave_icw3 = 0; + state->slave_icw4 = 0; + state->slave_imr = 0; + state->slave_ocw2 = 0; + state->slave_ocw3 = 0x2; + state->slave_state = ICW1; + + + dev_hook_io(dev, MASTER_PORT1, &read_master_port1, &write_master_port1); + dev_hook_io(dev, MASTER_PORT2, &read_master_port2, &write_master_port2); + dev_hook_io(dev, SLAVE_PORT1, &read_slave_port1, &write_slave_port1); + dev_hook_io(dev, SLAVE_PORT2, &read_slave_port2, &write_slave_port2); + + return 0; +} + + +int pic_deinit(struct vm_device * dev) { + dev_unhook_io(dev, MASTER_PORT1); + dev_unhook_io(dev, MASTER_PORT2); + dev_unhook_io(dev, SLAVE_PORT1); + dev_unhook_io(dev, SLAVE_PORT2); + + return 0; +} + + + + + + + +static struct vm_device_ops dev_ops = { + .init = pic_init, + .deinit = pic_deinit, + .reset = NULL, + .start = NULL, + .stop = NULL, +}; + + +struct vm_device * create_pic() { + struct pic_internal * state = NULL; + VMMMalloc(struct pic_internal *, state, sizeof(struct pic_internal)); + + struct vm_device *device = create_device("8259A", &dev_ops, state); + + return device; +} + + + -static const uint_t PIC1_PORT_1 0x20; -static const uint_t PIC1_PORT_2 0x21; -static const uint_t PIC2_PORT_1 0xA0; -static const uint_t PIC2_PORT_2 0xA1;