/* * Initialize VHPT support. * * Copyright (C) 2004 Hewlett-Packard Co * Dan Magenheimer * * Copyright (c) 2006 Isaku Yamahata * VA Linux Systems Japan K.K. * per vcpu vhpt support */ #include #include #include #include #include #include #include #include #include #include #include DEFINE_PER_CPU (unsigned long, vhpt_paddr); DEFINE_PER_CPU (unsigned long, vhpt_pend); #ifdef CONFIG_XEN_IA64_TLBFLUSH_CLOCK DEFINE_PER_CPU(volatile u32, vhpt_tlbflush_timestamp); #endif static void __vhpt_flush(unsigned long vhpt_maddr, unsigned long vhpt_size_log2) { struct vhpt_lf_entry *v = (struct vhpt_lf_entry*)__va(vhpt_maddr); unsigned long num_entries = 1 << (vhpt_size_log2 - 5); int i; for (i = 0; i < num_entries; i++, v++) v->ti_tag = INVALID_TI_TAG; } void local_vhpt_flush(void) { /* increment flush clock before flush */ u32 flush_time = tlbflush_clock_inc_and_return(); __vhpt_flush(__ia64_per_cpu_var(vhpt_paddr), VHPT_SIZE_LOG2); /* this must be after flush */ tlbflush_update_time(&__get_cpu_var(vhpt_tlbflush_timestamp), flush_time); perfc_incr(local_vhpt_flush); } void vcpu_vhpt_flush(struct vcpu* v) { unsigned long vhpt_size_log2 = VHPT_SIZE_LOG2; #ifdef CONFIG_XEN_IA64_PERVCPU_VHPT if (HAS_PERVCPU_VHPT(v->domain)) vhpt_size_log2 = v->arch.pta.size; #endif __vhpt_flush(vcpu_vhpt_maddr(v), vhpt_size_log2); perfc_incr(vcpu_vhpt_flush); } static void vhpt_erase(unsigned long vhpt_maddr, unsigned long vhpt_size_log2) { struct vhpt_lf_entry *v = (struct vhpt_lf_entry*)__va(vhpt_maddr); unsigned long num_entries = 1 << (vhpt_size_log2 - 5); int i; for (i = 0; i < num_entries; i++, v++) { v->itir = 0; v->CChain = 0; v->page_flags = 0; v->ti_tag = INVALID_TI_TAG; } // initialize cache too??? } void vhpt_insert (unsigned long vadr, unsigned long pte, unsigned long itir) { struct vhpt_lf_entry *vlfe = (struct vhpt_lf_entry *)ia64_thash(vadr); unsigned long tag = ia64_ttag (vadr); /* Even though VHPT is per VCPU, still need to first disable the entry, * because the processor may support speculative VHPT walk. */ vlfe->ti_tag = INVALID_TI_TAG; wmb(); vlfe->itir = itir; vlfe->page_flags = pte | _PAGE_P; *(volatile unsigned long*)&vlfe->ti_tag = tag; } void vhpt_multiple_insert(unsigned long vaddr, unsigned long pte, unsigned long itir) { unsigned char ps = current->arch.vhpt_pg_shift; ia64_itir_t _itir = {.itir = itir}; unsigned long mask = (1L << _itir.ps) - 1; int i; if (_itir.ps - ps > 10 && !running_on_sim) { // if this happens, we may want to revisit this algorithm panic("vhpt_multiple_insert:logps-PAGE_SHIFT>10,spinning..\n"); } if (_itir.ps - ps > 2) { // FIXME: Should add counter here to see how often this // happens (e.g. for 16MB pages!) and determine if it // is a performance problem. On a quick look, it takes // about 39000 instrs for a 16MB page and it seems to occur // only a few times/second, so OK for now. // An alternate solution would be to just insert the one // 16KB in the vhpt (but with the full mapping)? //printk("vhpt_multiple_insert: logps-PAGE_SHIFT==%d," //"va=%p, pa=%p, pa-masked=%p\n", //logps-PAGE_SHIFT,vaddr,pte&_PFN_MASK, //(pte&_PFN_MASK)&~mask); } vaddr &= ~mask; pte = ((pte & _PFN_MASK) & ~mask) | (pte & ~_PFN_MASK); for (i = 1L << (_itir.ps - ps); i > 0; i--) { vhpt_insert(vaddr, pte, _itir.itir); vaddr += (1L << ps); } } void __init vhpt_init(void) { unsigned long paddr; struct page_info *page; #if !VHPT_ENABLED return; #endif /* This allocation only holds true if vhpt table is unique for * all domains. Or else later new vhpt table should be allocated * from domain heap when each domain is created. Assume xen buddy * allocator can provide natural aligned page by order? */ page = alloc_domheap_pages(NULL, VHPT_SIZE_LOG2 - PAGE_SHIFT, 0); if (!page) panic("vhpt_init: can't allocate VHPT!\n"); paddr = page_to_maddr(page); if (paddr & ((1 << VHPT_SIZE_LOG2) - 1)) panic("vhpt_init: bad VHPT alignment!\n"); __get_cpu_var(vhpt_paddr) = paddr; __get_cpu_var(vhpt_pend) = paddr + (1 << VHPT_SIZE_LOG2) - 1; printk(XENLOG_DEBUG "vhpt_init: vhpt paddr=0x%lx, end=0x%lx\n", paddr, __get_cpu_var(vhpt_pend)); vhpt_erase(paddr, VHPT_SIZE_LOG2); // we don't enable VHPT here. // context_switch() or schedule_tail() does it. } #ifdef CONFIG_XEN_IA64_PERVCPU_VHPT void domain_set_vhpt_size(struct domain *d, int8_t vhpt_size_log2) { if (vhpt_size_log2 == -1) { d->arch.has_pervcpu_vhpt = 0; printk(XENLOG_INFO "XEN_DOMCTL_arch_setup: " "domain %d VHPT is global.\n", d->domain_id); } else { d->arch.has_pervcpu_vhpt = 1; d->arch.vhpt_size_log2 = vhpt_size_log2; printk(XENLOG_INFO "XEN_DOMCTL_arch_setup: " "domain %d VHPT is per vcpu. size=2**%d\n", d->domain_id, vhpt_size_log2); } } int pervcpu_vhpt_alloc(struct vcpu *v) { unsigned long vhpt_size_log2 = VHPT_SIZE_LOG2; if (v->domain->arch.vhpt_size_log2 > 0) vhpt_size_log2 = canonicalize_vhpt_size(v->domain->arch.vhpt_size_log2); printk(XENLOG_DEBUG "%s vhpt_size_log2=%ld\n", __func__, vhpt_size_log2); v->arch.vhpt_entries = (1UL << vhpt_size_log2) / sizeof(struct vhpt_lf_entry); v->arch.vhpt_page = alloc_domheap_pages(NULL, vhpt_size_log2 - PAGE_SHIFT, 0); if (!v->arch.vhpt_page) return -ENOMEM; v->arch.vhpt_maddr = page_to_maddr(v->arch.vhpt_page); if (v->arch.vhpt_maddr & ((1 << VHPT_SIZE_LOG2) - 1)) panic("pervcpu_vhpt_init: bad VHPT alignment!\n"); v->arch.pta.val = 0; // to zero reserved bits v->arch.pta.ve = 1; // enable vhpt v->arch.pta.size = vhpt_size_log2; v->arch.pta.vf = 1; // long format v->arch.pta.base = __va_ul(v->arch.vhpt_maddr) >> 15; vhpt_erase(v->arch.vhpt_maddr, vhpt_size_log2); smp_mb(); // per vcpu vhpt may be used by another physical cpu. return 0; } void pervcpu_vhpt_free(struct vcpu *v) { if (likely(v->arch.vhpt_page != NULL)) free_domheap_pages(v->arch.vhpt_page, v->arch.pta.size - PAGE_SHIFT); } #endif void domain_purge_swtc_entries(struct domain *d) { struct vcpu* v; for_each_vcpu(d, v) { if (!v->is_initialised) continue; /* Purge TC entries. FIXME: clear only if match. */ vcpu_purge_tr_entry(&PSCBX(v,dtlb)); vcpu_purge_tr_entry(&PSCBX(v,itlb)); } } void domain_purge_swtc_entries_vcpu_dirty_mask(struct domain* d, vcpumask_t vcpu_dirty_mask) { int vcpu; for_each_vcpu_mask(vcpu, vcpu_dirty_mask) { struct vcpu* v = d->vcpu[vcpu]; if (!v->is_initialised) continue; /* Purge TC entries. FIXME: clear only if match. */ vcpu_purge_tr_entry(&PSCBX(v, dtlb)); vcpu_purge_tr_entry(&PSCBX(v, itlb)); } } // SMP: we can't assume v == current, vcpu might move to a
#!/usr/bin/env perl
#
# Script for generating redboot configs, based on brcmImage.pl
#
# Copyright (C) 2015 Álvaro Fernández Rojas <noltari@gmail.com>
#
# 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
# (at your option) any later version.
#
# 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.
#
# 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.
#

