xref: /kvmtool/powerpc/kvm.c (revision 8f46c736eae508f0973d41618db17cced6b5796c) !
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 
51d391177aSMatt Evans static uint32_t mfpvr(void)
52d391177aSMatt Evans {
53d391177aSMatt Evans 	uint32_t r;
54d391177aSMatt Evans 	asm volatile ("mfpvr %0" : "=r"(r));
55d391177aSMatt Evans 	return r;
56d391177aSMatt Evans }
57d391177aSMatt Evans 
5863e158a0SMatt Evans bool kvm__arch_cpu_supports_vm(void)
5963e158a0SMatt Evans {
6063e158a0SMatt Evans 	return true;
6163e158a0SMatt Evans }
6263e158a0SMatt Evans 
6363e158a0SMatt Evans void kvm__init_ram(struct kvm *kvm)
6463e158a0SMatt Evans {
6563e158a0SMatt Evans 	u64	phys_start, phys_size;
6663e158a0SMatt Evans 	void	*host_mem;
6763e158a0SMatt Evans 
6863e158a0SMatt Evans 	phys_start = 0;
6963e158a0SMatt Evans 	phys_size  = kvm->ram_size;
7063e158a0SMatt Evans 	host_mem   = kvm->ram_start;
7163e158a0SMatt Evans 
7263e158a0SMatt Evans 	/*
7363e158a0SMatt Evans 	 * We put MMIO at PPC_MMIO_START, high up.  Make sure that this doesn't
7463e158a0SMatt Evans 	 * crash into the end of RAM -- on PPC64 at least, this is so high
7563e158a0SMatt Evans 	 * (63TB!) that this is unlikely.
7663e158a0SMatt Evans 	 */
7763e158a0SMatt Evans 	if (phys_size >= PPC_MMIO_START)
7863e158a0SMatt Evans 		die("Too much memory (%lld, what a nice problem): "
7963e158a0SMatt Evans 		    "overlaps MMIO!\n",
8063e158a0SMatt Evans 		    phys_size);
8163e158a0SMatt Evans 
82*8f46c736SJean-Philippe Brucker 	kvm__register_ram(kvm, phys_start, phys_size, host_mem);
8363e158a0SMatt Evans }
8463e158a0SMatt Evans 
8563e158a0SMatt Evans void kvm__arch_set_cmdline(char *cmdline, bool video)
8663e158a0SMatt Evans {
8763e158a0SMatt Evans 	/* We don't need anything unusual in here. */
8863e158a0SMatt Evans }
8963e158a0SMatt Evans 
9063e158a0SMatt Evans /* Architecture-specific KVM init */
917eff9f49SWanlong Gao void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size)
9263e158a0SMatt Evans {
9363e158a0SMatt Evans 	int cap_ppc_rma;
94df129a0aSMatt Evans 	unsigned long hpt;
9563e158a0SMatt Evans 
9663e158a0SMatt Evans 	kvm->ram_size		= ram_size;
9763e158a0SMatt Evans 
988cd50b93SMichael Ellerman 	/* Map "default" hugetblfs path to the standard 16M mount point */
998cd50b93SMichael Ellerman 	if (hugetlbfs_path && !strcmp(hugetlbfs_path, "default"))
10063e158a0SMatt Evans 		hugetlbfs_path = HUGETLBFS_PATH;
1018cd50b93SMichael Ellerman 
1023ebd8e0bSMichael Ellerman 	kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path, kvm->ram_size);
1038cd50b93SMichael Ellerman 
10463e158a0SMatt Evans 	if (kvm->ram_start == MAP_FAILED)
10563e158a0SMatt Evans 		die("Couldn't map %lld bytes for RAM (%d)\n",
10663e158a0SMatt Evans 		    kvm->ram_size, errno);
10763e158a0SMatt Evans 
10863e158a0SMatt Evans 	/* FDT goes at top of memory, RTAS just below */
10942ac24f9SSasha Levin 	kvm->arch.fdt_gra = kvm->ram_size - FDT_MAX_SIZE;
11063e158a0SMatt Evans 	/* FIXME: Not all PPC systems have RTAS */
11142ac24f9SSasha Levin 	kvm->arch.rtas_gra = kvm->arch.fdt_gra - RTAS_MAX_SIZE;
11263e158a0SMatt Evans 	madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE);
11363e158a0SMatt Evans 
114df129a0aSMatt Evans 	/* FIXME:  SPAPR-PR specific; allocate a guest HPT. */
115df129a0aSMatt Evans 	if (posix_memalign((void **)&hpt, (1<<HPT_ORDER), (1<<HPT_ORDER)))
116df129a0aSMatt Evans 		die("Can't allocate %d bytes for HPT\n", (1<<HPT_ORDER));
117df129a0aSMatt Evans 
11842ac24f9SSasha Levin 	kvm->arch.sdr1 = ((hpt + 0x3ffffULL) & ~0x3ffffULL) | (HPT_ORDER-18);
119df129a0aSMatt Evans 
12042ac24f9SSasha Levin 	kvm->arch.pvr = mfpvr();
121d391177aSMatt Evans 
12263e158a0SMatt Evans 	/* FIXME: This is book3s-specific */
12363e158a0SMatt Evans 	cap_ppc_rma = ioctl(kvm->sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_PPC_RMA);
12463e158a0SMatt Evans 	if (cap_ppc_rma == 2)
12563e158a0SMatt Evans 		die("Need contiguous RMA allocation on this hardware, "
12663e158a0SMatt Evans 		    "which is not yet supported.");
127be76823fSMatt Evans 
128be76823fSMatt Evans 	/* Do these before FDT setup, IRQ setup, etc. */
129be76823fSMatt Evans 	/* FIXME: SPAPR-specific */
130be76823fSMatt Evans 	hypercall_init();
131be76823fSMatt Evans 	register_core_rtas();
1327295767dSMatt Evans 	/* Now that hypercalls are initialised, register a couple for the console: */
1337295767dSMatt Evans 	spapr_hvcons_init();
134c481cfd5SMatt Evans 	spapr_create_phb(kvm, "pci", SPAPR_PCI_BUID,
135c481cfd5SMatt Evans 			 SPAPR_PCI_MEM_WIN_ADDR,
136c481cfd5SMatt Evans 			 SPAPR_PCI_MEM_WIN_SIZE,
137c481cfd5SMatt Evans 			 SPAPR_PCI_IO_WIN_ADDR,
138c481cfd5SMatt Evans 			 SPAPR_PCI_IO_WIN_SIZE);
13963e158a0SMatt Evans }
14063e158a0SMatt Evans 
141e56e2de7SLai Jiangshan void kvm__arch_delete_ram(struct kvm *kvm)
142e56e2de7SLai Jiangshan {
143e56e2de7SLai Jiangshan 	munmap(kvm->ram_start, kvm->ram_size);
144e56e2de7SLai Jiangshan }
145e56e2de7SLai Jiangshan 
14663e158a0SMatt Evans void kvm__irq_trigger(struct kvm *kvm, int irq)
14763e158a0SMatt Evans {
14863e158a0SMatt Evans 	kvm__irq_line(kvm, irq, 1);
14963e158a0SMatt Evans 	kvm__irq_line(kvm, irq, 0);
15063e158a0SMatt Evans }
15163e158a0SMatt Evans 
15212c406a8SJonathan Austin void kvm__arch_read_term(struct kvm *kvm)
1537295767dSMatt Evans {
1547295767dSMatt Evans 	/* FIXME: Should register callbacks to platform-specific polls */
1557295767dSMatt Evans 	spapr_hvcons_poll(kvm);
1567295767dSMatt Evans }
1577295767dSMatt Evans 
158004f7684SAndre Przywara bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd,
159004f7684SAndre Przywara 				 const char *kernel_cmdline)
16063e158a0SMatt Evans {
16163e158a0SMatt Evans 	void *p;
16263e158a0SMatt Evans 	void *k_start;
163d0fb441dSAndre Przywara 	ssize_t filesize;
16463e158a0SMatt Evans 
16563e158a0SMatt Evans 	p = k_start = guest_flat_to_host(kvm, KERNEL_LOAD_ADDR);
16663e158a0SMatt Evans 
167d0fb441dSAndre Przywara 	filesize = read_file(fd_kernel, p, INITRD_LOAD_ADDR - KERNEL_LOAD_ADDR);
168d0fb441dSAndre Przywara 	if (filesize < 0) {
169d0fb441dSAndre Przywara 		if (errno == ENOMEM)
170d0fb441dSAndre Przywara 			die("Kernel overlaps initrd!");
17163e158a0SMatt Evans 
172d0fb441dSAndre Przywara 		die_perror("kernel read");
173d0fb441dSAndre Przywara 	}
174d0fb441dSAndre Przywara 	pr_info("Loaded kernel to 0x%x (%ld bytes)", KERNEL_LOAD_ADDR,
175d0fb441dSAndre Przywara 		filesize);
17663e158a0SMatt Evans 	if (fd_initrd != -1) {
17763e158a0SMatt Evans 		if (p-k_start > INITRD_LOAD_ADDR)
17863e158a0SMatt Evans 			die("Kernel overlaps initrd!");
17963e158a0SMatt Evans 
18063e158a0SMatt Evans 		/* Round up kernel size to 8byte alignment, and load initrd right after. */
181d0fb441dSAndre Przywara 		p = guest_flat_to_host(kvm, INITRD_LOAD_ADDR);
18263e158a0SMatt Evans 
183d0fb441dSAndre Przywara 		filesize = read_file(fd_initrd, p,
184d0fb441dSAndre Przywara 			       (kvm->ram_start + kvm->ram_size) - p);
185d0fb441dSAndre Przywara 		if (filesize < 0) {
186d0fb441dSAndre Przywara 			if (errno == ENOMEM)
18763e158a0SMatt Evans 				die("initrd too big to contain in guest RAM.\n");
188d0fb441dSAndre Przywara 			die_perror("initrd read");
189d0fb441dSAndre Przywara 		}
19063e158a0SMatt Evans 
19163e158a0SMatt Evans 		pr_info("Loaded initrd to 0x%x (%ld bytes)",
192d0fb441dSAndre Przywara 			INITRD_LOAD_ADDR, filesize);
19342ac24f9SSasha Levin 		kvm->arch.initrd_gra = INITRD_LOAD_ADDR;
194d0fb441dSAndre Przywara 		kvm->arch.initrd_size = filesize;
19563e158a0SMatt Evans 	} else {
19642ac24f9SSasha Levin 		kvm->arch.initrd_size = 0;
19763e158a0SMatt Evans 	}
19863e158a0SMatt Evans 	strncpy(kern_cmdline, kernel_cmdline, 2048);
19963e158a0SMatt Evans 	kern_cmdline[2047] = '\0';
20063e158a0SMatt Evans 
20163e158a0SMatt Evans 	return true;
20263e158a0SMatt Evans }
20363e158a0SMatt Evans 
204de494ec5SMichael Ellerman struct fdt_prop {
205de494ec5SMichael Ellerman 	void *value;
206de494ec5SMichael Ellerman 	int size;
207de494ec5SMichael Ellerman };
208de494ec5SMichael Ellerman 
209de494ec5SMichael Ellerman static void generate_segment_page_sizes(struct kvm_ppc_smmu_info *info, struct fdt_prop *prop)
210de494ec5SMichael Ellerman {
211de494ec5SMichael Ellerman 	struct kvm_ppc_one_seg_page_size *sps;
212de494ec5SMichael Ellerman 	int i, j, size;
213de494ec5SMichael Ellerman 	u32 *p;
214de494ec5SMichael Ellerman 
215de494ec5SMichael Ellerman 	for (size = 0, i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
216de494ec5SMichael Ellerman 		sps = &info->sps[i];
217de494ec5SMichael Ellerman 
218de494ec5SMichael Ellerman 		if (sps->page_shift == 0)
219de494ec5SMichael Ellerman 			break;
220de494ec5SMichael Ellerman 
221de494ec5SMichael Ellerman 		/* page shift, slb enc & count */
222de494ec5SMichael Ellerman 		size += 3;
223de494ec5SMichael Ellerman 
224de494ec5SMichael Ellerman 		for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
225de494ec5SMichael Ellerman 			if (info->sps[i].enc[j].page_shift == 0)
226de494ec5SMichael Ellerman 				break;
227de494ec5SMichael Ellerman 
228de494ec5SMichael Ellerman 			/* page shift & pte enc */
229de494ec5SMichael Ellerman 			size += 2;
230de494ec5SMichael Ellerman 		}
231de494ec5SMichael Ellerman 	}
232de494ec5SMichael Ellerman 
233de494ec5SMichael Ellerman 	if (!size) {
234de494ec5SMichael Ellerman 		prop->value = NULL;
235de494ec5SMichael Ellerman 		prop->size = 0;
236de494ec5SMichael Ellerman 		return;
237de494ec5SMichael Ellerman 	}
238de494ec5SMichael Ellerman 
239de494ec5SMichael Ellerman 	/* Convert size to bytes */
240de494ec5SMichael Ellerman 	prop->size = size * sizeof(u32);
241de494ec5SMichael Ellerman 
242de494ec5SMichael Ellerman 	prop->value = malloc(prop->size);
243de494ec5SMichael Ellerman 	if (!prop->value)
244de494ec5SMichael Ellerman 		die_perror("malloc failed");
245de494ec5SMichael Ellerman 
246de494ec5SMichael Ellerman 	p = (u32 *)prop->value;
247de494ec5SMichael Ellerman 	for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
248de494ec5SMichael Ellerman 		sps = &info->sps[i];
249de494ec5SMichael Ellerman 
250de494ec5SMichael Ellerman 		if (sps->page_shift == 0)
251de494ec5SMichael Ellerman 			break;
252de494ec5SMichael Ellerman 
25362a15bd1SBalbir Singh 		*p++ = cpu_to_be32(sps->page_shift);
25462a15bd1SBalbir Singh 		*p++ = cpu_to_be32(sps->slb_enc);
255de494ec5SMichael Ellerman 
256de494ec5SMichael Ellerman 		for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++)
257de494ec5SMichael Ellerman 			if (!info->sps[i].enc[j].page_shift)
258de494ec5SMichael Ellerman 				break;
259de494ec5SMichael Ellerman 
26062a15bd1SBalbir Singh 		*p++ = cpu_to_be32(j);	/* count of enc */
261de494ec5SMichael Ellerman 
262de494ec5SMichael Ellerman 		for (j = 0; j < KVM_PPC_PAGE_SIZES_MAX_SZ; j++) {
263de494ec5SMichael Ellerman 			if (!info->sps[i].enc[j].page_shift)
264de494ec5SMichael Ellerman 				break;
265de494ec5SMichael Ellerman 
26662a15bd1SBalbir Singh 			*p++ = cpu_to_be32(info->sps[i].enc[j].page_shift);
26762a15bd1SBalbir Singh 			*p++ = cpu_to_be32(info->sps[i].enc[j].pte_enc);
268de494ec5SMichael Ellerman 		}
269de494ec5SMichael Ellerman 	}
270de494ec5SMichael Ellerman }
271de494ec5SMichael Ellerman 
272d391177aSMatt Evans #define SMT_THREADS 4
273d391177aSMatt Evans 
274d391177aSMatt Evans /*
275d391177aSMatt Evans  * Set up the FDT for the kernel: This function is currently fairly SPAPR-heavy,
276d391177aSMatt Evans  * and whilst most PPC targets will require CPU/memory nodes, others like RTAS
277d391177aSMatt Evans  * should eventually be added separately.
278d391177aSMatt Evans  */
279fc935584SMichael Ellerman static int setup_fdt(struct kvm *kvm)
28063e158a0SMatt Evans {
281d391177aSMatt Evans 	uint64_t 	mem_reg_property[] = { 0, cpu_to_be64(kvm->ram_size) };
282d391177aSMatt Evans 	int 		smp_cpus = kvm->nrcpus;
283f17e5a37SMatt Evans 	uint32_t	int_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
284d391177aSMatt Evans 	char 		hypertas_prop_kvm[] = "hcall-pft\0hcall-term\0"
285d391177aSMatt Evans 		"hcall-dabr\0hcall-interrupt\0hcall-tce\0hcall-vio\0"
286015785d4SBalbir Singh 		"hcall-splpar\0hcall-bulk\0hcall-set-mode";
287d391177aSMatt Evans 	int 		i, j;
288d391177aSMatt Evans 	char 		cpu_name[30];
289d391177aSMatt Evans 	u8		staging_fdt[FDT_MAX_SIZE];
2904a75c603SMichael Ellerman 	struct cpu_info *cpu_info = find_cpu_info(kvm);
291de494ec5SMichael Ellerman 	struct fdt_prop segment_page_sizes;
29262a15bd1SBalbir Singh 	u32 segment_sizes_1T[] = {cpu_to_be32(0x1c), cpu_to_be32(0x28), 0xffffffff, 0xffffffff};
29363e158a0SMatt Evans 
29442ac24f9SSasha Levin 	/* Generate an appropriate DT at kvm->arch.fdt_gra */
29542ac24f9SSasha Levin 	void *fdt_dest = guest_flat_to_host(kvm, kvm->arch.fdt_gra);
296d391177aSMatt Evans 	void *fdt = staging_fdt;
297d391177aSMatt Evans 
298d391177aSMatt Evans 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
299d391177aSMatt Evans 	_FDT(fdt_finish_reservemap(fdt));
300d391177aSMatt Evans 
301d391177aSMatt Evans 	_FDT(fdt_begin_node(fdt, ""));
302d391177aSMatt Evans 
303d391177aSMatt Evans 	_FDT(fdt_property_string(fdt, "device_type", "chrp"));
304d391177aSMatt Evans 	_FDT(fdt_property_string(fdt, "model", "IBM pSeries (kvmtool)"));
305d391177aSMatt Evans 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x2));
306d391177aSMatt Evans 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
307d391177aSMatt Evans 
308be76823fSMatt Evans 	/* RTAS */
309be76823fSMatt Evans 	_FDT(fdt_begin_node(fdt, "rtas"));
310be76823fSMatt Evans 	/* This is what the kernel uses to switch 'We're an LPAR'! */
311be76823fSMatt Evans         _FDT(fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop_kvm,
312be76823fSMatt Evans                            sizeof(hypertas_prop_kvm)));
31342ac24f9SSasha Levin 	_FDT(fdt_property_cell(fdt, "linux,rtas-base", kvm->arch.rtas_gra));
31442ac24f9SSasha Levin 	_FDT(fdt_property_cell(fdt, "linux,rtas-entry", kvm->arch.rtas_gra));
31542ac24f9SSasha Levin 	_FDT(fdt_property_cell(fdt, "rtas-size", kvm->arch.rtas_size));
316be76823fSMatt Evans 	/* Now add properties for all RTAS tokens: */
317be76823fSMatt Evans 	if (spapr_rtas_fdt_setup(kvm, fdt))
318be76823fSMatt Evans 		die("Couldn't create RTAS FDT properties\n");
319be76823fSMatt Evans 
320be76823fSMatt Evans 	_FDT(fdt_end_node(fdt));
321be76823fSMatt Evans 
322d391177aSMatt Evans 	/* /chosen */
323d391177aSMatt Evans 	_FDT(fdt_begin_node(fdt, "chosen"));
324d391177aSMatt Evans 	/* cmdline */
325d391177aSMatt Evans 	_FDT(fdt_property_string(fdt, "bootargs", kern_cmdline));
326d391177aSMatt Evans 	/* Initrd */
32742ac24f9SSasha Levin 	if (kvm->arch.initrd_size != 0) {
32842ac24f9SSasha Levin 		uint32_t ird_st_prop = cpu_to_be32(kvm->arch.initrd_gra);
32942ac24f9SSasha Levin 		uint32_t ird_end_prop = cpu_to_be32(kvm->arch.initrd_gra +
33042ac24f9SSasha Levin 						    kvm->arch.initrd_size);
331d391177aSMatt Evans 		_FDT(fdt_property(fdt, "linux,initrd-start",
332d391177aSMatt Evans 				   &ird_st_prop, sizeof(ird_st_prop)));
333d391177aSMatt Evans 		_FDT(fdt_property(fdt, "linux,initrd-end",
334d391177aSMatt Evans 				   &ird_end_prop, sizeof(ird_end_prop)));
335d391177aSMatt Evans 	}
3367295767dSMatt Evans 
3377295767dSMatt Evans 	/*
3387295767dSMatt Evans 	 * stdout-path: This is assuming we're using the HV console.  Also, the
3397295767dSMatt Evans 	 * address is hardwired until we do a VIO bus.
3407295767dSMatt Evans 	 */
3417295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "linux,stdout-path",
3427295767dSMatt Evans 				 "/vdevice/vty@30000000"));
343d391177aSMatt Evans 	_FDT(fdt_end_node(fdt));
344d391177aSMatt Evans 
345d391177aSMatt Evans 	/*
346d391177aSMatt Evans 	 * Memory: We don't alloc. a separate RMA yet.  If we ever need to
347d391177aSMatt Evans 	 * (CAP_PPC_RMA == 2) then have one memory node for 0->RMAsize, and
348d391177aSMatt Evans 	 * another RMAsize->endOfMem.
349d391177aSMatt Evans 	 */
350d391177aSMatt Evans 	_FDT(fdt_begin_node(fdt, "memory@0"));
351d391177aSMatt Evans 	_FDT(fdt_property_string(fdt, "device_type", "memory"));
352d391177aSMatt Evans 	_FDT(fdt_property(fdt, "reg", mem_reg_property,
353d391177aSMatt Evans 			  sizeof(mem_reg_property)));
354d391177aSMatt Evans 	_FDT(fdt_end_node(fdt));
355d391177aSMatt Evans 
356de494ec5SMichael Ellerman 	generate_segment_page_sizes(&cpu_info->mmu_info, &segment_page_sizes);
357de494ec5SMichael Ellerman 
358d391177aSMatt Evans 	/* CPUs */
359d391177aSMatt Evans 	_FDT(fdt_begin_node(fdt, "cpus"));
360d391177aSMatt Evans 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
361d391177aSMatt Evans 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
362d391177aSMatt Evans 
363d391177aSMatt Evans 	for (i = 0; i < smp_cpus; i += SMT_THREADS) {
36462a15bd1SBalbir Singh 		int32_t pft_size_prop[] = { 0, cpu_to_be32(HPT_ORDER) };
365d391177aSMatt Evans 		uint32_t servers_prop[SMT_THREADS];
366d391177aSMatt Evans 		uint32_t gservers_prop[SMT_THREADS * 2];
367d391177aSMatt Evans 		int threads = (smp_cpus - i) >= SMT_THREADS ? SMT_THREADS :
368d391177aSMatt Evans 			smp_cpus - i;
369d391177aSMatt Evans 
370d391177aSMatt Evans 		sprintf(cpu_name, "PowerPC,%s@%d", cpu_info->name, i);
371d391177aSMatt Evans 		_FDT(fdt_begin_node(fdt, cpu_name));
372d391177aSMatt Evans 		sprintf(cpu_name, "PowerPC,%s", cpu_info->name);
373d391177aSMatt Evans 		_FDT(fdt_property_string(fdt, "name", cpu_name));
374d391177aSMatt Evans 		_FDT(fdt_property_string(fdt, "device_type", "cpu"));
375d391177aSMatt Evans 
376d391177aSMatt Evans 		_FDT(fdt_property_cell(fdt, "reg", i));
37742ac24f9SSasha Levin 		_FDT(fdt_property_cell(fdt, "cpu-version", kvm->arch.pvr));
378d391177aSMatt Evans 
379d391177aSMatt Evans 		_FDT(fdt_property_cell(fdt, "dcache-block-size", cpu_info->d_bsize));
380d391177aSMatt Evans 		_FDT(fdt_property_cell(fdt, "icache-block-size", cpu_info->i_bsize));
381d391177aSMatt Evans 
382bf4bc902SMichael Ellerman 		if (cpu_info->tb_freq)
383d391177aSMatt Evans 			_FDT(fdt_property_cell(fdt, "timebase-frequency", cpu_info->tb_freq));
384bf4bc902SMichael Ellerman 
385d391177aSMatt Evans 		/* Lies, but safeish lies! */
386d391177aSMatt Evans 		_FDT(fdt_property_cell(fdt, "clock-frequency", 0xddbab200));
387d391177aSMatt Evans 
388cb90966cSMichael Ellerman 		if (cpu_info->mmu_info.slb_size)
389cb90966cSMichael Ellerman 			_FDT(fdt_property_cell(fdt, "ibm,slb-size", cpu_info->mmu_info.slb_size));
390cb90966cSMichael Ellerman 
391d391177aSMatt Evans 		/*
392d391177aSMatt Evans 		 * HPT size is hardwired; KVM currently fixes it at 16MB but the
393d391177aSMatt Evans 		 * moment that changes we'll need to read it out of the kernel.
394d391177aSMatt Evans 		 */
395d391177aSMatt Evans 		_FDT(fdt_property(fdt, "ibm,pft-size", pft_size_prop,
396d391177aSMatt Evans 				  sizeof(pft_size_prop)));
397d391177aSMatt Evans 
398d391177aSMatt Evans 		_FDT(fdt_property_string(fdt, "status", "okay"));
399d391177aSMatt Evans 		_FDT(fdt_property(fdt, "64-bit", NULL, 0));
400d391177aSMatt Evans 		/* A server for each thread in this core */
401d391177aSMatt Evans 		for (j = 0; j < SMT_THREADS; j++) {
402d391177aSMatt Evans 			servers_prop[j] = cpu_to_be32(i+j);
403d391177aSMatt Evans 			/*
404d391177aSMatt Evans 			 * Hack borrowed from QEMU, direct the group queues back
405d391177aSMatt Evans 			 * to cpu 0:
406d391177aSMatt Evans 			 */
407d391177aSMatt Evans 			gservers_prop[j*2] = cpu_to_be32(i+j);
408d391177aSMatt Evans 			gservers_prop[j*2 + 1] = 0;
409d391177aSMatt Evans 		}
410d391177aSMatt Evans 		_FDT(fdt_property(fdt, "ibm,ppc-interrupt-server#s",
411d391177aSMatt Evans 				   servers_prop, threads * sizeof(uint32_t)));
412d391177aSMatt Evans 		_FDT(fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
413d391177aSMatt Evans 				  gservers_prop,
414d391177aSMatt Evans 				  threads * 2 * sizeof(uint32_t)));
415de494ec5SMichael Ellerman 
416de494ec5SMichael Ellerman 		if (segment_page_sizes.value)
417d391177aSMatt Evans 			_FDT(fdt_property(fdt, "ibm,segment-page-sizes",
418de494ec5SMichael Ellerman 					  segment_page_sizes.value,
419de494ec5SMichael Ellerman 					  segment_page_sizes.size));
420de494ec5SMichael Ellerman 
42138baf777SMichael Ellerman 		if (cpu_info->mmu_info.flags & KVM_PPC_1T_SEGMENTS)
422d391177aSMatt Evans 			_FDT(fdt_property(fdt, "ibm,processor-segment-sizes",
42338baf777SMichael Ellerman 					  segment_sizes_1T, sizeof(segment_sizes_1T)));
42438baf777SMichael Ellerman 
425d391177aSMatt Evans 		/* VSX / DFP options: */
426d391177aSMatt Evans 		if (cpu_info->flags & CPUINFO_FLAG_VMX)
427d391177aSMatt Evans 			_FDT(fdt_property_cell(fdt, "ibm,vmx",
428d391177aSMatt Evans 					       (cpu_info->flags &
429d391177aSMatt Evans 						CPUINFO_FLAG_VSX) ? 2 : 1));
430d391177aSMatt Evans 		if (cpu_info->flags & CPUINFO_FLAG_DFP)
431d391177aSMatt Evans 			_FDT(fdt_property_cell(fdt, "ibm,dfp", 0x1));
432d391177aSMatt Evans 		_FDT(fdt_end_node(fdt));
433d391177aSMatt Evans 	}
434d391177aSMatt Evans 	_FDT(fdt_end_node(fdt));
435d391177aSMatt Evans 
436f17e5a37SMatt Evans 	/* IRQ controller */
437f17e5a37SMatt Evans 	_FDT(fdt_begin_node(fdt, "interrupt-controller@0"));
438f17e5a37SMatt Evans 
439f17e5a37SMatt Evans 	_FDT(fdt_property_string(fdt, "device_type",
440f17e5a37SMatt Evans 				 "PowerPC-External-Interrupt-Presentation"));
441f17e5a37SMatt Evans 	_FDT(fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"));
442f17e5a37SMatt Evans 	_FDT(fdt_property_cell(fdt, "reg", 0));
443f17e5a37SMatt Evans 	_FDT(fdt_property(fdt, "interrupt-controller", NULL, 0));
444f17e5a37SMatt Evans 	_FDT(fdt_property(fdt, "ibm,interrupt-server-ranges",
445f17e5a37SMatt Evans 			   int_server_ranges_prop,
446f17e5a37SMatt Evans 			   sizeof(int_server_ranges_prop)));
447f17e5a37SMatt Evans 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 2));
448f17e5a37SMatt Evans 	_FDT(fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP));
449f17e5a37SMatt Evans 	_FDT(fdt_property_cell(fdt, "phandle", PHANDLE_XICP));
450f17e5a37SMatt Evans 	_FDT(fdt_end_node(fdt));
451f17e5a37SMatt Evans 
4527295767dSMatt Evans 	/*
4537295767dSMatt Evans 	 * VIO: See comment in linux,stdout-path; we don't yet represent a VIO
4547295767dSMatt Evans 	 * bus/address allocation so addresses are hardwired here.
4557295767dSMatt Evans 	 */
4567295767dSMatt Evans 	_FDT(fdt_begin_node(fdt, "vdevice"));
4577295767dSMatt Evans 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x1));
4587295767dSMatt Evans 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x0));
4597295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "device_type", "vdevice"));
4607295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "compatible", "IBM,vdevice"));
4617295767dSMatt Evans 	_FDT(fdt_begin_node(fdt, "vty@30000000"));
4627295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "name", "vty"));
4637295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "device_type", "serial"));
4647295767dSMatt Evans 	_FDT(fdt_property_string(fdt, "compatible", "hvterm1"));
4657295767dSMatt Evans 	_FDT(fdt_property_cell(fdt, "reg", 0x30000000));
4667295767dSMatt Evans 	_FDT(fdt_end_node(fdt));
4677295767dSMatt Evans 	_FDT(fdt_end_node(fdt));
4687295767dSMatt Evans 
469d391177aSMatt Evans 	/* Finalise: */
470d391177aSMatt Evans 	_FDT(fdt_end_node(fdt)); /* Root node */
471d391177aSMatt Evans 	_FDT(fdt_finish(fdt));
472d391177aSMatt Evans 
473d391177aSMatt Evans 	_FDT(fdt_open_into(fdt, fdt_dest, FDT_MAX_SIZE));
474c481cfd5SMatt Evans 
475c481cfd5SMatt Evans 	/* PCI */
476c481cfd5SMatt Evans 	if (spapr_populate_pci_devices(kvm, PHANDLE_XICP, fdt_dest))
477c481cfd5SMatt Evans 		die("Fail populating PCI device nodes");
478c481cfd5SMatt Evans 
47942ac24f9SSasha Levin 	_FDT(fdt_add_mem_rsv(fdt_dest, kvm->arch.rtas_gra, kvm->arch.rtas_size));
480d391177aSMatt Evans 	_FDT(fdt_pack(fdt_dest));
481de494ec5SMichael Ellerman 
482de494ec5SMichael Ellerman 	free(segment_page_sizes.value);
483fc935584SMichael Ellerman 
484fc935584SMichael Ellerman 	return 0;
48563e158a0SMatt Evans }
486fc935584SMichael Ellerman firmware_init(setup_fdt);
48763e158a0SMatt Evans 
48863e158a0SMatt Evans /**
48963e158a0SMatt Evans  * kvm__arch_setup_firmware
49063e158a0SMatt Evans  */
491f7f9d02bSCyrill Gorcunov int kvm__arch_setup_firmware(struct kvm *kvm)
49263e158a0SMatt Evans {
493be76823fSMatt Evans 	/*
494be76823fSMatt Evans 	 * Set up RTAS stub.  All it is is a single hypercall:
495be76823fSMatt Evans 	 *  0:   7c 64 1b 78     mr      r4,r3
496be76823fSMatt Evans 	 *  4:   3c 60 00 00     lis     r3,0
497be76823fSMatt Evans 	 *  8:   60 63 f0 00     ori     r3,r3,61440
498be76823fSMatt Evans 	 *  c:   44 00 00 22     sc      1
499be76823fSMatt Evans 	 * 10:   4e 80 00 20     blr
500be76823fSMatt Evans 	 */
50142ac24f9SSasha Levin 	uint32_t *rtas = guest_flat_to_host(kvm, kvm->arch.rtas_gra);
502be76823fSMatt Evans 
50362a15bd1SBalbir Singh 	rtas[0] = cpu_to_be32(0x7c641b78);
50462a15bd1SBalbir Singh 	rtas[1] = cpu_to_be32(0x3c600000);
50562a15bd1SBalbir Singh 	rtas[2] = cpu_to_be32(0x6063f000);
50662a15bd1SBalbir Singh 	rtas[3] = cpu_to_be32(0x44000022);
50762a15bd1SBalbir Singh 	rtas[4] = cpu_to_be32(0x4e800020);
50842ac24f9SSasha Levin 	kvm->arch.rtas_size = 20;
509be76823fSMatt Evans 
510be76823fSMatt Evans 	pr_info("Set up %ld bytes of RTAS at 0x%lx\n",
51142ac24f9SSasha Levin 		kvm->arch.rtas_size, kvm->arch.rtas_gra);
51263e158a0SMatt Evans 
51363e158a0SMatt Evans 	/* Load SLOF */
51463e158a0SMatt Evans 
515f7f9d02bSCyrill Gorcunov 	return 0;
51663e158a0SMatt Evans }
5171add9f73SSasha Levin 
5181add9f73SSasha Levin int kvm__arch_free_firmware(struct kvm *kvm)
5191add9f73SSasha Levin {
5201add9f73SSasha Levin 	return 0;
5211add9f73SSasha Levin }
522