view cp/guest/init.c @ 653:0d289341e756

cp: maintain a circular buffer of Channel Report Words Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Thu, 29 Mar 2012 17:42:13 -0400
parents dad0b2bc37c0
children 3ede15f57135
line wrap: on
line source

/*
 * (C) Copyright 2007-2012  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * This file is released under the GPLv2.  See the COPYING file for more
 * details.
 */

#include <directory.h>
#include <sched.h>
#include <errno.h>
#include <page.h>
#include <buddy.h>
#include <slab.h>
#include <dat.h>
#include <clock.h>
#include <ebcdic.h>
#include <cpu.h>
#include <vcpu.h>
#include <vdevice.h>
#include <mutex.h>
#include <vsprintf.h>
#include <shell.h>
#include <guest.h>

static LOCK_CLASS(online_users_lc);
static LIST_HEAD(online_users);
static UNLOCKED_MUTEX(online_users_lock, &online_users_lc);

static LOCK_CLASS(guest_interrupt_queue_lc);

static LOCK_CLASS(virt_devs_lc);

static void alloc_guest_devices(struct virt_sys *sys)
{
	struct directory_vdev *cur;
	int ret;
	int i;

	mutex_init(&sys->virt_devs_lock, &virt_devs_lc);
	INIT_LIST_HEAD(&sys->virt_devs);

	mutex_lock(&sys->virt_devs_lock);

	i = 0;
	list_for_each_entry(cur, &sys->directory->devices, list) {
		ret = alloc_virt_dev(sys, cur, 0x10000 + i);
		if (ret)
			con_printf(sys->con, "Failed to allocate vdev %04X, "
				   "SCH = %05X (%s)\n", cur->vdev, 0x10000 + i,
				   errstrings[-ret]);
		i++;
	}

	mutex_unlock(&sys->virt_devs_lock);
}

static int alloc_guest_storage(struct virt_sys *sys)
{
	u64 pages = sys->directory->storage_size >> PAGE_SHIFT;
	struct page *p;

	INIT_LIST_HEAD(&sys->guest_pages);

	while (pages) {
		p = alloc_pages(0, ZONE_NORMAL);
		if (!p)
			return -ENOMEM;

		list_add(&p->guest, &sys->guest_pages);

		pages--;

		dat_insert_page(&sys->as, (u64) page_to_addr(p),
				pages << PAGE_SHIFT);
	}

	return 0;
}

static void free_vcpu(struct virt_sys *sys)
{
	struct virt_cpu *cpu = sys->cpu;

	if (sys->task) {
		FIXME("kill the vcpu task");
	}

	assert(list_empty(&cpu->int_io[0]));
	assert(list_empty(&cpu->int_io[1]));
	assert(list_empty(&cpu->int_io[2]));
	assert(list_empty(&cpu->int_io[3]));
	assert(list_empty(&cpu->int_io[4]));
	assert(list_empty(&cpu->int_io[5]));
	assert(list_empty(&cpu->int_io[6]));
	assert(list_empty(&cpu->int_io[7]));

	free_pages(cpu, 0);

	sys->cpu = NULL;
}

static int alloc_vcpu(struct virt_sys *sys)
{
	char tname[TASK_NAME_LEN+1];
	struct virt_cpu *cpu;
	struct page *page;
	int ret;

	page = alloc_pages(0, ZONE_NORMAL);
	if (!page)
		return -ENOMEM;

	cpu = page_to_addr(page);

	memset(cpu, 0, PAGE_SIZE);
	mutex_init(&cpu->int_lock, &guest_interrupt_queue_lc);
	INIT_LIST_HEAD(&cpu->int_io[0]);
	INIT_LIST_HEAD(&cpu->int_io[1]);
	INIT_LIST_HEAD(&cpu->int_io[2]);
	INIT_LIST_HEAD(&cpu->int_io[3]);
	INIT_LIST_HEAD(&cpu->int_io[4]);
	INIT_LIST_HEAD(&cpu->int_io[5]);
	INIT_LIST_HEAD(&cpu->int_io[6]);
	INIT_LIST_HEAD(&cpu->int_io[7]);

	cpu->cpuid = getcpuid() | 0xFF00000000000000ULL;

	cpu->sie_cb.gmsor = 0;
	cpu->sie_cb.gmslm = sys->directory->storage_size;
	cpu->sie_cb.gbea = 1;
	cpu->sie_cb.ecb  = 2;
	cpu->sie_cb.eca  = 0xC1002001U;
	/*
	 * TODO: What about ->scaoh and ->scaol?
	 */

	sys->cpu = cpu;

	snprintf(tname, 32, "%s-vcpu0", sysconf.oper_userid);
	sys->task = create_task(tname, shell_start, sys);
	if (IS_ERR(sys->task)) {
		ret = PTR_ERR(sys->task);
		sys->task = NULL;
		free_vcpu(sys);
		return ret;
	}

	return 0;
}

static int alloc_console(struct virt_cons *con)
{
	struct page *page;
	int ret;

	con->wlines = alloc_spool();
	if (IS_ERR(con->wlines)) {
		ret = PTR_ERR(con->wlines);
		goto out;
	}

	con->rlines = alloc_spool();
	if (IS_ERR(con->rlines)) {
		ret = PTR_ERR(con->rlines);
		goto out_free;
	}

	page = alloc_pages(0, ZONE_NORMAL);
	if (!page) {
		ret = -ENOMEM;
		goto out_free2;
	}

	con->bigbuf = page_to_addr(page);

	return 0;

out_free2:
	free_spool(con->rlines);
out_free:
	free_spool(con->wlines);
out:
	return ret;
}

static void free_console(struct virt_cons *con)
{
	free_spool(con->rlines);
	free_spool(con->wlines);
	free_pages(con->bigbuf, 0);
}

struct virt_sys *guest_create(char *name, struct device *rcon)
{
	struct virt_sys *sys;
	int already_online = 0;
	int ret;

	/* first, check that we're not already online */
	mutex_lock(&online_users_lock);
	list_for_each_entry(sys, &online_users, online_users) {
		if (!strcmp(sys->directory->userid, name)) {
			already_online = 1;
			break;
		}
	}
	mutex_unlock(&online_users_lock);

	if (already_online)
		return NULL;

	sys = malloc(sizeof(struct virt_sys), ZONE_NORMAL);
	if (!sys)
		return NULL;

	init_circbuf(&sys->crws, struct crw, NUM_CRWS);

	if (alloc_console(&sys->console))
		goto free;

	sys->con = &sys->console;
	sys->con->dev = rcon;

	sys->directory = find_user_by_id(name);
	if (IS_ERR(sys->directory))
		goto free_cons;

	alloc_guest_devices(sys);

	ret = alloc_guest_storage(sys);
	assert(!ret);

	ret = alloc_vcpu(sys);
	assert(!ret);

	sys->print_ts = 1; /* print timestamps */

	mutex_lock(&online_users_lock);
	list_add_tail(&sys->online_users, &online_users);
	mutex_unlock(&online_users_lock);

	return sys;

free_cons:
	free_console(&sys->console);
free:
	free(sys);
	return NULL;
}

void list_users(struct virt_cons *con, void (*f)(struct virt_cons *con,
						 struct virt_sys *sys))
{
	struct virt_sys *sys;

	if (!f)
		return;

	mutex_lock(&online_users_lock);
	list_for_each_entry(sys, &online_users, online_users)
		f(con, sys);
	mutex_unlock(&online_users_lock);
}