summaryrefslogtreecommitdiffstats
path: root/util/cbfstool/linux_trampoline.S
blob: 6a62ff0ed52259e453d479286a629263b11e2f68 (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* linux_trampoline */
/* SPDX-License-Identifier: GPL-2.0-only */

/* NOTE: THIS CODE MUST REMAIN POSITION INDEPENDENT
 *       IT SHOULDN'T USE THE STACK
 *       AND IN GENERAL EXPECT NOTHING BUT RAM TO WORK
 */
.code32
.data

#include "linux_trampoline.h"
#define HEADER_SIG 0x4f49424c // LBIO little endian
#define CB_TAG_FORWARD 0x11
#define CB_TAG_MEMORY 0x1
#define CB_TAG_FRAMEBUFFER 0x12
#define CB_TAG_ACPI_RSDP 0x43

#define ACPI_RSDP_ADDR 0x70
#define E820_NR_OFFSET 0x1e8
#define PROTOCOL_VERSION 0x206
#define LINUX_ENTRY_OFFSET 0x214
#define E820_OFFSET 0x2d0

.trampoline_start:
cld
xor %edx, %edx
mov $0, %ecx

.headerSearch:
mov $0x10000, %ebx
add %ecx, %ebx
mov (%ecx), %eax
cmp $HEADER_SIG, %eax
je .headerSearchDone // found the header
add $16, %ecx
cmp %ecx, %ebx
jne .headerSearch

.headerSearchDone:
cmp %ecx, %ebx // reached the end == not found anything?
je 2f // give up

// we assume the checksum is okay, no test
mov 4(%ecx), %ebx
add %ecx, %ebx // ebx = cb_header + header_bytes
mov 20(%ecx), %ecx // ecx = table_entries

.tableScan:
cmp $CB_TAG_FORWARD, (%ebx)
jne .testMemory

/* forward tag: assume 32bit pointer */
mov 8(%ebx), %ecx
jmp .headerSearch

.testMemory:
cmp $CB_TAG_MEMORY, (%ebx)
jne .testAcpiRsdp

/* memory tag: copy e820 map and entry count. also determine alt_mem_k */
mov 4(%ebx), %eax
sub $8, %eax
shr $2, %eax /* eax = number of dwords of e820 data */
/*
 * Historically linux had space for 32 entries. This limit was increased in
 * the year 2005 (Linux 2.6.11) to hold up to 128 entries.
 * Assume 128 entries when the boot protocol version is 2.04+.
 */
cmpw $0x0204, (LINUX_PARAM_LOC + PROTOCOL_VERSION)
jge .e820big  /* protocol version >= 2.04 can handle 128 entries of 5 dwords */
cmp $(32 * 5), %eax /* linux wants at most 32 entries of 5 dwords */
jng 1f
mov $(32 * 5), %eax /* only copy 32 entries */
jmp 1f

.e820big:
cmp $(128 * 5), %eax /* linux wants at most 128 entries of 5 dwords */
jng 1f
mov $(128 * 5), %eax /* only copy 128 entries */
1:
mov %eax, %esi
mov $5, %edi
div %edi
mov %eax, (LINUX_PARAM_LOC + E820_NR_OFFSET)
mov %esi, %eax
xchg %eax, %ecx
lea 8(%ebx), %esi /* e820 data source */
mov $(LINUX_PARAM_LOC + E820_OFFSET), %edi
rep movsl
xchg %eax, %ecx
/* e820 and LB_TAG_MEMORY type don't fully match: remap unknown type to 2, reserved memory */
mov (LINUX_PARAM_LOC + E820_NR_OFFSET), %eax
mov $(LINUX_PARAM_LOC + E820_OFFSET), %edi
.test_e820_entry:
cmp $0, %eax
je .endScan
cmp $12, 16(%edi) /* type */
jng .next_e820_entry
/* Fixup the type to 2, reserved memory */
mov $2, 16(%edi)
.next_e820_entry:
dec %eax
add $20, %edi
jmp .test_e820_entry

.testAcpiRsdp:
cmp $CB_TAG_ACPI_RSDP, (%ebx)
jne .testFramebuffer

mov 8(%ebx), %eax
mov %eax, (LINUX_PARAM_LOC + ACPI_RSDP_ADDR)
mov 12(%ebx), %eax
mov %eax, (LINUX_PARAM_LOC + ACPI_RSDP_ADDR + 4)
jmp .endScan

.testFramebuffer:
cmp $CB_TAG_FRAMEBUFFER, (%ebx)
jne .endScan

cmpw $0x020f, (LINUX_PARAM_LOC + PROTOCOL_VERSION)
jge .framebufferSetup  /* protocol version >= 2.15 can handle 64-bit address */
cmpl $0, 0x0c(%ebx)    /* check if upper 32-bit of framebuffer address are 0 */
jne .endScan

.framebufferSetup:
mov $LINUX_PARAM_LOC, %edi   /* translate the framebuffer entry into Linux' struct screen_info */
mov 0x08(%ebx), %eax   /* physical_address   */
mov %eax, 0x18(%edi)   /* -> lfb_base        */
mov 0x0c(%ebx), %eax   /* physical_address   */
mov %eax, 0x3a(%edi)   /* -> ext_lfb_base    */
mov 0x10(%ebx), %eax   /* x_resolution       */
mov %ax,  0x12(%edi)   /* -> lfb_width       */
mov 0x14(%ebx), %eax   /* y_resolution       */
mov %ax,  0x14(%edi)   /* -> lfb_height      */
mov 0x18(%ebx), %edx   /* bytes_per_line     */
mov %dx,  0x24(%edi)   /* -> lfb_linelength  */

mul %edx    /* bytes_per_line * y_resolution */
mov %eax, 0x1c(%edi)   /* -> lfb_size        */

movzbw 0x1c(%ebx), %ax /* bits_per_pixel     */
mov %ax,  0x16(%edi)   /* -> lfb_depth       */

mov $4, %esi           /* Copy 4 color components' pos and size, each 1 byte. */
1:
mov 0x1b(%ebx, %esi, 2), %ax
rol %ax                /* Order is reversed for Linux, hence swap. */
mov %ax, 0x24(%edi, %esi, 2)
dec %esi
jnz 1b

#define VIDEO_CAPABILITY_64BIT_BASE (1 << 1)
movl $VIDEO_CAPABILITY_64BIT_BASE, 0x36(%edi)

#define LFB_EFI_SIMPLE 0x70      /* VIDEO_TYPE_EFI in Linux */
movb $LFB_EFI_SIMPLE, 0x0f(%edi) /* -> orig_video_isVGA     */

.endScan:
add 4(%ebx), %ebx
dec %ecx
jnz .tableScan

/* Setup basic code and data segment selectors for Linux
**
** Flat code segment descriptor:
**   selector: 0x10
**   base    : 0x00000000
**   limit   : 0xFFFFFFFF
**   type    : code, execute, read
**
** Flat data segment descriptor:
**   selector: 0x18
**   base    : 0x00000000
**   limit   : 0xFFFFFFFF
**   type    : data, read/write
**
** Use TRAMPOLINE_ENTRY_LOC as a scratchpad.
*/
mov  $TRAMPOLINE_ENTRY_LOC, %eax
movl  $0x0000ffff, 16(%eax)		// Set up the 2 new descriptors
movl  $0x00cf9b00, 20(%eax)
movl  $0x0000ffff, 24(%eax)
movl  $0x00cf9300, 28(%eax)
movb $0x2b, 0(%eax)			// Set the size
movl %eax, 2(%eax)			// Set pointer to new GDT
lgdt (%eax)				// Load it

/* finally: jump to kernel */
mov $LINUX_PARAM_LOC, %esi
jmp *(LINUX_PARAM_LOC + LINUX_ENTRY_OFFSET)


2:
hlt
jmp 2b
.trampoline_end: