/* $NetBSD: locore.S,v 1.47 2025/10/12 04:08:26 thorpej Exp $ */

/*-
 * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Matt Thomas of 3am Software Foundry, and by Nick Hudson.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "opt_console.h"
#include "opt_multiprocessor.h"
#include "opt_riscv_debug.h"

#include <machine/asm.h>
#include "assym.h"

#define BOOT_AP_STACKSIZE	1024	/* size of temporary stack for APs */
#define NBBY_SHIFT		3	/* log2(8 bits per byte) */

#define PRINTS(string)		\
	call	locore_prints	; \
	.asciz string		; \
	.align 3		; \


#if defined(VERBOSE_INIT_RISCV)

#define VPRINTS(string)		\
	call	locore_prints	; \
	.asciz string		; \
	.align 3		; \

#define VPRINTX(regno)		\
	mv	a0, regno	; \
	call	locore_printx

#define VPRINTXNL(regno)	\
	mv	a0, regno	; \
	call	locore_printxnl

/* Need to turn relaxation off for VPRINTS */
	.option norelax

#else
#define VPRINTS(string)		/* nothing */
#define VPRINTX(regno)		/* nothing */
#define VPRINTXNL(regno)	/* nothing */
#endif

#if VM_MIN_KERNEL_ADDRESS != VM_KERNEL_BASE
#error VM_MIN_KERNEL_ADDRESS assumed to match VM_KERNEL_BASE
#endif

/*
 * Entry point where.
 *    a0 is hartid
 *    a1 is pointer to dtb (PA)
 */
ENTRY_NP(start)
	csrw	sie, zero		// disable interrupts
	csrw	sip, zero		// clear any pending

	li	s0, SR_FS
	csrc	sstatus, s0		// disable FP

	mv	s10, a0			// copy hartid
	mv	s11, a1			// copy dtb PA

	/* set the stack pointer for boot */
	PTR_LA	t0, _C_LABEL(bootstk)
	mv	sp, t0

	VPRINTS("\n------------\nNetBSD start\n\n")
	VPRINTS("sp:      ")
	VPRINTXNL(sp)

	VPRINTS("pc:      ")
	auipc	a0, 0
	VPRINTXNL(a0)

	VPRINTS("hart:    ")
	VPRINTXNL(s10)

	VPRINTS("dtb:     ")
	VPRINTXNL(s11)

	/*
	 * Calculate the difference between the VA and PA for start and
	 * keep in s8.  Store this in kern_vtopdiff once the MMU is on.
	 */
	PTR_LA	t0, start
	PTR_L	s8, .Lstart

	sub	s8, s8, t0

	PTR_LA	s5, _C_LABEL(lwp0uspace)
	PTR_LA	s6, _C_LABEL(bootstk)

	/*
	 * Before we construct the initial MMU tables, we need to know
	 * if there are any extra PTE bits that we need to include.
	 * (Grumble, mumble, T-Head XMAE).
	 *
	 * We'll push space for 2 PTE prototypes, and fill them with
	 * zeros for now.  sp[0] is for "regular memory", sp[1] is
	 * for "devices".
	 */
	addi	sp, sp, -(2 * SZREG)
	REG_S	zero, 0(sp)
	REG_S	zero, SZREG(sp)

	/* Get the CPU vendor ID, which requires an SBI call. */
	VPRINTS("vendor:  ")
	li	a7, SBI_EID_BASE
	li	a6, SBI_FID_BASE_GETMVENDORID
	ecall
	VPRINTXNL(a1)

#ifdef _LP64
	li	a0, CPU_VENDOR_THEAD
	bne	a0, a1, 9f		/* not T-Head, skip */
	csrr	a1, 0x5c0		/* read T-Head TX.SXSTATUS reg */
	li	a0, TX_SXSTATUS_MAEE	/* is MAEE bit set? */
	and	a0, a0, a1
	beqz	a0, .Lpdetab		/* nope, carry on */
	VPRINTS("extsn:   T-Head XMAE\r\n")
	li	a0, PTE_XMAE_PMA
	REG_S	a0, 0(sp)
	li	a0, PTE_XMAE_IO
	REG_S	a0, SZREG(sp)
	j	.Lpdetab		/* done. */
