summaryrefslogtreecommitdiffstats
path: root/arch/hexagon/kernel/head.S
blob: d859402c73bac00251cfde0109ce6a42bab7bf72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
 * Early kernel startup code for Hexagon
 *
 * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/asm-offsets.h>
#include <asm/mem-layout.h>
#include <asm/vm_mmu.h>
#include <asm/page.h>

	__INIT
ENTRY(stext)
	/*
	 * VMM will already have set up true vector page, MMU, etc.
	 * To set up initial kernel identity map, we have to pass
	 * the VMM a pointer to some canonical page tables. In
	 * this implementation, we're assuming that we've got
	 * them precompiled. Generate value in R24, as we'll need
	 * it again shortly.
	 */
	r24.L = #LO(swapper_pg_dir)
	r24.H = #HI(swapper_pg_dir)

	/*
	 * Symbol is kernel segment address, but we need
	 * the logical/physical address.
	 */
	r24 = asl(r24, #2)
	r24 = lsr(r24, #2)

	r0 = r24

	/*
	 * Initialize a 16MB PTE to make the virtual and physical
	 * addresses where the kernel was loaded be identical.
	 */
#define PTE_BITS ( __HVM_PTE_R | __HVM_PTE_W | __HVM_PTE_X	\
		  | __HEXAGON_C_WB_L2 << 6			\
		  | __HVM_PDE_S_4MB)

	r1 = pc
	r2.H = #0xffc0
	r2.L = #0x0000
	r1 = and(r1,r2)		/* round PC to 4MB boundary	*/
	r2 = lsr(r1, #22)	/* 4MB page number		*/
	r2 = asl(r2, #2)	/* times sizeof(PTE) (4bytes)	*/
	r0 = add(r0,r2)		/* r0 = address of correct PTE	*/
	r2 = #PTE_BITS
	r1 = add(r1,r2)		/* r1 = 4MB PTE for the first entry	*/
	r2.h = #0x0040
	r2.l = #0x0000		/* 4MB	*/
	memw(r0 ++ #4) = r1
	r1 = add(r1, r2)
	memw(r0 ++ #4) = r1

	r0 = r24

	/*
	 * The subroutine wrapper around the virtual instruction touches
	 * no memory, so we should be able to use it even here.
	 */
	call	__vmnewmap;

	/*  Jump into virtual address range.  */

	r31.h = #hi(__head_s_vaddr_target)
	r31.l = #lo(__head_s_vaddr_target)
	jumpr r31

	/*  Insert trippy space effects.  */

__head_s_vaddr_target:
	/*
	 * Tear down VA=PA translation now that we are running
	 * in the desgnated kernel segments.
	 */
	r0 = #__HVM_PDE_S_INVALID
	r1 = r24
	loop0(1f,#0x100)
1:
	{
		memw(R1 ++ #4) = R0
	}:endloop0

	r0 = r24
	call __vmnewmap

	/*  Go ahead and install the trap0 return so angel calls work  */
	r0.h = #hi(_K_provisional_vec)
	r0.l = #lo(_K_provisional_vec)
	call __vmsetvec

	/*
	 * OK, at this point we should start to be much more careful,
	 * we're going to enter C code and start touching memory
	 * in all sorts of places.
	 * This means:
	 *      SGP needs to be OK
	 *	Need to lock shared resources
	 *	A bunch of other things that will cause
	 * 	all kinds of painful bugs
	 */

	/*
	 * Stack pointer should be pointed at the init task's
	 * thread stack, which should have been declared in arch/init_task.c.
	 * So uhhhhh...
	 * It's accessible via the init_thread_union, which is a union
	 * of a thread_info struct and a stack; of course, the top
	 * of the stack is not for you.  The end of the stack
	 * is simply init_thread_union + THREAD_SIZE.
	 */

	{r29.H = #HI(init_thread_union); r0.H = #HI(_THREAD_SIZE); }
	{r29.L = #LO(init_thread_union); r0.L = #LO(_THREAD_SIZE); }

	/*  initialize the register used to point to current_thread_info */
	/*  Fixme:  THREADINFO_REG can't be R2 because of that memset thing. */
	{r29 = add(r29,r0); THREADINFO_REG = r29; }

	/*  Hack:  zero bss; */
	{ r0.L = #LO(__bss_start);  r1 = #0; r2.l = #LO(__bss_stop); }
	{ r0.H = #HI(__bss_start);           r2.h = #HI(__bss_stop); }

	r2 = sub(r2,r0);
	call memset;

	/* Time to make the doughnuts.   */
	call start_kernel

	/*
	 * Should not reach here.
	 */
1:
	jump 1b

.p2align PAGE_SHIFT
ENTRY(external_cmdline_buffer)
        .fill _PAGE_SIZE,1,0

.data
.p2align PAGE_SHIFT
ENTRY(empty_zero_page)
        .fill _PAGE_SIZE,1,0