use strict;
use Getopt::Std;
use File::stat;

my $version = "0.1";
my %arg = (
	o => 'redboot.script',
	s => 0x1000,
	f => 0xbe430000,
	a => 0x80010000,
	l => 0x7c0000,
	t => 20,
);
my $prog = $0;
$prog =~ s/^.*\///;
getopts("r:k:o:s:f:a:l:t:vh", \%arg);

die "usage: $prog ~opts~

  -r <file>	: input rootfs file
  -k <file>	: input kernel file
  -o <file>	: output image file, default $arg{o}
  -s <size_kb>	: redboot script size, default ".sprintf('%d', parse_num($arg{s}))."
  -f <baseaddr>	: flash base, default ".sprintf('0x%x', parse_num($arg{f}))."
  -a <loadaddr>	: Kernel load address, default ".sprintf('0x%x', parse_num($arg{a}))."
  -l <linux_kb>	: linux partition size, default ".sprintf('0x%x', parse_num($arg{l}))."
  -t <timeout> 	: redboot script timeout, default ".sprintf('%d', parse_num($arg{t}))."
  -v		: be more verbose
  -h		: help, version $version

EXAMPLES:
    $prog -k kern -r rootfs
" if $arg{h} || !$arg{k} || !$arg{r};

sub parse_num
{
	my $num = @_[0];
	if (index(lc($num), lc("0x")) == 0) {
		return hex($num);
	} else {
		return $num + 0;
	}
}