9:
#endif /* _LP64 */

	/* Next vendor quirk check goes here. */

.Lpdetab:
	/*
	 * Our load address is not fixed, but our VA is.  We need to construct
	 * an initial PDETAB.
	 *
	 * The space for the initial page table is included in the kernel
	 * .bss size calculation so we know the space exists.
	 */

	li	a1, 0
	PTR_LA	s2, _C_LABEL(l1_pte)
	mv	s4, s2			// last page table
#ifdef _LP64
	PTR_LA	s3, _C_LABEL(l2_pte)	// s3 = second PDE page (RV64 only)
	mv	s4, s3			// last page table
#ifdef notyet
	PTR_LA	s4, _C_LABEL(l3_pte)
#endif
#endif
	PTR_LA	s7, _C_LABEL(mmutables_end)


	// s2	L1 PDE (SV32:4MiB megapages, SV{39,48}: 2MiB megapages)
	// s3	L2 PDE (_LP64 SV39 only)
	// s5	lwp0uspace
	// s6	bootstk
	// s7   end of memory to clear

	VPRINTS("l1:      ")
	VPRINTXNL(s2)
#ifdef _LP64
	VPRINTS("l2:      ")
	VPRINTXNL(s3)
#endif

	VPRINTS("uspace:  ")
	VPRINTXNL(s5)
	VPRINTS("bootstk: ")
	VPRINTXNL(s6)

	VPRINTS("vtopdiff:")
	VPRINTXNL(s8)

	VPRINTS("\n\r")

	VPRINTS("bss:     ")
	PTR_LA	a0, _C_LABEL(__bss_start)
	VPRINTX(a0)
	VPRINTS(" - ")
	VPRINTXNL(s7)

	VPRINTS("\n\r")

	// a0	start of memory to clear
	// a1	end of memory to clear
	PTR_LA	a0, _C_LABEL(__bss_start)
	mv	a1, s7

	call	clear_bss		// zero through kernel_end (inc. stack)

	li	s7, PTE_V		// page table pointer {X,W,R} = {0,0,0}

	// We allocated the kernel first PDE page so let's insert in the
	// page table.

	// Need to setup tables so that for
	// sv32 : s2
	// sv39 : s3 -> s2

#ifdef _LP64
	VPRINTS("l2pde:   ")
	srli	t0, s2, (PGSHIFT - PTE_PPN_SHIFT)
	or	t0, t0, s7		// Assumes s2[11:0] == 0
#if ((VM_MIN_KERNEL_ADDRESS >> XSEGSHIFT) & (NPDEPG - 1)) * SZREG
	li	t1, ((VM_MIN_KERNEL_ADDRESS >> XSEGSHIFT) & (NPDEPG - 1)) * SZREG
	add	t1, t1, s3
	REG_S	t0, 0(t1)

	VPRINTX(t1)
#else
	REG_S	t0, 0(s3)

	VPRINTX(s3)
#endif

	VPRINTS(":  ")
	VPRINTXNL(t0)
	VPRINTS("\n\r")
#endif // _LP64

	// kernel VA
	li	t1,  ((VM_MIN_KERNEL_ADDRESS >> SEGSHIFT) & (NPDEPG - 1)) * SZREG
	add	s9, s2, t1

#if PGSHIFT < PTE_PPN_SHIFT
#error Code assumes PGSHIFT is greater than PTE_PPN_SHIFT
#endif

	li	s5, (VM_KERNEL_SIZE >> SEGSHIFT)		// # of megapages
	li	s6, (NBSEG >> (PGSHIFT - PTE_PPN_SHIFT))	// load for ease
	li	s7, PTE_KERN | PTE_HARDWIRED | PTE_R | PTE_W | PTE_X
	REG_L	a0, 0(sp)
	or	s7, s7, a0

	//
	// Fill in the PDEs for kernel.
	//
	PTR_LA	s0, start
	srli	s0, s0, SEGSHIFT	// round down to NBSEG, and shift in
	slli	s0, s0, (SEGSHIFT - PGSHIFT + PTE_PPN_SHIFT)	// ... to PPN
	or	s0, s0, s7
