/* * Copyright (c) 2012, ARM Limited. All rights reserved. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. */ #include "virt_helpers.h" #include "virtualisor.h" #include "events.h" #include "misc.h" #include "cache_geom.h" #include "mem_trap.h" #include "gic_registers.h" virt_reg_data host_virt_regs[NUM_CPUS]; reg_trap_data host_trap_regs[NUM_CPUS]; cache_geometry host_cache_geometry[NUM_CPUS]; cache_geometry target_cache_geometry[NUM_CPUS]; /* Cache geometry differences for each cpu at each level */ cache_diff cache_delta[NUM_CPUS][MAX_CACHE_LEVELS]; static mem_trap_data svgic_distif_trap __attribute__ ((section("s2_trap_section"))) = { 0, 0x0, 0x0, 0x0, 0x0, 0x0,}; /* * Flags which indicate whether the cpu independent * functionality of the Virtualisor has been setup * on both the host and target clusters. */ static unsigned virt_init[NUM_CPUS]; /* * Detect the type of dual cluster system we are, read * our cpu type and then use the KFS_ID register to * return the type of cpu on the other cluster. */ unsigned find_sibling_cpu() { unsigned cpu_no = PART_NO(read_midr()); switch (DC_SYSTYPE) { case A15_A15: if (cpu_no == A15) return cpu_no; break; case A7_A15: case A15_A7: if (cpu_no == A15) return A7; else if (cpu_no == A7) return A15; else break; } printf("Unsupported Dual cluster system : 0x%x\n", DC_SYSTYPE); panic(); return 0; } void SetupVirtualisor(unsigned first_cpu) { unsigned rc = 0, cpu_id = read_cpuid(), cpu_no = PART_NO(read_midr()); unsigned vd_len = 0, index = 0, cluster_id = read_clusterid(); virt_descriptor *vd_array = &virt_desc_section$$Base; unsigned (*handler) (unsigned, unsigned) = 0x0, sibling; unsigned sibling_cpuid = 0, abs_cpuid = 0; if (!switcher) { sibling_cpuid = abs_cpuid(cpu_id, !cluster_id); abs_cpuid = abs_cpuid(cpu_id, cluster_id); } /* Find our brother from another mother */ sibling = find_sibling_cpu(); /* * Do the generic trap setup */ if (virt_init[cpu_id] == FALSE) { /* * In the "always-on" configuration, both clusters have * ensure that the L2CTLR register includes the cpu count * of both the clusters while reporting the number of * secondary cpus. So setup the necessary trap. */ if (!switcher) { /* * Enable traps to CRn = 9 cp15 space */ write_hstr(read_hstr() | (1 << 9)); } /* * Cache geometry of each cpu on the host cluster needs * to be virtualised if the cpu type is different from * that on the target cluster. This can be done generic- * ally. */ if (cpu_no != sibling) { rc = map_cache_geometries(&host_cache_geometry[cpu_id], &target_cache_geometry [cpu_id], &cache_delta[cpu_id][0]); if (rc) { printf("%s: Failed to map cache geometries \n", __FUNCTION__); rc = 1; goto out; } } /* * If the two clusters have different cpu types, then the * target saves its midr and the host uses the value to * virtualise its midr. * mpidr is virtualised on the host cluster whether we are * running "always on" or "switching". The latter cares * about the cluster id while the former cares about the * cpu ids as well. */ if (cluster_id != host_cluster) { host_virt_regs[cpu_id].mpidr = read_mpidr(); if (cpu_no != sibling) host_virt_regs[cpu_id].midr = read_midr(); if (!switcher) { /* * Send a signal to the host to indicate * that the regs is ready to be read. The * cpu id is the absolute cpu number across * clusters. */ set_event(VID_REGS_DONE, sibling_cpuid); } } else { if (!switcher) { /* * Wait for the target to read its regs * before using them. */ wait_for_event(VID_REGS_DONE, abs_cpuid); reset_event(VID_REGS_DONE, abs_cpuid); /* * Add number of cpus in the target cluster to * the cpuid of this cpu. */ host_virt_regs[cpu_id].mpidr += CLUSTER_CPU_COUNT(!host_cluster); } write_vmpidr(host_virt_regs[cpu_id].mpidr); if (cpu_no != sibling) write_vmidr(host_virt_regs[cpu_id].midr); } if (cluster_id == host_cluster) { /* * Assuming that with the switcher, the host always * runs after the target. So, if we are here then * the target must have completed its initialisation * * In the other case, if we are here after exchanging * the events above, then the target has finished * initialising. */ virt_init[cpu_id] = 1; } } else { if (switcher) RestoreVirtualisor(first_cpu); } /* * Do the cpu specific initialisation (if any) */ vd_len = (unsigned)&virt_desc_section$$Length; for (index = 0; index < (vd_len / sizeof(virt_descriptor)); index++) { if (cpu_no == vd_array[index].cpu_no) { /* If not initialised then setup else restore */ if (vd_array[index].init[cpu_id] == 0) handler = vd_array[index].trap_setup; else handler = vd_array[index].trap_restore; if (handler) { rc = handler(first_cpu, sibling); if (rc) { printf("%s: failed on cpu%d \n", __FUNCTION__, cpu_no); goto out; } } } } out: if (rc) { printf("%s: Failed : Cpu%d : Host=0x%x : Target=0x%x\n ", __FUNCTION__, cpu_id, cpu_no, sibling); panic(); } return; }