riscv-virtual-memory: Add virtual memory setup

Execution in supervisor level code in RISCV requires early setup of
virtual memory. Add initialization calls in
src/arch/riscv/virtual_memory.c to implement the required page table
setup, and helper functions to use when jumping to the payload correctly
in riscv.

Change-Id: I46e080e0ee8dc13277d567dcd4bf0f61a4507b76
Signed-off-by: Thaminda Edirisooriya <thaminda@google.com>
Reviewed-on: http://review.coreboot.org/11369
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
diff --git a/src/arch/riscv/include/vm.h b/src/arch/riscv/include/vm.h
new file mode 100644
index 0000000..f9ffc40
--- /dev/null
+++ b/src/arch/riscv/include/vm.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013, The Regents of the University of California (Regents).
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Regents nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+ * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
+ * OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
+ * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
+ * HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef _VM_H
+#define _VM_H
+
+#include <string.h>
+#include <stdint.h>
+
+#define SUPERPAGE_SIZE ((uintptr_t)(RISCV_PGSIZE << RISCV_PGLEVEL_BITS))
+#define VM_CHOICE VM_SV39
+#define VA_BITS 39
+#define MEGAPAGE_SIZE (SUPERPAGE_SIZE << RISCV_PGLEVEL_BITS)
+
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define PROT_EXEC 4
+
+#define MAP_PRIVATE 0x2
+#define MAP_FIXED 0x10
+#define MAP_ANONYMOUS 0x20
+#define MAP_POPULATE 0x8000
+#define MREMAP_FIXED 0x2
+
+#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1)))
+#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))))
+
+#define supervisor_paddr_valid(start, length) \
+  ((uintptr_t)(start) >= current.first_user_vaddr + current.bias \
+   && (uintptr_t)(start) + (length) < mem_size \
+   && (uintptr_t)(start) + (length) >= (uintptr_t)(start))
+
+typedef uintptr_t pte_t;
+extern pte_t* root_page_table;
+
+void enter_supervisor(void);
+void initVirtualMemory(void);
+
+size_t pte_ppn(pte_t pte);
+pte_t ptd_create(uintptr_t ppn);
+pte_t pte_create(uintptr_t ppn, int prot, int user);
+
+void walk_page_table(void);
+
+void init_vm(uintptr_t virtMemStart, uintptr_t physMemStart, uintptr_t pageTableStart);
+void mstatus_init(void); // need to setup mstatus so we know we have virtual memory
+
+void flush_tlb(void);
+
+#endif
diff --git a/src/arch/riscv/virtual_memory.c b/src/arch/riscv/virtual_memory.c
new file mode 100644
index 0000000..2095bfa
--- /dev/null
+++ b/src/arch/riscv/virtual_memory.c
@@ -0,0 +1,142 @@
+/*
+ * Early initialization code for riscv virtual memory
+ *
+ * Copyright 2015 Google Inc.
+ *
+ * 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; version 2 of
+ * the License.
+ *
+ * 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.
+ */
+
+#include <vm.h>
+#include <arch/encoding.h>
+#include <atomic.h>
+#include <stdint.h>
+#include <console/console.h>
+
+pte_t* root_page_table;
+
+void walk_page_table(void) {
+	// TODO: implement a full walk to make sure memory was set up
+	//const size_t pte_per_page = RISCV_PGSIZE/sizeof(void*);
+	pte_t* t = root_page_table;
+	printk(BIOS_DEBUG, "root_page_table: %p\n", t);
+}
+
+void enter_supervisor(void) {
+	// enter supervisor mode
+	asm volatile("la t0, 1f; csrw mepc, t0; eret; 1:" ::: "t0");
+}
+
+void flush_tlb(void)
+{
+	asm volatile("sfence.vm");
+}
+
+size_t pte_ppn(pte_t pte)
+{
+	return pte >> PTE_PPN_SHIFT;
+}
+
+pte_t ptd_create(uintptr_t ppn)
+{
+	return (ppn << PTE_PPN_SHIFT) | PTE_V | PTE_TYPE_TABLE;
+}
+
+pte_t pte_create(uintptr_t ppn, int prot, int user)
+{
+	pte_t pte = (ppn << PTE_PPN_SHIFT) | PTE_V;
+	if (prot & PROT_WRITE) pte |= PTE_TYPE_URW_SRW;
+	if (prot & PROT_EXEC) pte |= PTE_TYPE_URX_SRX;
+	if (!user) pte |= PTE_TYPE_SR;
+	return pte;
+}
+
+void init_vm(uintptr_t virtMemStart, uintptr_t physMemStart, uintptr_t pageTableStart) {
+	pte_t* sbi_pt = (pte_t*) pageTableStart;
+	memset(sbi_pt, 0, RISCV_PGSIZE);
+	// need to leave room for sbi page
+	uintptr_t memorySize = 0x7F000000; // 0xFFF... - 0xFFFFFFFF81000000 - RISCV_PGSIZE
+
+	// middle page table
+	pte_t* middle_pt = (void*)sbi_pt + RISCV_PGSIZE;
+	size_t num_middle_pts = 2; // 3 level page table, 39 bit virtual address space for now
+
+	// root page table
+	pte_t* root_pt = (void*)middle_pt + num_middle_pts * RISCV_PGSIZE;
+	memset(middle_pt, 0, (num_middle_pts + 1) * RISCV_PGSIZE); // 0's out middle_pt and root_pt
+	for (size_t i = 0; i < num_middle_pts; i++)
+		root_pt[(1<<RISCV_PGLEVEL_BITS)-num_middle_pts+i] = ptd_create(((uintptr_t)middle_pt >> RISCV_PGSHIFT) + i);
+
+	// fill the middle page table
+	for (uintptr_t vaddr = virtMemStart, paddr = physMemStart; paddr < memorySize; vaddr += SUPERPAGE_SIZE, paddr += SUPERPAGE_SIZE) {
+		int l2_shift = RISCV_PGLEVEL_BITS + RISCV_PGSHIFT;
+		size_t l2_idx = (virtMemStart >> l2_shift) & ((1 << RISCV_PGLEVEL_BITS)-1);
+		l2_idx += ((vaddr - virtMemStart) >> l2_shift);
+		middle_pt[l2_idx] = pte_create(paddr >> RISCV_PGSHIFT, PROT_READ|PROT_WRITE|PROT_EXEC, 0);
+	}
+
+	// map SBI at top of vaddr space
+	uintptr_t num_sbi_pages = 1; // only need to map a single page for sbi interface
+	uintptr_t sbiStartAddress = 0x2000; // the start of the sbi mapping
+	uintptr_t sbiAddr = sbiStartAddress;
+	for (uintptr_t i = 0; i < num_sbi_pages; i++) {
+		uintptr_t idx = (1 << RISCV_PGLEVEL_BITS) - num_sbi_pages + i;
+		sbi_pt[idx] = pte_create(sbiAddr >> RISCV_PGSHIFT, PROT_READ|PROT_EXEC, 0);
+		sbiAddr += RISCV_PGSIZE;
+	}
+	pte_t* sbi_pte = middle_pt + ((num_middle_pts << RISCV_PGLEVEL_BITS)-1);
+	*sbi_pte = ptd_create((uintptr_t)sbi_pt >> RISCV_PGSHIFT);
+
+	mb();
+	root_page_table = root_pt;
+	write_csr(sptbr, root_pt);
+}
+
+void initVirtualMemory(void) {
+	printk(BIOS_DEBUG, "Initializing virtual memory...\n");
+	uintptr_t physicalStart = 0x1000000; // TODO: Figure out how to grab this from cbfs
+	uintptr_t virtualStart = 0xffffffff81000000;
+	uintptr_t pageTableStart = 0x1f0000;
+	init_vm(virtualStart, physicalStart, pageTableStart);
+	mb();
+	printk(BIOS_DEBUG, "Finished initializing virtual memory, starting walk...\n");
+	walk_page_table();
+}
+
+void mstatus_init(void)
+{
+	// supervisor support is required
+
+	uintptr_t ms = 0;
+	ms = INSERT_FIELD(ms, MSTATUS_PRV, PRV_M);
+	ms = INSERT_FIELD(ms, MSTATUS_PRV1, PRV_S);
+	ms = INSERT_FIELD(ms, MSTATUS_PRV2, PRV_U);
+	ms = INSERT_FIELD(ms, MSTATUS_IE2, 1);
+	ms = INSERT_FIELD(ms, MSTATUS_VM, VM_CHOICE);
+	ms = INSERT_FIELD(ms, MSTATUS_FS, 3);
+	ms = INSERT_FIELD(ms, MSTATUS_XS, 3);
+	write_csr(mstatus, ms);
+	ms = read_csr(mstatus);
+
+	if (EXTRACT_FIELD(ms, MSTATUS_VM) != VM_CHOICE) {
+		printk(BIOS_DEBUG, "we don't have virtual memory...\n");
+	} else {
+		printk(BIOS_DEBUG, "-----------------------------\n");
+		printk(BIOS_DEBUG, "virtual memory status enabled\n");
+		printk(BIOS_DEBUG, "-----------------------------\n");
+	}
+
+	clear_csr(mip, MIP_MSIP);
+	set_csr(mie, MIP_MSIP);
+}