1:
	VPRINTS("kern:    ")
	VPRINTX(s9)
	VPRINTS(":  ")
	VPRINTXNL(s0)

	REG_S	s0, 0(s9)		// store PDE
	add	s0, s0, s6		// advance PA in PDE to next segment
	add	s9, s9, SZREG		// advance to next PDE slot
	addi	s5, s5, -1		// count down segment
	bnez	s5, 1b			// loop if more

	// DTB VA
	li	t1,  ((VM_KERNEL_DTB_BASE >> SEGSHIFT) & (NPDEPG - 1)) * SZREG
	add	s9, s2, t1

	li	s7, PTE_KERN | PTE_HARDWIRED | PTE_R | PTE_W
	REG_L	a0, 0(sp)
	or	s7, s7, a0

	//
	// Fill in the PDE for the DTB. Only do one - if any more are required
	// they will be mapped in later.
	//
	mv	s0, s11
	srli	s0, s0, SEGSHIFT	// round down to NBSEG, and shift in
	slli	s0, s0, (SEGSHIFT - PGSHIFT + PTE_PPN_SHIFT)	// ... to PPN
	or	s0, s0, s7

	VPRINTS("dtb:     ")
	VPRINTX(s9)
	VPRINTS(":  ")
	VPRINTXNL(s0)

	REG_S	s0, 0(s9)

#ifdef CONSADDR
	li	s7, PTE_KERN | PTE_HARDWIRED | PTE_R | PTE_W
	REG_L	a0, SZREG(sp)
	or	s7, s7, a0
	li	t1,  ((VM_KERNEL_IO_BASE >> SEGSHIFT) & (NPDEPG - 1)) * SZREG
	add	s9, s2, t1

	// Fill in the PDE for CONSADDR.
	PTR_L	t0, .Lconsaddr
	mv	s0, t0
	srli	s0, s0, SEGSHIFT	// round down to NBSEG, and shift in
	slli	s0, s0, (SEGSHIFT - PGSHIFT + PTE_PPN_SHIFT)	// ... to PPN
	or	s0, s0, s7

	VPRINTS("cons:    ")
	VPRINTX(s9)
	VPRINTS(":  ")
	VPRINTXNL(s0)

	REG_S	s0, 0(s9)
#endif

	/* Pop the PTE prototypes pushed way up above. */
	addi	sp, sp, (2 * SZREG)

	li	a0, 'P'
	call	_C_LABEL(uartputc)

	/* Set supervisor trap vector base register */
	PTR_LA	t0, vstart
	add	t0, t0, s8
	csrw	stvec, t0

	/* Set supervisor address translation and protection register */
	srli	t1, s4, PGSHIFT
#ifdef _LP64
	li	t0, SATP_MODE_SV39
#else
	li	t0, SATP_MODE_SV32
#endif
	or	t0, t0, t1
	sfence.vma
	csrw	satp, t0

	.align 2
	.global vstart
vstart:
	// MMU is on!
	csrw	sscratch, zero		// zero in sscratch to mark kernel

#ifdef CONSADDR
	add	sp, sp, s8
