163e158a0SMatt Evans /* 263e158a0SMatt Evans * PPC64 (SPAPR) platform support 363e158a0SMatt Evans * 463e158a0SMatt Evans * Copyright 2011 Matt Evans <matt@ozlabs.org>, IBM Corporation. 563e158a0SMatt Evans * 6d391177aSMatt Evans * Portions of FDT setup borrowed from QEMU, copyright 2010 David Gibson, IBM 7d391177aSMatt Evans * Corporation. 8d391177aSMatt Evans * 963e158a0SMatt Evans * This program is free software; you can redistribute it and/or modify it 1063e158a0SMatt Evans * under the terms of the GNU General Public License version 2 as published 1163e158a0SMatt Evans * by the Free Software Foundation. 1263e158a0SMatt Evans */ 1363e158a0SMatt Evans 141299331aSWill Deacon #include "kvm/fdt.h" 1563e158a0SMatt Evans #include "kvm/kvm.h" 1663e158a0SMatt Evans #include "kvm/util.h" 17d391177aSMatt Evans #include "cpu_info.h" 1863e158a0SMatt Evans 19be76823fSMatt Evans #include "spapr.h" 207295767dSMatt Evans #include "spapr_hvcons.h" 21c481cfd5SMatt Evans #include "spapr_pci.h" 22be76823fSMatt Evans 2363e158a0SMatt Evans #include <linux/kvm.h> 2463e158a0SMatt Evans 2563e158a0SMatt Evans #include <sys/types.h> 2663e158a0SMatt Evans #include <sys/ioctl.h> 2763e158a0SMatt Evans #include <sys/mman.h> 2863e158a0SMatt Evans #include <stdbool.h> 2963e158a0SMatt Evans #include <stdlib.h> 3063e158a0SMatt Evans #include <string.h> 3163e158a0SMatt Evans #include <unistd.h> 3263e158a0SMatt Evans #include <stdio.h> 3363e158a0SMatt Evans #include <fcntl.h> 3463e158a0SMatt Evans #include <asm/unistd.h> 3563e158a0SMatt Evans #include <errno.h> 3663e158a0SMatt Evans 3763e158a0SMatt Evans #include <linux/byteorder.h> 38d391177aSMatt Evans 39d391177aSMatt Evans #define HPT_ORDER 24 4063e158a0SMatt Evans 4163e158a0SMatt Evans #define HUGETLBFS_PATH "/var/lib/hugetlbfs/global/pagesize-16MB/" 4263e158a0SMatt Evans 4363e158a0SMatt Evans static char kern_cmdline[2048]; 4463e158a0SMatt Evans 4563e158a0SMatt Evans struct kvm_ext kvm_req_ext[] = { 46f17e5a37SMatt Evans { DEFINE_KVM_EXT(KVM_CAP_PPC_UNSET_IRQ) }, 47f17e5a37SMatt Evans { DEFINE_KVM_EXT(KVM_CAP_PPC_IRQ_LEVEL) }, 4863e158a0SMatt Evans { 0, 0 } 4963e158a0SMatt Evans }; 5063e158a0SMatt Evans 51*abe3f28aSAlexandru Elisei void kvm__arch_validate_cfg(struct kvm *kvm) 52*abe3f28aSAlexandru Elisei { 53*abe3f28aSAlexandru Elisei } 54*abe3f28aSAlexandru Elisei 55d391177aSMatt Evans static uint32_t mfpvr(void) 56d391177aSMatt Evans { 57d391177aSMatt Evans uint32_t r; 58d391177aSMatt Evans asm volatile ("mfpvr %0" : "=r"(r)); 59d391177aSMatt Evans return r; 60d391177aSMatt Evans } 61d391177aSMatt Evans 6263e158a0SMatt Evans bool kvm__arch_cpu_supports_vm(void) 6363e158a0SMatt Evans { 6463e158a0SMatt Evans return true; 6563e158a0SMatt Evans } 6663e158a0SMatt Evans 6763e158a0SMatt Evans void kvm__init_ram(struct kvm *kvm) 6863e158a0SMatt Evans { 6963e158a0SMatt Evans u64 phys_start, phys_size; 7063e158a0SMatt Evans void *host_mem; 7163e158a0SMatt Evans 7263e158a0SMatt Evans phys_start = 0; 7363e158a0SMatt Evans phys_size = kvm->ram_size; 7463e158a0SMatt Evans host_mem = kvm->ram_start; 7563e158a0SMatt Evans 7663e158a0SMatt Evans /* 7763e158a0SMatt Evans * We put MMIO at PPC_MMIO_START, high up. Make sure that this doesn't 7863e158a0SMatt Evans * crash into the end of RAM -- on PPC64 at least, this is so high 7963e158a0SMatt Evans * (63TB!) that this is unlikely. 8063e158a0SMatt Evans */ 8163e158a0SMatt Evans if (phys_size >= PPC_MMIO_START) 8263e158a0SMatt Evans die("Too much memory (%lld, what a nice problem): " 8363e158a0SMatt Evans "overlaps MMIO!\n", 8463e158a0SMatt Evans phys_size); 8563e158a0SMatt Evans 868f46c736SJean-Philippe Brucker kvm__register_ram(kvm, phys_start, phys_size, host_mem); 8763e158a0SMatt Evans } 8863e158a0SMatt Evans 8963e158a0SMatt Evans void kvm__arch_set_cmdline(char *cmdline, bool video) 9063e158a0SMatt Evans { 9163e158a0SMatt Evans /* We don't need anything unusual in here. */ 9263e158a0SMatt Evans } 9363e158a0SMatt Evans 9463e158a0SMatt Evans /* Architecture-specific KVM init */ 957eff9f49SWanlong Gao void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size) 9663e158a0SMatt Evans { 9763e158a0SMatt Evans int cap_ppc_rma; 98df129a0aSMatt Evans unsigned long hpt; 9963e158a0SMatt Evans 10063e158a0SMatt Evans kvm->ram_size = ram_size; 10163e158a0SMatt Evans 1028cd50b93SMichael Ellerman /* Map "default" hugetblfs path to the standard 16M mount point */ 1038cd50b93SMichael Ellerman if (hugetlbfs_path && !strcmp(hugetlbfs_path, "default")) 10463e158a0SMatt Evans hugetlbfs_path = HUGETLBFS_PATH; 1058cd50b93SMichael Ellerman 1063ebd8e0bSMichael Ellerman kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path, kvm->ram_size); 1078cd50b93SMichael Ellerman 10863e158a0SMatt Evans if (kvm->ram_start == MAP_FAILED) 10963e158a0SMatt Evans die("Couldn't map %lld bytes for RAM (%d)\n", 11063e158a0SMatt Evans kvm->ram_size, errno); 11163e158a0SMatt Evans 11263e158a0SMatt Evans /* FDT goes at top of memory, RTAS just below */ 11342ac24f9SSasha Levin kvm->arch.fdt_gra = kvm->ram_size - FDT_MAX_SIZE; 11463e158a0SMatt Evans /* FIXME: Not all PPC systems have RTAS */ 11542ac24f9SSasha Levin kvm->arch.rtas_gra = kvm->arch.fdt_gra - RTAS_MAX_SIZE; 11663e158a0SMatt Evans madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE); 11763e158a0SMatt Evans 118df129a0aSMatt Evans /* FIXME: SPAPR-PR specific; allocate a guest HPT. */ 119df129a0aSMatt Evans if (posix_memalign((void **)&hpt, (1<<HPT_ORDER), (1<<HPT_ORDER))) 120df129a0aSMatt Evans die("Can't allocate %d bytes for HPT\n", (1<<HPT_ORDER)); 121df129a0aSMatt Evans 12242ac24f9SSasha Levin kvm->arch.sdr1 = ((hpt + 0x3ffffULL) & ~0x3ffffULL) | (HPT_ORDER-18); 123df129a0aSMatt Evans 12442ac24f9SSasha Levin kvm->arch.pvr = mfpvr(); 125d391177aSMatt Evans 12663e158a0SMatt Evans /* FIXME: This is book3s-specific */ 12763e158a0SMatt Evans cap_ppc_rma = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_PPC_RMA); 12863e158a0SMatt Evans if (cap_ppc_rma == 2) 12963e158a0SMatt Evans die("Need contiguous RMA allocation on this hardware, " 13063e158a0SMatt Evans "which is not yet supported."); 131be76823fSMatt Evans 132be76823fSMatt Evans /* Do these before FDT setup, IRQ setup, etc. */ 133be76823fSMatt Evans /* FIXME: SPAPR-specific */ 134be76823fSMatt Evans hypercall_init(); 135be76823fSMatt Evans register_core_rtas(); 1367295767dSMatt Evans /* Now that hypercalls are initialised, register a couple for the console: */ 1377295767dSMatt Evans spapr_hvcons_init(); 138c481cfd5SMatt Evans spapr_create_phb(kvm, "pci", SPAPR_PCI_BUID, 139c481cfd5SMatt Evans SPAPR_PCI_MEM_WIN_ADDR, 140c481cfd5SMatt Evans SPAPR_PCI_MEM_WIN_SIZE, 141c481cfd5SMatt Evans SPAPR_PCI_IO_WIN_ADDR, 142c481cfd5SMatt Evans SPAPR_PCI_IO_WIN_SIZE); 14363e158a0SMatt Evans } 14463e158a0SMatt Evans 145e56e2de7SLai Jiangshan void kvm__arch_delete_ram(struct kvm *kvm) 146e56e2de7SLai Jiangshan { 147e56e2de7SLai Jiangshan munmap(kvm->ram_start, kvm->ram_size); 148e56e2de7SLai Jiangshan } 149e56e2de7SLai Jiangshan 15063e158a0SMatt Evans void kvm__irq_trigger(struct kvm *kvm, int irq) 15163e158a0SMatt Evans { 15263e158a0SMatt Evans kvm__irq_line(kvm, irq, 1); 15363e158a0SMatt Evans kvm__irq_line(kvm, irq, 0); 15463e158a0SMatt Evans } 15563e158a0SMatt Evans 15612c406a8SJonathan Austin void kvm__arch_read_term(struct kvm *kvm) 1577295767dSMatt Evans { 1587295767dSMatt Evans /* FIXME: Should register callbacks to platform-specific polls */ 1597295767dSMatt Evans spapr_hvcons_poll(kvm); 1607295767dSMatt Evans } 1617295767dSMatt Evans 162004f7684SAndre Przywara bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, 163004f7684SAndre Przywara const char *kernel_cmdline) 16463e158a0SMatt Evans { 16563e158a0SMatt Evans void *p; 16663e158a0SMatt Evans void *k_start; 167d0fb441dSAndre Przywara ssize_t filesize; 16863e158a0SMatt Evans 16963e158a0SMatt Evans p = k_start = guest_flat_to_host(kvm, KERNEL_LOAD_ADDR); 17063e158a0SMatt Evans 171d0fb441dSAndre Przywara filesize = read_file(fd_kernel, p, INITRD_LOAD_ADDR - KERNEL_LOAD_ADDR); 172d0fb441dSAndre Przywara if (filesize < 0) { 173d0fb441dSAndre Przywara if (errno == ENOMEM) 174d0fb441dSAndre Przywara die("Kernel overlaps initrd!"); 17563e158a0SMatt Evans 176d0fb441dSAndre Przywara die_perror("kernel read"); 177d0fb441dSAndre Przywara } 178d0fb441dSAndre Przywara pr_info("Loaded kernel to 0x%x (%ld bytes)", KERNEL_LOAD_ADDR, 179d0fb441dSAndre Przywara filesize); 18063e158a0SMatt Evans if (fd_initrd != -1) { 18163e158a0SMatt Evans if (p-k_start > INITRD_LOAD_ADDR) 18263e158a0SMatt Evans die("Kernel overlaps initrd!"); 18363e158a0SMatt Evans 18463e158a0SMatt Evans /* Round up kernel size to 8byte alignment, and load initrd right after. */ 185d0fb441dSAndre Przywara p = guest_flat_to_host(kvm, INITRD_LOAD_ADDR); 18663e158a0SMatt Evans 187d0fb441dSAndre Przywara filesize = read_file(fd_initrd, p, 188d0fb441dSAndre Przywara (kvm->ram_start + kvm->ram_size) - p); 189d0fb441dSAndre Przywara if (filesize < 0) { 190d0fb441dSAndre Przywara if (errno == ENOMEM) 19163e158a0SMatt Evans die("initrd too big to contain in guest RAM.\n"); 192d0fb441dSAndre Przywara die_perror("initrd read"); 193d0fb441dSAndre Przywara } 19463e158a0SMatt Evans 19563e158a0SMatt Evans pr_info("Loaded initrd to 0x%x (%ld bytes)", 196d0fb441dSAndre Przywara INITRD_LOAD_ADDR, filesize); 19742ac24f9SSasha Levin kvm->arch.initrd_gra = INITRD_LOAD_ADDR; 198d0fb441dSAndre Przywara kvm->arch.initrd_size = filesize; 19963e158a0SMatt Evans } else { 20042ac24f9SSasha Levin kvm->arch.initrd_size = 0; 20163e158a0SMatt Evans } 20263e158a0SMatt Evans strncpy(kern_cmdline, kernel_cmdline, 2048); 20363e158a0SMatt Evans kern_cmdline[2047] = '\0'; 20463e158a0SMatt Evans 20563e158a0SMatt Evans return true; 20663e158a0SMatt Evans } 20763e158a0SMatt Evans 208de494ec5SMichael Ellerman struct fdt_prop { 209de494ec5SMichael Ellerman void *value; 210de494ec5SMichael Ellerman int size; 211de494ec5SMichael Ellerman }; 212de494ec5SMichael Ellerman 213de494ec5SMichael Ellerman static void generate_segment_page_sizes(struct kvm_ppc_smmu_info *info, struct fdt_prop *prop) 214de494ec5SMichael Ellerman { 215de494ec5SMichael Ellerman struct kvm_ppc_one_seg_page_size *sps; 216de494ec5SMichael Ellerman int i, j, size; 217de494ec5SMichael Ellerman u32 *p; 218de494ec5SMichael Ellerman 219de494ec5SMichael Ellerman for (size = 0, i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) { 220de494ec5SMichael Ellerman sps = &info->sps[i]; 221de494ec5SMichael Ellerman 222de494ec5SMichael Ellerman if (sps->page_shift == 0) 223de494ec5SMichael Ellerman break; 224de494ec5SMichael Ellerman 225de494ec5SMichael Ellerman /* page shift, slb enc & count */ 226de494ec5SMichael Ellerman size += 3; 227de494ec5SMichael Ellerman 228de494ec5SMichael Ellerman for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) { 229de494ec5SMichael Ellerman if (info->sps[i].enc[j].page_shift == 0) 230de494ec5SMichael Ellerman break; 231de494ec5SMichael Ellerman 232de494ec5SMichael Ellerman /* page shift & pte enc */ 233de494ec5SMichael Ellerman size += 2; 234de494ec5SMichael Ellerman } 235de494ec5SMichael Ellerman } 236de494ec5SMichael Ellerman 237de494ec5SMichael Ellerman if (!size) { 238de494ec5SMichael Ellerman prop->value = NULL; 239de494ec5SMichael Ellerman prop->size = 0; 240de494ec5SMichael Ellerman return; 241de494ec5SMichael Ellerman } 242de494ec5SMichael Ellerman 243de494ec5SMichael Ellerman /* Convert size to bytes */ 244de494ec5SMichael Ellerman prop->size = size * sizeof(u32); 245de494ec5SMichael Ellerman 246de494ec5SMichael Ellerman prop->value = malloc(prop->size); 247de494ec5SMichael Ellerman if (!prop->value) 248de494ec5SMichael Ellerman die_perror("malloc failed"); 249de494ec5SMichael Ellerman 250de494ec5SMichael Ellerman p = (u32 *)prop->value; 251de494ec5SMichael Ellerman for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) { 252de494ec5SMichael Ellerman sps = &info->sps[i]; 253de494ec5SMichael Ellerman 254de494ec5SMichael Ellerman if (sps->page_shift == 0) 255de494ec5SMichael Ellerman break; 256de494ec5SMichael Ellerman 25762a15bd1SBalbir Singh *p++ = cpu_to_be32(sps->page_shift); 25862a15bd1SBalbir Singh *p++ = cpu_to_be32(sps->slb_enc); 259de494ec5SMichael Ellerman 260de494ec5SMichael Ellerman for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) 261de494ec5SMichael Ellerman if (!info->sps[i].enc[j].page_shift) 262de494ec5SMichael Ellerman break; 263de494ec5SMichael Ellerman 26462a15bd1SBalbir Singh *p++ = cpu_to_be32(j); /* count of enc */ 265de494ec5SMichael Ellerman 266de494ec5SMichael Ellerman for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) { 267de494ec5SMichael Ellerman if (!info->sps[i].enc[j].page_shift) 268de494ec5SMichael Ellerman break; 269de494ec5SMichael Ellerman 27062a15bd1SBalbir Singh *p++ = cpu_to_be32(info->sps[i].enc[j].page_shift); 27162a15bd1SBalbir Singh *p++ = cpu_to_be32(info->sps[i].enc[j].pte_enc); 272de494ec5SMichael Ellerman } 273de494ec5SMichael Ellerman } 274de494ec5SMichael Ellerman } 275de494ec5SMichael Ellerman 276d391177aSMatt Evans #define SMT_THREADS 4 277d391177aSMatt Evans 278d391177aSMatt Evans /* 279d391177aSMatt Evans * Set up the FDT for the kernel: This function is currently fairly SPAPR-heavy, 280d391177aSMatt Evans * and whilst most PPC targets will require CPU/memory nodes, others like RTAS 281d391177aSMatt Evans * should eventually be added separately. 282d391177aSMatt Evans */ 283fc935584SMichael Ellerman static int setup_fdt(struct kvm *kvm) 28463e158a0SMatt Evans { 285d391177aSMatt Evans uint64_t mem_reg_property[] = { 0, cpu_to_be64(kvm->ram_size) }; 286d391177aSMatt Evans int smp_cpus = kvm->nrcpus; 287f17e5a37SMatt Evans uint32_t int_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; 288d391177aSMatt Evans char hypertas_prop_kvm[] = "hcall-pft\0hcall-term\0" 289d391177aSMatt Evans "hcall-dabr\0hcall-interrupt\0hcall-tce\0hcall-vio\0" 290015785d4SBalbir Singh "hcall-splpar\0hcall-bulk\0hcall-set-mode"; 291d391177aSMatt Evans int i, j; 292d391177aSMatt Evans char cpu_name[30]; 293d391177aSMatt Evans u8 staging_fdt[FDT_MAX_SIZE]; 2944a75c603SMichael Ellerman struct cpu_info *cpu_info = find_cpu_info(kvm); 295de494ec5SMichael Ellerman struct fdt_prop segment_page_sizes; 29662a15bd1SBalbir Singh u32 segment_sizes_1T[] = {cpu_to_be32(0x1c), cpu_to_be32(0x28), 0xffffffff, 0xffffffff}; 29763e158a0SMatt Evans 29842ac24f9SSasha Levin /* Generate an appropriate DT at kvm->arch.fdt_gra */ 29942ac24f9SSasha Levin void *fdt_dest = guest_flat_to_host(kvm, kvm->arch.fdt_gra); 300d391177aSMatt Evans void *fdt = staging_fdt; 301d391177aSMatt Evans 302d391177aSMatt Evans _FDT(fdt_create(fdt, FDT_MAX_SIZE)); 303d391177aSMatt Evans _FDT(fdt_finish_reservemap(fdt)); 304d391177aSMatt Evans 305d391177aSMatt Evans _FDT(fdt_begin_node(fdt, "")); 306d391177aSMatt Evans 307d391177aSMatt Evans _FDT(fdt_property_string(fdt, "device_type", "chrp")); 308d391177aSMatt Evans _FDT(fdt_property_string(fdt, "model", "IBM pSeries (kvmtool)")); 309d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "#address-cells", 0x2)); 310d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "#size-cells", 0x2)); 311d391177aSMatt Evans 312be76823fSMatt Evans /* RTAS */ 313be76823fSMatt Evans _FDT(fdt_begin_node(fdt, "rtas")); 314be76823fSMatt Evans /* This is what the kernel uses to switch 'We're an LPAR'! */ 315be76823fSMatt Evans _FDT(fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop_kvm, 316be76823fSMatt Evans sizeof(hypertas_prop_kvm))); 31742ac24f9SSasha Levin _FDT(fdt_property_cell(fdt, "linux,rtas-base", kvm->arch.rtas_gra)); 31842ac24f9SSasha Levin _FDT(fdt_property_cell(fdt, "linux,rtas-entry", kvm->arch.rtas_gra)); 31942ac24f9SSasha Levin _FDT(fdt_property_cell(fdt, "rtas-size", kvm->arch.rtas_size)); 320be76823fSMatt Evans /* Now add properties for all RTAS tokens: */ 321be76823fSMatt Evans if (spapr_rtas_fdt_setup(kvm, fdt)) 322be76823fSMatt Evans die("Couldn't create RTAS FDT properties\n"); 323be76823fSMatt Evans 324be76823fSMatt Evans _FDT(fdt_end_node(fdt)); 325be76823fSMatt Evans 326d391177aSMatt Evans /* /chosen */ 327d391177aSMatt Evans _FDT(fdt_begin_node(fdt, "chosen")); 328d391177aSMatt Evans /* cmdline */ 329d391177aSMatt Evans _FDT(fdt_property_string(fdt, "bootargs", kern_cmdline)); 330d391177aSMatt Evans /* Initrd */ 33142ac24f9SSasha Levin if (kvm->arch.initrd_size != 0) { 33242ac24f9SSasha Levin uint32_t ird_st_prop = cpu_to_be32(kvm->arch.initrd_gra); 33342ac24f9SSasha Levin uint32_t ird_end_prop = cpu_to_be32(kvm->arch.initrd_gra + 33442ac24f9SSasha Levin kvm->arch.initrd_size); 335d391177aSMatt Evans _FDT(fdt_property(fdt, "linux,initrd-start", 336d391177aSMatt Evans &ird_st_prop, sizeof(ird_st_prop))); 337d391177aSMatt Evans _FDT(fdt_property(fdt, "linux,initrd-end", 338d391177aSMatt Evans &ird_end_prop, sizeof(ird_end_prop))); 339d391177aSMatt Evans } 3407295767dSMatt Evans 3417295767dSMatt Evans /* 3427295767dSMatt Evans * stdout-path: This is assuming we're using the HV console. Also, the 3437295767dSMatt Evans * address is hardwired until we do a VIO bus. 3447295767dSMatt Evans */ 3457295767dSMatt Evans _FDT(fdt_property_string(fdt, "linux,stdout-path", 3467295767dSMatt Evans "/vdevice/vty@30000000")); 347d391177aSMatt Evans _FDT(fdt_end_node(fdt)); 348d391177aSMatt Evans 349d391177aSMatt Evans /* 350d391177aSMatt Evans * Memory: We don't alloc. a separate RMA yet. If we ever need to 351d391177aSMatt Evans * (CAP_PPC_RMA == 2) then have one memory node for 0->RMAsize, and 352d391177aSMatt Evans * another RMAsize->endOfMem. 353d391177aSMatt Evans */ 354d391177aSMatt Evans _FDT(fdt_begin_node(fdt, "memory@0")); 355d391177aSMatt Evans _FDT(fdt_property_string(fdt, "device_type", "memory")); 356d391177aSMatt Evans _FDT(fdt_property(fdt, "reg", mem_reg_property, 357d391177aSMatt Evans sizeof(mem_reg_property))); 358d391177aSMatt Evans _FDT(fdt_end_node(fdt)); 359d391177aSMatt Evans 360de494ec5SMichael Ellerman generate_segment_page_sizes(&cpu_info->mmu_info, &segment_page_sizes); 361de494ec5SMichael Ellerman 362d391177aSMatt Evans /* CPUs */ 363d391177aSMatt Evans _FDT(fdt_begin_node(fdt, "cpus")); 364d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); 365d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); 366d391177aSMatt Evans 367d391177aSMatt Evans for (i = 0; i < smp_cpus; i += SMT_THREADS) { 36862a15bd1SBalbir Singh int32_t pft_size_prop[] = { 0, cpu_to_be32(HPT_ORDER) }; 369d391177aSMatt Evans uint32_t servers_prop[SMT_THREADS]; 370d391177aSMatt Evans uint32_t gservers_prop[SMT_THREADS * 2]; 371d391177aSMatt Evans int threads = (smp_cpus - i) >= SMT_THREADS ? SMT_THREADS : 372d391177aSMatt Evans smp_cpus - i; 373d391177aSMatt Evans 374d391177aSMatt Evans sprintf(cpu_name, "PowerPC,%s@%d", cpu_info->name, i); 375d391177aSMatt Evans _FDT(fdt_begin_node(fdt, cpu_name)); 376d391177aSMatt Evans sprintf(cpu_name, "PowerPC,%s", cpu_info->name); 377d391177aSMatt Evans _FDT(fdt_property_string(fdt, "name", cpu_name)); 378d391177aSMatt Evans _FDT(fdt_property_string(fdt, "device_type", "cpu")); 379d391177aSMatt Evans 380d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "reg", i)); 38142ac24f9SSasha Levin _FDT(fdt_property_cell(fdt, "cpu-version", kvm->arch.pvr)); 382d391177aSMatt Evans 383d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "dcache-block-size", cpu_info->d_bsize)); 384d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "icache-block-size", cpu_info->i_bsize)); 385d391177aSMatt Evans 386bf4bc902SMichael Ellerman if (cpu_info->tb_freq) 387d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "timebase-frequency", cpu_info->tb_freq)); 388bf4bc902SMichael Ellerman 389d391177aSMatt Evans /* Lies, but safeish lies! */ 390d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "clock-frequency", 0xddbab200)); 391d391177aSMatt Evans 392cb90966cSMichael Ellerman if (cpu_info->mmu_info.slb_size) 393cb90966cSMichael Ellerman _FDT(fdt_property_cell(fdt, "ibm,slb-size", cpu_info->mmu_info.slb_size)); 394cb90966cSMichael Ellerman 395d391177aSMatt Evans /* 396d391177aSMatt Evans * HPT size is hardwired; KVM currently fixes it at 16MB but the 397d391177aSMatt Evans * moment that changes we'll need to read it out of the kernel. 398d391177aSMatt Evans */ 399d391177aSMatt Evans _FDT(fdt_property(fdt, "ibm,pft-size", pft_size_prop, 400d391177aSMatt Evans sizeof(pft_size_prop))); 401d391177aSMatt Evans 402d391177aSMatt Evans _FDT(fdt_property_string(fdt, "status", "okay")); 403d391177aSMatt Evans _FDT(fdt_property(fdt, "64-bit", NULL, 0)); 404d391177aSMatt Evans /* A server for each thread in this core */ 405d391177aSMatt Evans for (j = 0; j < SMT_THREADS; j++) { 406d391177aSMatt Evans servers_prop[j] = cpu_to_be32(i+j); 407d391177aSMatt Evans /* 408d391177aSMatt Evans * Hack borrowed from QEMU, direct the group queues back 409d391177aSMatt Evans * to cpu 0: 410d391177aSMatt Evans */ 411d391177aSMatt Evans gservers_prop[j*2] = cpu_to_be32(i+j); 412d391177aSMatt Evans gservers_prop[j*2 + 1] = 0; 413d391177aSMatt Evans } 414d391177aSMatt Evans _FDT(fdt_property(fdt, "ibm,ppc-interrupt-server#s", 415d391177aSMatt Evans servers_prop, threads * sizeof(uint32_t))); 416d391177aSMatt Evans _FDT(fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", 417d391177aSMatt Evans gservers_prop, 418d391177aSMatt Evans threads * 2 * sizeof(uint32_t))); 419de494ec5SMichael Ellerman 420de494ec5SMichael Ellerman if (segment_page_sizes.value) 421d391177aSMatt Evans _FDT(fdt_property(fdt, "ibm,segment-page-sizes", 422de494ec5SMichael Ellerman segment_page_sizes.value, 423de494ec5SMichael Ellerman segment_page_sizes.size)); 424de494ec5SMichael Ellerman 42538baf777SMichael Ellerman if (cpu_info->mmu_info.flags & KVM_PPC_1T_SEGMENTS) 426d391177aSMatt Evans _FDT(fdt_property(fdt, "ibm,processor-segment-sizes", 42738baf777SMichael Ellerman segment_sizes_1T, sizeof(segment_sizes_1T))); 42838baf777SMichael Ellerman 429d391177aSMatt Evans /* VSX / DFP options: */ 430d391177aSMatt Evans if (cpu_info->flags & CPUINFO_FLAG_VMX) 431d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "ibm,vmx", 432d391177aSMatt Evans (cpu_info->flags & 433d391177aSMatt Evans CPUINFO_FLAG_VSX) ? 2 : 1)); 434d391177aSMatt Evans if (cpu_info->flags & CPUINFO_FLAG_DFP) 435d391177aSMatt Evans _FDT(fdt_property_cell(fdt, "ibm,dfp", 0x1)); 436d391177aSMatt Evans _FDT(fdt_end_node(fdt)); 437d391177aSMatt Evans } 438d391177aSMatt Evans _FDT(fdt_end_node(fdt)); 439d391177aSMatt Evans 440f17e5a37SMatt Evans /* IRQ controller */ 441f17e5a37SMatt Evans _FDT(fdt_begin_node(fdt, "interrupt-controller@0")); 442f17e5a37SMatt Evans 443f17e5a37SMatt Evans _FDT(fdt_property_string(fdt, "device_type", 444f17e5a37SMatt Evans "PowerPC-External-Interrupt-Presentation")); 445f17e5a37SMatt Evans _FDT(fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")); 446f17e5a37SMatt Evans _FDT(fdt_property_cell(fdt, "reg", 0)); 447f17e5a37SMatt Evans _FDT(fdt_property(fdt, "interrupt-controller", NULL, 0)); 448f17e5a37SMatt Evans _FDT(fdt_property(fdt, "ibm,interrupt-server-ranges", 449f17e5a37SMatt Evans int_server_ranges_prop, 450f17e5a37SMatt Evans sizeof(int_server_ranges_prop))); 451f17e5a37SMatt Evans _FDT(fdt_property_cell(fdt, "#interrupt-cells", 2)); 452f17e5a37SMatt Evans _FDT(fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)); 453f17e5a37SMatt Evans _FDT(fdt_property_cell(fdt, "phandle", PHANDLE_XICP)); 454f17e5a37SMatt Evans _FDT(fdt_end_node(fdt)); 455f17e5a37SMatt Evans 4567295767dSMatt Evans /* 4577295767dSMatt Evans * VIO: See comment in linux,stdout-path; we don't yet represent a VIO 4587295767dSMatt Evans * bus/address allocation so addresses are hardwired here. 4597295767dSMatt Evans */ 4607295767dSMatt Evans _FDT(fdt_begin_node(fdt, "vdevice")); 4617295767dSMatt Evans _FDT(fdt_property_cell(fdt, "#address-cells", 0x1)); 4627295767dSMatt Evans _FDT(fdt_property_cell(fdt, "#size-cells", 0x0)); 4637295767dSMatt Evans _FDT(fdt_property_string(fdt, "device_type", "vdevice")); 4647295767dSMatt Evans _FDT(fdt_property_string(fdt, "compatible", "IBM,vdevice")); 4657295767dSMatt Evans _FDT(fdt_begin_node(fdt, "vty@30000000")); 4667295767dSMatt Evans _FDT(fdt_property_string(fdt, "name", "vty")); 4677295767dSMatt Evans _FDT(fdt_property_string(fdt, "device_type", "serial")); 4687295767dSMatt Evans _FDT(fdt_property_string(fdt, "compatible", "hvterm1")); 4697295767dSMatt Evans _FDT(fdt_property_cell(fdt, "reg", 0x30000000)); 4707295767dSMatt Evans _FDT(fdt_end_node(fdt)); 4717295767dSMatt Evans _FDT(fdt_end_node(fdt)); 4727295767dSMatt Evans 473d391177aSMatt Evans /* Finalise: */ 474d391177aSMatt Evans _FDT(fdt_end_node(fdt)); /* Root node */ 475d391177aSMatt Evans _FDT(fdt_finish(fdt)); 476d391177aSMatt Evans 477d391177aSMatt Evans _FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE)); 478c481cfd5SMatt Evans 479c481cfd5SMatt Evans /* PCI */ 480c481cfd5SMatt Evans if (spapr_populate_pci_devices(kvm, PHANDLE_XICP, fdt_dest)) 481c481cfd5SMatt Evans die("Fail populating PCI device nodes"); 482c481cfd5SMatt Evans 48342ac24f9SSasha Levin _FDT(fdt_add_mem_rsv(fdt_dest, kvm->arch.rtas_gra, kvm->arch.rtas_size)); 484d391177aSMatt Evans _FDT(fdt_pack(fdt_dest)); 485de494ec5SMichael Ellerman 486de494ec5SMichael Ellerman free(segment_page_sizes.value); 487fc935584SMichael Ellerman 488fc935584SMichael Ellerman return 0; 48963e158a0SMatt Evans } 490fc935584SMichael Ellerman firmware_init(setup_fdt); 49163e158a0SMatt Evans 49263e158a0SMatt Evans /** 49363e158a0SMatt Evans * kvm__arch_setup_firmware 49463e158a0SMatt Evans */ 495f7f9d02bSCyrill Gorcunov int kvm__arch_setup_firmware(struct kvm *kvm) 49663e158a0SMatt Evans { 497be76823fSMatt Evans /* 498be76823fSMatt Evans * Set up RTAS stub. All it is is a single hypercall: 499be76823fSMatt Evans * 0: 7c 64 1b 78 mr r4,r3 500be76823fSMatt Evans * 4: 3c 60 00 00 lis r3,0 501be76823fSMatt Evans * 8: 60 63 f0 00 ori r3,r3,61440 502be76823fSMatt Evans * c: 44 00 00 22 sc 1 503be76823fSMatt Evans * 10: 4e 80 00 20 blr 504be76823fSMatt Evans */ 50542ac24f9SSasha Levin uint32_t *rtas = guest_flat_to_host(kvm, kvm->arch.rtas_gra); 506be76823fSMatt Evans 50762a15bd1SBalbir Singh rtas[0] = cpu_to_be32(0x7c641b78); 50862a15bd1SBalbir Singh rtas[1] = cpu_to_be32(0x3c600000); 50962a15bd1SBalbir Singh rtas[2] = cpu_to_be32(0x6063f000); 51062a15bd1SBalbir Singh rtas[3] = cpu_to_be32(0x44000022); 51162a15bd1SBalbir Singh rtas[4] = cpu_to_be32(0x4e800020); 51242ac24f9SSasha Levin kvm->arch.rtas_size = 20; 513be76823fSMatt Evans 514be76823fSMatt Evans pr_info("Set up %ld bytes of RTAS at 0x%lx\n", 51542ac24f9SSasha Levin kvm->arch.rtas_size, kvm->arch.rtas_gra); 51663e158a0SMatt Evans 51763e158a0SMatt Evans /* Load SLOF */ 51863e158a0SMatt Evans 519f7f9d02bSCyrill Gorcunov return 0; 52063e158a0SMatt Evans } 5211add9f73SSasha Levin 5221add9f73SSasha Levin int kvm__arch_free_firmware(struct kvm *kvm) 5231add9f73SSasha Levin { 5241add9f73SSasha Levin return 0; 5251add9f73SSasha Levin } 526