diff options
author | Thaminda Edirisooriya <thaminda@google.com> | 2015-08-26 15:28:04 -0700 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2015-09-10 17:23:45 +0000 |
commit | b094583c6fd9d330be28ed6feb1c1140de07ff37 (patch) | |
tree | 57cb30a458d5bc1434ee530f31bc65a192d9e209 /src | |
parent | 87582d1cf2ff09c5ef7f48f41469eaf124e9ffde (diff) |
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>
Diffstat (limited to 'src')
-rw-r--r-- | src/arch/riscv/include/vm.h | 74 | ||||
-rw-r--r-- | src/arch/riscv/virtual_memory.c | 142 |
2 files changed, 216 insertions, 0 deletions
diff --git a/src/arch/riscv/include/vm.h b/src/arch/riscv/include/vm.h new file mode 100644 index 0000000000..f9ffc407da --- /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 0000000000..2095bfa440 --- /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); +} |