#endif
	li	a0, 'M'
	call	_C_LABEL(uartputc)	// uartputs doesn't use stack
	li	a0, '\n'
	call	_C_LABEL(uartputc)	// uartputs doesn't use stack
	li	a0, '\r'
	call	_C_LABEL(uartputc)	// uartputs doesn't use stack

	PTR_LA	tp, _C_LABEL(lwp0)	// put curlwp in tp

	/* Set supervisor trap vector base register */
	PTR_LA	a0, _C_LABEL(cpu_exception_handler)
	csrw	stvec, a0

	PTR_LA	t0, bootstk		// top of lwp0uspace
	PTR_S	t0, L_PCB(tp)		// set uarea of lwp (already zeroed)
	addi	sp, t0, -TF_LEN		// switch to new stack
	PTR_S	sp, L_MD_UTF(tp)	// store pointer to empty trapframe

	PTR_LA	t1, _C_LABEL(kernel_pmap_store)
	add	t2, s4, s8 		// PA -> VA
	srli	t3, s4, PGSHIFT
	PTR_S	t2, PM_PDETAB(t1)	// VA of kernel PDETAB
	PTR_S	t3, PM_MD_PPN(t1)	// PPN of kernel PDETAB

	/*
	 * Store kern_vtopdiff (the difference between the physical
	 * and virtual address of the "start" symbol).
	 *
	 * XXX For some reason doing this store to the physical
	 * XXX address of kern_vtopdiff before the MMU is enabled
	 * XXX doesn't work on the AllWinner D1.
	 */
	PTR_LA	t0, _C_LABEL(kern_vtopdiff)
	PTR_S	s8, 0(t0)	/* kern_vtopdiff = start(virt) - start(phys) */

#if notyet
	mv	a0, s11			// dtb
	call	_C_LABEL(init_mmu)
#endif

	li	t0, VM_MIN_KERNEL_ADDRESS + VM_KERNEL_SIZE
	li	t1, NBSEG - 1
	and	t1, s11, t1
	or	t0, t0, t1

	/* Set the global pointer */
	.option push
	.option norelax
	lla	gp, __global_pointer$
	.option pop

	// Now we should ready to start initializing the kernel.
	mv	a0, s10			// hartid
	mv	a1, s11			// dtb (physical)

	li	s0, 0			// zero frame pointer
	call	_C_LABEL(init_riscv)	// do MD startup
	tail	_C_LABEL(main)		// and transfer to main
	/* No return from main */
END(start)


#if defined(MULTIPROCESSOR)

// a0 is hartid
// a1 is the cookie from sbi_hart_start
ENTRY(cpu_mpstart)
	mv	s10, a0			// copy hartid
	mv	s11, a1			// copy sbi_hart_start cookie

	/*
	 * s11 = cpuindex
	 */

	/* set stack pointer for boot */
	li	t1, BOOT_AP_STACKSIZE		// XXXNH do a shift
	mul	t1, s11, t1
	PTR_LA	t0, _C_LABEL(bootstk)
	/* sp = bootstk + (BOOT_AP_STACKSIZE * cpuindex) */
	add	sp, t0, t1


	/*
	 * Calculate the difference between the VA and PA for start and
	 * keep in s8.
	 */
	PTR_LA	t0, start
	PTR_L	s8, .Lstart

	sub	s8, s8, t0

#ifdef _LP64
	PTR_LA	s4, _C_LABEL(l2_pte)
#else
	PTR_LA	s4, _C_LABEL(l1_pte)
#endif

	// s4 is satp address....
	// s8 is kern_vtopdiff
	//

	/* Set supervisor trap vector base register */
	PTR_LA	t0, vmpstart
	add	t0, t0, s8
	csrw	stvec, t0

	/* Set supervisor address translation and protection register */
	srli	t1, s4, PGSHIFT
#ifdef _LP64
	li	t0, SATP_MODE_SV39
#else
	li	t0, SATP_MODE_SV32
#endif
	or	t0, t0, t1
	sfence.vma
	csrw	satp, t0

	.align 2
	.global vmpstart
vmpstart:
	// MMU is on!
	csrw	sscratch, zero		// zero in sscratch to mark kernel

	/* Set the global pointer */
	.option push
	.option norelax
	lla	gp, __global_pointer$
	.option pop

	/* Set SP to VA */
	add	sp, sp, s8

	/* Set supervisor trap vector base register with ipi_handler */
	PTR_LA	a0, _C_LABEL(ipi_handler)
	csrw	stvec, a0
	csrsi	sie, SIE_SSIE
	csrsi	sstatus, SR_SIE		// enable interrupts

	li	tp, 0
	mv	a0, s11
	call	_C_LABEL(cpu_init_secondary_processor)

	/* t3 = __BIT(cpuindex % (sizeof(u_long) * NBBY)) */
	li	t3, 1
	andi	t0, s11, (1U << (LONG_SCALESHIFT + NBBY_SHIFT)) - 1
	sll	t3, t3, t0

	/* t4 = &riscv_cpu_mbox[cpuindex / (sizeof(u_long) * NBBY)] */
	PTR_LA	t0, _C_LABEL(riscv_cpu_mbox)
	srli	t1, s11, LONG_SCALESHIFT + NBBY_SHIFT
	slli	t1, t1, LONG_SCALESHIFT
	add	t4, t0, t1

	/* wait for the mailbox start bit to become true */
1:
	fence	rw, r		/* matches cpu_boot_secondary_processors */
	LONG_L	t0, 0(t4)
	and	t0, t0, t3
	bne	t0, zero, 9f
	wfi
	j	1b
9:

	/* Set supervisor trap vector base register */
	PTR_LA	a0, _C_LABEL(cpu_exception_handler)
	csrw	stvec, a0

	li	t0, CI_SIZE
	mul	t0, s11, t0
	PTR_LA	t1, _C_LABEL(cpu_info_store)
	add	a0, t0, t1		/* a0 = &cpu_info_store[cpuindex] */

	/*
	 * set curlwp (tp and curcpu()->ci_curlwp) now we know the
	 * idle lwp from curcpu()->ci_idlelwp
	 */
	PTR_L	tp, CI_IDLELWP(a0)	/* tp = curcpu()->ci_idlelwp */
	PTR_S	tp, CI_CURLWP(a0)	/* curlwp is idlelwp */

	/* get my stack from lwp */
	PTR_L	t2, L_PCB(tp)		/* t2 = lwp_getpcb(idlelwp) */
	li	t3, UPAGES * PAGE_SIZE
	add	t2, t2, t3
	addi	sp, t2, -TF_LEN		/* sp = pcb + USPACE - TF_LEN */

	li	s0, 0			/* trace back starts here (fp = 0) */
	PTR_L	a0, L_CPU(tp)		/* curlwp->l_cpu */
	mv	a1, s11			/* cpuindex */
	call	_C_LABEL(cpu_hatch)

	li	s0, 0			// zero frame pointer
	tail	idle_loop
	/* No return from idle_loop */
END(cpu_mpstart)


toomanyharts:
	PRINTS("too many harts, or hart id doesn't exist in cpu_hart[]\n")
1:	wfi
	j	1b

/*
 * A very basic exception handler to just return when an IPI comes in during
 * AP bringup.
 *
 * The handler address needs to have bottom two bits as zero.
 */
	.align 2

ipi_handler:
	csrrw	tp, sscratch, tp	// swap scratch and thread pointer
	bnez	tp, 1f			//   tp != 0, something went wrong.

	csrr	tp, scause		// get cause
	bgez	tp, 2f			// MSB is set if interrupt

	csrw	sip, zero		// clear all interrupts

	csrrw	tp, sscratch, zero	// get back our thread pointer
	sret

1:
	wfi
	j	1b
2:
	wfi
	j	2b
#endif

	.align 3
.Lstart:
#ifdef _LP64
	.quad	start
#else
	.word	start
#endif


#ifdef CONSADDR
	.align 3
.Lconsaddr:
#ifdef _LP64
	.quad	CONSADDR
#else
	.word	CONSADDR
#endif
#endif


ENTRY_NP(uartputc)
#ifdef EARLYCONS
	tail	___CONCAT(EARLYCONS, _platform_early_putchar)
#else
#define	SBI_LEGACY_CONSOLE_PUTCHAR	1
	li	a7, SBI_LEGACY_CONSOLE_PUTCHAR
	ecall
	ret
#endif
END(uartputc)


ENTRY_NP(uartgetc)
#ifdef EARLYCONS
	li	a0, -1
#else
#define	SBI_LEGACY_CONSOLE_GETCHAR	2
	li	a7, SBI_LEGACY_CONSOLE_GETCHAR
	ecall
	ret
#endif


ENTRY_NP(clear_bss)
	bgeu	a0, a1, 1f
2:
	sb	zero, 0(a0)
	addi	a0, a0, 1
	bne	a1, a0, 2b
1:
	ret
END(clear_bss)


	.globl  _C_LABEL(cpu_Debugger_insn)
	.globl  _C_LABEL(cpu_Debugger_ret)

ENTRY_NP(cpu_Debugger)
cpu_Debugger_insn:
	ebreak
cpu_Debugger_ret:
	ret
END(cpu_Debugger)

ENTRY_NP(locore_prints)
	addi	sp, sp, -(SZREG * 2)
	REG_S	s0, (0 * SZREG)(sp)
	mv	s0, ra
1:
	lbu	a0, 0(s0)
	beqz	a0, 2f

	call	uartputc

	addi	s0, s0, 1
	j	1b
2:
	addi	s0, s0, 8	// s0 points to the null terminator
	andi	ra, s0, -8

	REG_L	s0, (0 * SZREG)(sp)
	addi	sp, sp, (SZREG * 2)
	ret

END(locore_prints)


#if defined(VERBOSE_INIT_RISCV)
ENTRY_NP(locore_printx)
	addi	sp, sp, -(SZREG * 4)
	REG_S	s0, (0 * SZREG)(sp)
	REG_S	s1, (1 * SZREG)(sp)
	REG_S	s2, (2 * SZREG)(sp)
	REG_S	ra, (3 * SZREG)(sp)

	mv	s1, a0		// our print value
	li	s2, 10

	li	a0, '0'
	call	uartputc
	li	a0, 'x'
	call	uartputc

	// Word size in bits
	li	s0, (SZREG * 8)
1:
	addi	s0, s0, -4	// nibble shift

	srl	a0, s1, s0	// extract ...
	andi	a0, a0, 0xf

	bltu	a0, s2, 2f
	addi	a0, a0, ('a' - '0' - 10)
2:	addi	a0, a0, '0'

	call	uartputc

	beqz	s0, 3f

	and	a0, s0, (16 - 1)
	bnez	a0, 1b

	li	a0, '_'
	call	uartputc

	j	1b

3:
	REG_L	s0, (0 * SZREG)(sp)
	REG_L	s1, (1 * SZREG)(sp)
	REG_L	s2, (2 * SZREG)(sp)
	REG_L	ra, (3 * SZREG)(sp)
	addi	sp, sp, (SZREG * 4)
	ret
END(locore_printx)


ENTRY_NP(locore_printxnl)
	addi	sp, sp, -(SZREG * 2)
	REG_S	ra, (1 * SZREG)(sp)

	call	locore_printx
	li	a0, '\n'
	call	uartputc

	li	a0, '\r'
	call	uartputc

	REG_L	ra, (1 * SZREG)(sp)
	addi	sp, sp, (SZREG * 2)

	ret
END(locore_printxnl)
#endif	/* VERBOSE_INIT_RISCV */


	.data
	.align	2
hart_boot:
	.word	0

	/*
	 * Allocate some memory after the kernel image for stacks and
	 * bootstrap L1PT
	 */

//	.section "_init_memory", "aw", %nobits
	.align PGSHIFT
	.global _C_LABEL(lwp0uspace)
_C_LABEL(lwp0uspace):
	.space	UPAGES * PAGE_SIZE
bootstk:

#ifdef MULTIPROCESSOR
	.space	BOOT_AP_STACKSIZE * (MAXCPUS - 1)
#endif

//	.section "_init_memory", "aw", %nobits
	.align PGSHIFT
mmutables_start:
bootstrap_pde:
	.global _C_LABEL(bootstrap_pde)
#ifdef _LP64
	.global _C_LABEL(l2_pte)
l2_pte:
	.space PAGE_SIZE
#endif
	.global _C_LABEL(l1_pte)
l1_pte:
	.space PAGE_SIZE
mmutables_end:

