i8259.c

来自「linux 内核源代码」· C语言 代码 · 共 451 行

C
451
字号
/* * 8259 interrupt controller emulation * * Copyright (c) 2003-2004 Fabrice Bellard * Copyright (c) 2007 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * Authors: *   Yaozu (Eddie) Dong <Eddie.dong@intel.com> *   Port from Qemu. */#include <linux/mm.h>#include "irq.h"/* * set irq level. If an edge is detected, then the IRR is set to 1 */static inline void pic_set_irq1(struct kvm_kpic_state *s, int irq, int level){	int mask;	mask = 1 << irq;	if (s->elcr & mask)	/* level triggered */		if (level) {			s->irr |= mask;			s->last_irr |= mask;		} else {			s->irr &= ~mask;			s->last_irr &= ~mask;		}	else	/* edge triggered */		if (level) {			if ((s->last_irr & mask) == 0)				s->irr |= mask;			s->last_irr |= mask;		} else			s->last_irr &= ~mask;}/* * return the highest priority found in mask (highest = smallest * number). Return 8 if no irq */static inline int get_priority(struct kvm_kpic_state *s, int mask){	int priority;	if (mask == 0)		return 8;	priority = 0;	while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)		priority++;	return priority;}/* * return the pic wanted interrupt. return -1 if none */static int pic_get_irq(struct kvm_kpic_state *s){	int mask, cur_priority, priority;	mask = s->irr & ~s->imr;	priority = get_priority(s, mask);	if (priority == 8)		return -1;	/*	 * compute current priority. If special fully nested mode on the	 * master, the IRQ coming from the slave is not taken into account	 * for the priority computation.	 */	mask = s->isr;	if (s->special_fully_nested_mode && s == &s->pics_state->pics[0])		mask &= ~(1 << 2);	cur_priority = get_priority(s, mask);	if (priority < cur_priority)		/*		 * higher priority found: an irq should be generated		 */		return (priority + s->priority_add) & 7;	else		return -1;}/* * raise irq to CPU if necessary. must be called every time the active * irq may change */static void pic_update_irq(struct kvm_pic *s){	int irq2, irq;	irq2 = pic_get_irq(&s->pics[1]);	if (irq2 >= 0) {		/*		 * if irq request by slave pic, signal master PIC		 */		pic_set_irq1(&s->pics[0], 2, 1);		pic_set_irq1(&s->pics[0], 2, 0);	}	irq = pic_get_irq(&s->pics[0]);	if (irq >= 0)		s->irq_request(s->irq_request_opaque, 1);	else		s->irq_request(s->irq_request_opaque, 0);}void kvm_pic_update_irq(struct kvm_pic *s){	pic_update_irq(s);}void kvm_pic_set_irq(void *opaque, int irq, int level){	struct kvm_pic *s = opaque;	pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);	pic_update_irq(s);}/* * acknowledge interrupt 'irq' */static inline void pic_intack(struct kvm_kpic_state *s, int irq){	if (s->auto_eoi) {		if (s->rotate_on_auto_eoi)			s->priority_add = (irq + 1) & 7;	} else		s->isr |= (1 << irq);	/*	 * We don't clear a level sensitive interrupt here	 */	if (!(s->elcr & (1 << irq)))		s->irr &= ~(1 << irq);}int kvm_pic_read_irq(struct kvm_pic *s){	int irq, irq2, intno;	irq = pic_get_irq(&s->pics[0]);	if (irq >= 0) {		pic_intack(&s->pics[0], irq);		if (irq == 2) {			irq2 = pic_get_irq(&s->pics[1]);			if (irq2 >= 0)				pic_intack(&s->pics[1], irq2);			else				/*				 * spurious IRQ on slave controller				 */				irq2 = 7;			intno = s->pics[1].irq_base + irq2;			irq = irq2 + 8;		} else			intno = s->pics[0].irq_base + irq;	} else {		/*		 * spurious IRQ on host controller		 */		irq = 7;		intno = s->pics[0].irq_base + irq;	}	pic_update_irq(s);	return intno;}static void pic_reset(void *opaque){	struct kvm_kpic_state *s = opaque;	s->last_irr = 0;	s->irr = 0;	s->imr = 0;	s->isr = 0;	s->priority_add = 0;	s->irq_base = 0;	s->read_reg_select = 0;	s->poll = 0;	s->special_mask = 0;	s->init_state = 0;	s->auto_eoi = 0;	s->rotate_on_auto_eoi = 0;	s->special_fully_nested_mode = 0;	s->init4 = 0;}static void pic_ioport_write(void *opaque, u32 addr, u32 val){	struct kvm_kpic_state *s = opaque;	int priority, cmd, irq;	addr &= 1;	if (addr == 0) {		if (val & 0x10) {			pic_reset(s);	/* init */			/*			 * deassert a pending interrupt			 */			s->pics_state->irq_request(s->pics_state->						   irq_request_opaque, 0);			s->init_state = 1;			s->init4 = val & 1;			if (val & 0x02)				printk(KERN_ERR "single mode not supported");			if (val & 0x08)				printk(KERN_ERR				       "level sensitive irq not supported");		} else if (val & 0x08) {			if (val & 0x04)				s->poll = 1;			if (val & 0x02)				s->read_reg_select = val & 1;			if (val & 0x40)				s->special_mask = (val >> 5) & 1;		} else {			cmd = val >> 5;			switch (cmd) {			case 0:			case 4:				s->rotate_on_auto_eoi = cmd >> 2;				break;			case 1:	/* end of interrupt */			case 5:				priority = get_priority(s, s->isr);				if (priority != 8) {					irq = (priority + s->priority_add) & 7;					s->isr &= ~(1 << irq);					if (cmd == 5)						s->priority_add = (irq + 1) & 7;					pic_update_irq(s->pics_state);				}				break;			case 3:				irq = val & 7;				s->isr &= ~(1 << irq);				pic_update_irq(s->pics_state);				break;			case 6:				s->priority_add = (val + 1) & 7;				pic_update_irq(s->pics_state);				break;			case 7:				irq = val & 7;				s->isr &= ~(1 << irq);				s->priority_add = (irq + 1) & 7;				pic_update_irq(s->pics_state);				break;			default:				break;	/* no operation */			}		}	} else		switch (s->init_state) {		case 0:		/* normal mode */			s->imr = val;			pic_update_irq(s->pics_state);			break;		case 1:			s->irq_base = val & 0xf8;			s->init_state = 2;			break;		case 2:			if (s->init4)				s->init_state = 3;			else				s->init_state = 0;			break;		case 3:			s->special_fully_nested_mode = (val >> 4) & 1;			s->auto_eoi = (val >> 1) & 1;			s->init_state = 0;			break;		}}static u32 pic_poll_read(struct kvm_kpic_state *s, u32 addr1){	int ret;	ret = pic_get_irq(s);	if (ret >= 0) {		if (addr1 >> 7) {			s->pics_state->pics[0].isr &= ~(1 << 2);			s->pics_state->pics[0].irr &= ~(1 << 2);		}		s->irr &= ~(1 << ret);		s->isr &= ~(1 << ret);		if (addr1 >> 7 || ret != 2)			pic_update_irq(s->pics_state);	} else {		ret = 0x07;		pic_update_irq(s->pics_state);	}	return ret;}static u32 pic_ioport_read(void *opaque, u32 addr1){	struct kvm_kpic_state *s = opaque;	unsigned int addr;	int ret;	addr = addr1;	addr &= 1;	if (s->poll) {		ret = pic_poll_read(s, addr1);		s->poll = 0;	} else		if (addr == 0)			if (s->read_reg_select)				ret = s->isr;			else				ret = s->irr;		else			ret = s->imr;	return ret;}static void elcr_ioport_write(void *opaque, u32 addr, u32 val){	struct kvm_kpic_state *s = opaque;	s->elcr = val & s->elcr_mask;}static u32 elcr_ioport_read(void *opaque, u32 addr1){	struct kvm_kpic_state *s = opaque;	return s->elcr;}static int picdev_in_range(struct kvm_io_device *this, gpa_t addr){	switch (addr) {	case 0x20:	case 0x21:	case 0xa0:	case 0xa1:	case 0x4d0:	case 0x4d1:		return 1;	default:		return 0;	}}static void picdev_write(struct kvm_io_device *this,			 gpa_t addr, int len, const void *val){	struct kvm_pic *s = this->private;	unsigned char data = *(unsigned char *)val;	if (len != 1) {		if (printk_ratelimit())			printk(KERN_ERR "PIC: non byte write\n");		return;	}	switch (addr) {	case 0x20:	case 0x21:	case 0xa0:	case 0xa1:		pic_ioport_write(&s->pics[addr >> 7], addr, data);		break;	case 0x4d0:	case 0x4d1:		elcr_ioport_write(&s->pics[addr & 1], addr, data);		break;	}}static void picdev_read(struct kvm_io_device *this,			gpa_t addr, int len, void *val){	struct kvm_pic *s = this->private;	unsigned char data = 0;	if (len != 1) {		if (printk_ratelimit())			printk(KERN_ERR "PIC: non byte read\n");		return;	}	switch (addr) {	case 0x20:	case 0x21:	case 0xa0:	case 0xa1:		data = pic_ioport_read(&s->pics[addr >> 7], addr);		break;	case 0x4d0:	case 0x4d1:		data = elcr_ioport_read(&s->pics[addr & 1], addr);		break;	}	*(unsigned char *)val = data;}/* * callback when PIC0 irq status changed */static void pic_irq_request(void *opaque, int level){	struct kvm *kvm = opaque;	struct kvm_vcpu *vcpu = kvm->vcpus[0];	pic_irqchip(kvm)->output = level;	if (vcpu)		kvm_vcpu_kick(vcpu);}struct kvm_pic *kvm_create_pic(struct kvm *kvm){	struct kvm_pic *s;	s = kzalloc(sizeof(struct kvm_pic), GFP_KERNEL);	if (!s)		return NULL;	s->pics[0].elcr_mask = 0xf8;	s->pics[1].elcr_mask = 0xde;	s->irq_request = pic_irq_request;	s->irq_request_opaque = kvm;	s->pics[0].pics_state = s;	s->pics[1].pics_state = s;	/*	 * Initialize PIO device	 */	s->dev.read = picdev_read;	s->dev.write = picdev_write;	s->dev.in_range = picdev_in_range;	s->dev.private = s;	kvm_io_bus_register_dev(&kvm->pio_bus, &s->dev);	return s;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?