sub gen_script
{
	my $kernel_off = parse_num($arg{s});
	my $kernel_addr = parse_num($arg{f});
	my $kernel_len = stat($arg{k})->size;

	my $rootfs_off = $kernel_off + $kernel_len;
	my $rootfs_addr = $kernel_addr + $kernel_len;
	my $rootfs_len = parse_num($arg{l}) - $kernel_len;
	my $rootfs_size = stat($arg{r})->size;

	my $load_addr = parse_num($arg{a});

	my $timeout = parse_num($arg{t});

	if ($arg{v}) {
		printf "kernel_off: 0x%x(%u)\n", $kernel_off, $kernel_off;
		printf "kernel_addr: 0x%x(%u)\n", $kernel_addr, $kernel_addr;
		printf "kernel_len: 0x%x(%u)\n", $kernel_len, $kernel_len;

		printf "rootfs_off: 0x%x(%u)\n", $rootfs_off, $rootfs_off;
		printf "rootfs_addr: 0x%x(%u)\n", $rootfs_addr, $rootfs_addr;
		printf "rootfs_len: 0x%x(%u)\n", $rootfs_len, $rootfs_len;
		printf "rootfs_size: 0x%x(%u)\n", $rootfs_size, $rootfs_size;
	}

	open(FO, ">$arg{o}");
	printf FO "fis init -f\n";
	printf FO "\n";
	printf FO "fconfig boot_script true\n";
	printf FO "fconfig boot_script_data\n";
	printf FO "fis load -b 0x%x -d kernel\n", $load_addr;
	printf FO "exec -c \"noinitrd\" 0x%x\n", $load_addr;
	printf FO "\n";
	printf FO "fconfig boot_script_timeout %d\n", $timeout;
	printf FO "\n";
	printf FO "fis create -o 0x%x -f 0x%x -l 0x%x kernel\n", $kernel_off, $kernel_addr, $kernel_len;
	printf FO "\n";
	printf FO "fis create -o 0x%x -s 0x%x -f 0x%x -l 0x%x rootfs\n", $rootfs_off, $rootfs_size, $rootfs_addr, $rootfs_len;
	printf FO "\n";
	printf FO "reset\n";
	close FO;
}

# MAIN
gen_script();