boot.S (6147B)
1/* 2 * x86_64 boot and support code 3 * 4 * Copyright 2019 Linaro 5 * 6 * This work is licensed under the terms of the GNU GPL, version 3 or later. 7 * See the COPYING file in the top-level directory. 8 * 9 * Unlike the i386 version we instead use Xen's PVHVM booting header 10 * which should drop us automatically into 32 bit mode ready to go. I've 11 * nabbed bits of the Linux kernel setup to achieve this. 12 * 13 * SPDX-License-Identifier: GPL-3.0-or-later 14 */ 15 16 .section .head 17 18#define ELFNOTE_START(name, type, flags) \ 19.pushsection .note.name, flags,@note ; \ 20 .balign 4 ; \ 21 .long 2f - 1f /* namesz */ ; \ 22 .long 4484f - 3f /* descsz */ ; \ 23 .long type ; \ 241:.asciz #name ; \ 252:.balign 4 ; \ 263: 27 28#define ELFNOTE_END \ 294484:.balign 4 ; \ 30.popsection ; 31 32#define ELFNOTE(name, type, desc) \ 33 ELFNOTE_START(name, type, "") \ 34 desc ; \ 35 ELFNOTE_END 36 37#define XEN_ELFNOTE_ENTRY 1 38#define XEN_ELFNOTE_HYPERCALL_PAGE 2 39#define XEN_ELFNOTE_VIRT_BASE 3 40#define XEN_ELFNOTE_PADDR_OFFSET 4 41#define XEN_ELFNOTE_PHYS32_ENTRY 18 42 43#define __ASM_FORM(x) x 44#define __ASM_SEL(a,b) __ASM_FORM(b) 45#define _ASM_PTR __ASM_SEL(.long, .quad) 46 47 ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, _ASM_PTR 0x100000) 48 ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, _ASM_PTR _start) 49 ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, _ASM_PTR _start) /* entry == virtbase */ 50 ELFNOTE(Xen, XEN_ELFNOTE_PADDR_OFFSET, _ASM_PTR 0) 51 52 /* 53 * Entry point for PVH guests. 54 * 55 * Xen ABI specifies the following register state when we come here: 56 * 57 * - `ebx`: contains the physical memory address where the loader has placed 58 * the boot start info structure. 59 * - `cr0`: bit 0 (PE) must be set. All the other writeable bits are cleared. 60 * - `cr4`: all bits are cleared. 61 * - `cs `: must be a 32-bit read/execute code segment with a base of ‘0’ 62 * and a limit of ‘0xFFFFFFFF’. The selector value is unspecified. 63 * - `ds`, `es`: must be a 32-bit read/write data segment with a base of 64 * ‘0’ and a limit of ‘0xFFFFFFFF’. The selector values are all 65 * unspecified. 66 * - `tr`: must be a 32-bit TSS (active) with a base of '0' and a limit 67 * of '0x67'. 68 * - `eflags`: bit 17 (VM) must be cleared. Bit 9 (IF) must be cleared. 69 * Bit 8 (TF) must be cleared. Other bits are all unspecified. 70 * 71 * All other processor registers and flag bits are unspecified. The OS is in 72 * charge of setting up it's own stack, GDT and IDT. 73 */ 74 .code32 75 .section .text 76 77.global _start 78_start: 79 cld 80 lgdt gdtr 81 82 ljmp $0x8,$.Lloadcs 83.Lloadcs: 84 mov $0x10,%eax 85 mov %eax,%ds 86 mov %eax,%es 87 mov %eax,%fs 88 mov %eax,%gs 89 mov %eax,%ss 90 91 /* Enable PAE mode (bit 5). */ 92 mov %cr4, %eax 93 btsl $5, %eax 94 mov %eax, %cr4 95 96#define MSR_EFER 0xc0000080 /* extended feature register */ 97 98 /* Enable Long mode. */ 99 mov $MSR_EFER, %ecx 100 rdmsr 101 btsl $8, %eax 102 wrmsr 103 104 /* Enable paging */ 105 mov $.Lpml4, %ecx 106 mov %ecx, %cr3 107 108 mov %cr0, %eax 109 btsl $31, %eax 110 mov %eax, %cr0 111 112 /* Jump to 64-bit mode. */ 113 lgdt gdtr64 114 ljmp $0x8,$.Lenter64 115 116 .code64 117 .section .text 118.Lenter64: 119 120 121 // Setup stack ASAP 122 movq $stack_end,%rsp 123 124 /* don't worry about stack frame, assume everthing is garbage when we return */ 125 call main 126 127_exit: /* output any non-zero result in eax to isa-debug-exit device */ 128 test %al, %al 129 jz 1f 130 out %ax, $0xf4 131 1321: /* QEMU ACPI poweroff */ 133 mov $0x604,%edx 134 mov $0x2000,%eax 135 out %ax,%dx 136 hlt 137 jmp 1b 138 139 /* 140 * Helper Functions 141 * 142 * x86_64 calling convention is rdi, rsi, rdx, rcx, r8, r9 143 */ 144 145 /* Output a single character to serial port */ 146 .global __sys_outc 147__sys_outc: 148 pushq %rax 149 mov %rax, %rdx 150 out %al,$0xE9 151 popq %rax 152 ret 153 154 /* Interrupt Descriptor Table */ 155 156 .section .data 157 .align 16 158 159idt_00: .int 0, 0 160idt_01: .int 0, 0 161idt_02: .int 0, 0 162idt_03: .int 0, 0 163idt_04: .int 0, 0 164idt_05: .int 0, 0 165idt_06: .int 0, 0 /* intr_6_opcode, Invalid Opcode */ 166idt_07: .int 0, 0 167idt_08: .int 0, 0 168idt_09: .int 0, 0 169idt_0A: .int 0, 0 170idt_0B: .int 0, 0 171idt_0C: .int 0, 0 172idt_0D: .int 0, 0 173idt_0E: .int 0, 0 174idt_0F: .int 0, 0 175idt_10: .int 0, 0 176idt_11: .int 0, 0 177idt_12: .int 0, 0 178idt_13: .int 0, 0 179idt_14: .int 0, 0 180idt_15: .int 0, 0 181idt_16: .int 0, 0 182idt_17: .int 0, 0 183idt_18: .int 0, 0 184idt_19: .int 0, 0 185idt_1A: .int 0, 0 186idt_1B: .int 0, 0 187idt_1C: .int 0, 0 188idt_1D: .int 0, 0 189idt_1E: .int 0, 0 190idt_1F: .int 0, 0 191 192 193 /* 194 * Global Descriptor Table (GDT) 195 * 196 * This describes various memory areas (segments) through 197 * segment descriptors. In 32 bit mode each segment each 198 * segement is associated with segment registers which are 199 * implicitly (or explicitly) referenced depending on the 200 * instruction. However in 64 bit mode selectors are flat and 201 * segmented addressing isn't used. 202 */ 203gdt: 204 .short 0 205gdtr: 206 .short gdt_en - gdt - 1 207 .int gdt 208 209 // Code cs: 210 .short 0xFFFF 211 .short 0 212 .byte 0 213 .byte 0x9b 214 .byte 0xCF 215 .byte 0 216 217 // Data ds:, ss:, es:, fs:, and gs: 218 .short 0xFFFF 219 .short 0 220 .byte 0 221 .byte 0x93 222 .byte 0xCF 223 .byte 0 224gdt_en: 225 226gdt64: 227 .short 0 228gdtr64: 229 .short gdt64_en - gdt64 - 1 230 .int gdt64 231 232 // Code 233 .short 0xFFFF 234 .short 0 235 .byte 0 236 .byte 0x9b 237 .byte 0xAF 238 .byte 0 239 240 // Data 241 .short 0xFFFF 242 .short 0 243 .byte 0 244 .byte 0x93 245 .byte 0xCF 246 .byte 0 247gdt64_en: 248 249 .section .bss 250 .align 16 251 252stack: .space 65536 253stack_end: 254 255 .section .data 256 257.align 4096 258.Lpd: 259i = 0 260 .rept 512 * 4 261 .quad 0x1e7 | (i << 21) 262 i = i + 1 263 .endr 264 265.align 4096 266.Lpdp: 267 .quad .Lpd + 7 + 0 * 4096 /* 0-1 GB */ 268 .quad .Lpd + 7 + 1 * 4096 /* 1-2 GB */ 269 .quad .Lpd + 7 + 2 * 4096 /* 2-3 GB */ 270 .quad .Lpd + 7 + 3 * 4096 /* 3-4 GB */ 271 272.align 4096 273.Lpml4: 274 .quad .Lpdp + 7 /* 0-512 GB */