blob: b144fd35735b326bd39fdf0929de970b1d826b94 [file] [log] [blame]
Andrew Bresticker3537e952015-02-05 13:31:41 -08001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2015 Google, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Andrew Bresticker3537e952015-02-05 13:31:41 -080015 */
16
Andrew Bresticker3537e952015-02-05 13:31:41 -080017#include <arch/mmu.h>
18#include <console/console.h>
19#include <stddef.h>
20#include <stdint.h>
21#include <stdlib.h>
22
23#define MIN_PAGE_SIZE (4 * KiB)
24
25static int add_wired_tlb_entry(uint32_t entrylo0, uint32_t entrylo1,
26 uint32_t entryhi, uint32_t pgsize)
27{
28 uint32_t tlbindex;
29
30 tlbindex = read_c0_wired();
31 if (tlbindex >= get_tlb_size() || tlbindex >= C0_WIRED_MASK) {
32 printk(BIOS_ERR, "Ran out of TLB entries\n");
33 return -1;
34 }
35 write_c0_wired(tlbindex + 1);
36 write_c0_index(tlbindex);
37 write_c0_pagemask(((pgsize / MIN_PAGE_SIZE) - 1) << C0_PAGEMASK_SHIFT);
38 write_c0_entryhi(entryhi);
39 write_c0_entrylo0(entrylo0);
40 write_c0_entrylo1(entrylo1);
41 mtc0_tlbw_hazard();
42 tlb_write_indexed();
43 tlbw_use_hazard();
44
45 return 0;
46}
47
48static uint32_t pick_pagesize(uint32_t start, uint32_t len)
49{
50 uint32_t pgsize, max_pgsize;
51
52 max_pgsize = get_max_pagesize();
53 for (pgsize = max_pgsize;
54 pgsize >= MIN_PAGE_SIZE;
55 pgsize = pgsize / 4) {
56 /*
57 * Each TLB entry maps a pair of virtual pages. To avoid
58 * aliasing, pick the largest page size that is at most
59 * half the size of the region we're trying to map.
60 */
61 if (IS_ALIGNED(start, 2 * pgsize) && (2 * pgsize <= len))
62 break;
63 }
64
65 return pgsize;
66}
67
68/*
69 * Identity map the memory from [start,start+len] in the TLB using the
70 * largest suitable page size so as to conserve TLB entries.
71 */
Ionela Voinescue7a336a2015-07-24 15:00:20 +010072int identity_map(uint32_t start, size_t len, uint32_t coherency)
Andrew Bresticker3537e952015-02-05 13:31:41 -080073{
74 uint32_t pgsize, pfn, entryhi, entrylo0, entrylo1;
75
Ionela Voinescue7a336a2015-07-24 15:00:20 +010076 coherency &= C0_ENTRYLO_COHERENCY_MASK;
Andrew Bresticker3537e952015-02-05 13:31:41 -080077 while (len > 0) {
78 pgsize = pick_pagesize(start, len);
79 entryhi = start;
80 pfn = start >> 12;
Ionela Voinescue7a336a2015-07-24 15:00:20 +010081 entrylo0 = (pfn << C0_ENTRYLO_PFN_SHIFT) | coherency |
Andrew Bresticker3537e952015-02-05 13:31:41 -080082 C0_ENTRYLO_D | C0_ENTRYLO_V | C0_ENTRYLO_G;
83 start += pgsize;
84 len -= MIN(len, pgsize);
85 if (len >= pgsize) {
86 pfn = start >> 12;
87 entrylo1 = (pfn << C0_ENTRYLO_PFN_SHIFT) |
Ionela Voinescue7a336a2015-07-24 15:00:20 +010088 coherency | C0_ENTRYLO_D | C0_ENTRYLO_V |
Andrew Bresticker3537e952015-02-05 13:31:41 -080089 C0_ENTRYLO_G;
90 start += pgsize;
91 len -= MIN(len, pgsize);
92 } else {
93 entrylo1 = 0;
94 }
95 if (add_wired_tlb_entry(entrylo0, entrylo1, entryhi, pgsize))
96 return -1;
97 }
98
99 return 0;
100}