Commit 541574a6 authored by lixianglai's avatar lixianglai Committed by Hongchen Zhang
Browse files

loongarch/kvm: ls7a add iocsrw emulation

LoongArch inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I6MUZX



--------------------------------

The virtual machine has a case where the LS7A registers
 are accessed in length 4 bytes, so the emulation of
4-byte access to LS7A is added.

Signed-off-by: default avatarlixianglai <lixianglai@loongson.cn>
Change-Id: I46daf63d7a1766080a806adbd3f2d6b77f9657d0
parent e3874862
Loading
Loading
Loading
Loading
+134 −2
Original line number Diff line number Diff line
@@ -139,11 +139,13 @@ static int ls7a_ioapic_reg_write(struct ls7a_kvm_ioapic *s,
	struct kvm_ls7a_ioapic_state *state;
	int64_t offset_tmp;
	uint64_t offset;
	uint64_t data, old;
	uint64_t data, old, himask, lowmask;;

	offset = addr & 0xfff;
	kvm = s->kvm;
	state = &(s->ls7a_ioapic);
	lowmask = 0xFFFFFFFFUL;
	himask = lowmask << 32;

	if (offset & (len - 1)) {
		printk("%s(%d):unaligned address access %llx size %d \n",
@@ -191,6 +193,80 @@ static int ls7a_ioapic_reg_write(struct ls7a_kvm_ioapic *s,
			WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len);
			break;
		}
	} else if (4 == len) {
		data = *(uint32_t *)val;
		switch (offset) {
		case LS7A_INT_MASK_OFFSET:
			old = state->int_mask & lowmask;
			state->int_mask = (state->int_mask & himask) | data;
			if (old & ~data)
				kvm_ls7a_ioapic_raise(kvm, old & ~data);
			if (~old & data)
				kvm_ls7a_ioapic_lower(kvm, ~old & data);
			break;
		case LS7A_INT_MASK_OFFSET + 4:
			data = data << 32;
			old = state->int_mask & himask;
			state->int_mask = (state->int_mask & lowmask) | data;
			if (old & ~data)
				kvm_ls7a_ioapic_raise(kvm, old & ~data);
			if (~old & data)
				kvm_ls7a_ioapic_lower(kvm, ~old & data);
			break;
		case LS7A_INT_STATUS_OFFSET:
			state->intisr = (state->intisr & himask) | data;
			break;
		case LS7A_INT_STATUS_OFFSET + 4:
			data = data << 32;
			state->intisr = (state->intisr & lowmask) | data;
			break;
		case LS7A_INT_EDGE_OFFSET:
			state->intedge = (state->intedge & himask) | data;
			break;
		case LS7A_INT_EDGE_OFFSET + 4:
			data = data << 32;
			state->intedge = (state->intedge & lowmask) | data;
			break;
		case LS7A_INT_CLEAR_OFFSET:
			/*
			 * only clear edge triggered irq on writing INTCLR reg
			 * no effect on level triggered irq
			 */
			data = data & state->intedge;
			state->intirr &= ~data;
			kvm_ls7a_ioapic_lower(kvm, data);
			state->intisr &= ~data;
			break;
		case LS7A_INT_CLEAR_OFFSET + 4:
			data = data << 32;
			data = data & state->intedge;
			state->intirr &= ~data;
			kvm_ls7a_ioapic_lower(kvm, data);
			state->intisr &= ~data;
			break;
		case LS7A_INT_POL_OFFSET:
			state->int_polarity = (state->int_polarity & himask) | data;
			break;
		case LS7A_INT_POL_OFFSET+4:
			data = data << 32;
			state->int_polarity = (state->int_polarity & lowmask) | data;
			break;
		case LS7A_HTMSI_EN_OFFSET:
			state->htmsi_en = (state->htmsi_en & himask) | data;
			break;
		case LS7A_HTMSI_EN_OFFSET+4:
			data = data << 32;
			state->htmsi_en = (state->htmsi_en & lowmask) | data;
			break;
		case LS7A_AUTO_CTRL0_OFFSET:
		case LS7A_AUTO_CTRL0_OFFSET+4:
		case LS7A_AUTO_CTRL1_OFFSET:
		case LS7A_AUTO_CTRL1_OFFSET+4:
			break;
		default:
			WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len);
			break;
		}
	} else if (1 == len) {
		data = *(unsigned char *)val;
		if (offset >= LS7A_HTMSI_VEC_OFFSET) {
@@ -240,11 +316,13 @@ static int ls7a_ioapic_reg_read(struct ls7a_kvm_ioapic *s,
	uint64_t offset, offset_tmp;
	struct kvm *kvm;
	struct kvm_ls7a_ioapic_state *state;
	uint64_t result = 0;
	uint64_t result = 0, lowmask, himask;

	state = &(s->ls7a_ioapic);
	kvm = s->kvm;
	offset = addr & 0xfff;
	lowmask = 0xFFFFFFFFUL;
	himask = lowmask << 32;
	if (offset & (len - 1)) {
		printk("%s(%d):unaligned address access %llx size %d \n",
			__FUNCTION__, __LINE__, addr, len);
@@ -281,6 +359,60 @@ static int ls7a_ioapic_reg_read(struct ls7a_kvm_ioapic *s,
		}
		if (val != NULL)
			*(uint64_t *)val = result;
	} else if (4 == len) {
		switch (offset) {
		case LS7A_INT_MASK_OFFSET:
			result = state->int_mask & lowmask;
			break;
		case LS7A_INT_MASK_OFFSET + 4:
			result = state->int_mask & himask;
			result = result >> 32;
			break;
		case LS7A_INT_STATUS_OFFSET:
			result = state->intisr & (~state->int_mask) & lowmask;
			break;
		case LS7A_INT_STATUS_OFFSET + 4:
			result = state->intisr & (~state->int_mask) & himask;
			result = result >> 32;
			break;
		case LS7A_INT_EDGE_OFFSET:
			result = state->intedge & lowmask;
			break;
		case LS7A_INT_EDGE_OFFSET + 4:
			result = state->intedge & himask;
			result = result >> 32;
			break;
		case LS7A_INT_POL_OFFSET:
			result = state->int_polarity & lowmask;
			break;
		case LS7A_INT_POL_OFFSET + 4:
			result = state->int_polarity & himask;
			result = result >> 32;
			break;
		case LS7A_HTMSI_EN_OFFSET:
			result = state->htmsi_en & lowmask;
			break;
		case LS7A_HTMSI_EN_OFFSET + 4:
			result = state->htmsi_en & himask;
			result = result >> 32;
			break;
		case LS7A_AUTO_CTRL0_OFFSET:
		case LS7A_AUTO_CTRL0_OFFSET + 4:
		case LS7A_AUTO_CTRL1_OFFSET:
		case LS7A_AUTO_CTRL1_OFFSET + 4:
			break;
		case LS7A_INT_ID_OFFSET:
			result = LS7A_INT_ID_VAL;
			break;
		case LS7A_INT_ID_OFFSET + 4:
			result = LS7A_INT_ID_VER;
			break;
		default:
			WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len);
			break;
		}
		if (val != NULL)
			*(uint32_t *)val = result;
	} else if (1 == len) {
		if (offset >= LS7A_HTMSI_VEC_OFFSET) {
			offset_tmp = offset - LS7A_HTMSI_VEC_OFFSET;