More work at patching and setup; load the boot sector when done
[profile/ivi/syslinux.git] / memdisk / msetup.c
1 #ident "$Id$"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2001 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Bostom MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * msetup.c
16  *
17  * Initialization code for memory-based disk
18  */
19
20 #include <stdint.h>
21 #include "e820.h"
22
23 static inline int get_e820(void)
24 {
25   struct e820_info {
26     uint64_t base;
27     uint64_t len;
28     uint32_t type;
29   } __attribute__((packed));
30   struct e820_info buf;
31   uint32_t lastptr = 0;
32   int copied;
33   int range_count;
34   
35   do {
36     asm volatile("int $0x15 ; "
37                  "jc 1f ; "
38                  "cmpl $0x534d4150, %%eax ; "
39                  "je 2f\n"
40                  "1:\n\t"
41                  "xorl %%ecx, %%ecx\n"
42                  "2:"
43                  : "=c" (copied), "=&b" (lastptr)
44                  : "a" (0x0000e820), "d" (0x534d4150),
45                  "c" (20), "D" (&buf)
46                  : "esi", "ebp");
47
48     if ( copied < 20 )
49       break;
50
51     insertrange(buf.base, buf.len, buf.type);
52     range_count++;
53
54   } while ( lastptr );
55
56   return !range_count;
57 }
58
59 static inline void get_dos_mem(void)
60 {
61   uint16_t dos_kb;
62
63   asm volatile("int $0x12" : "=a" (dos_kb)
64                :: "ebx", "ecx", "edx", "esi", "edi", "ebp");
65   
66   insertrange(0, (uint64_t)((uint32_t)dos_kb << 10), 1);
67 }
68
69 static inline int get_e881(void)
70 {
71   uint32_t low_mem;
72   uint32_t high_mem;
73   uint8_t err;
74
75   asm volatile("movw $0xe881, %%ax ; "
76                "int $0x15 ; "
77                "setc %2"
78                : "=a" (low_mem), "=b" (high_mem), "=d" (err)
79                :: "ecx", "esi", "edi", "ebp");
80
81   if ( !err ) {
82     if ( low_mem ) {
83       insertrange(0x100000, (uint64_t)low_mem << 10, 1);
84     }
85     if ( high_mem ) {
86       insertrange(0x1000000, (uint64_t)high_mem << 16, 1);
87     }
88   }
89
90   return err;
91 }
92
93 static inline int get_e801(void)
94 {
95   uint16_t low_mem;
96   uint16_t high_mem;
97   uint8_t err;
98
99   asm volatile("movw $0xe801, %%ax ; "
100                "int $0x15 ; "
101                "setc %2"
102                : "=a" (low_mem), "=b" (high_mem), "=d" (err)
103                :: "ecx", "esi", "edi", "ebp");
104
105   if ( !err ) {
106     if ( low_mem ) {
107       insertrange(0x100000, (uint64_t)((uint32_t)low_mem << 10), 1);
108     }
109     if ( high_mem ) {
110       insertrange(0x1000000, (uint64_t)((uint32_t)high_mem << 16), 1);
111     }
112   }
113
114   return err;
115 }
116
117 static inline int get_88(void)
118 {
119   uint16_t low_mem;
120   uint8_t err;
121
122   asm volatile("movb $0x88,%%ah ; "
123                "int $0x15 ; "
124                "setc %1"
125                : "=a" (low_mem), "=d" (err)
126                :: "ebx", "ecx", "esi", "edi", "ebp");
127
128   if ( !err ) {
129     if ( low_mem ) {
130       insertrange(0x100000, (uint64_t)((uint32_t)low_mem << 10), 1);
131     }
132   }
133
134   return err;
135 }
136
137 uint32_t dos_mem  = 0;          /* 0-1MB */
138 uint32_t low_mem  = 0;          /* 1-16MB */
139 uint32_t high_mem = 0;          /* 16+ MB */
140
141 void get_mem(void)
142 {
143   if ( get_e820() ) {
144     get_dos_mem();
145     if ( get_e881() ) {
146       if ( get_e801() ) {
147         if ( get_88() ) {
148           /* Running out of ideas here... */
149         }
150       }
151     }
152   }
153 }
154
155 void parse_mem(void)
156 {
157   struct e820range *ep;
158
159   /* Derive "dos mem", "high mem", and "low mem" from the range array */
160   for ( ep = ranges ; ep->type != -1 ; ep++ ) {
161     if ( ep->type == 1 ) {
162       /* Only look at memory ranges */
163       if ( ep->start == 0 ) {
164         if ( ep[1].start > 0x100000 )
165           dos_mem = 0x100000;
166         else
167           dos_mem = ep[1].start;
168       }
169       if ( ep->start <= 0x100000 && ep[1].start > 0x100000 ) {
170         if ( ep[1].start > 0x1000000 )
171           low_mem = 0x1000000 - ep->start;
172         else
173           low_mem = ep[1].start - ep->start;
174       }
175       if ( ep->start <= 0x1000000 && ep[1].start > 0x1000000 ) {
176         if ( ep[1].start > 0x100000000 )
177           high_mem = 0x100000000 - ep->start;
178         else
179           high_mem = ep[1].start - ep->start;
180       }
181     }
182   }
183 }
184
185 extern const char _binary_memdisk_bin_start[], _binary_memdisk_bin_end[];
186 extern const char _binary_memdisk_bin_size[]; /* Weird, I know */
187 struct memdisk_header {
188   uint16_t int13_offs;
189   uint16_t int15_offs;
190   uint16_t patch_offs;
191   uint16_t total_size;
192 };
193 struct patch_area {
194   uint8_t  driveno;
195   uint8_t  drivetype;
196   uint8_t  laststatus;
197   uint8_t  _pad1;
198
199   uint16_t cylinders;
200   uint16_t heads;
201   uint32_t sectors;
202   uint32_t disksize;
203   uint32_t diskbuf;
204
205   uint32_t e820table;
206   uint32_t mem1mb;
207   uint32_t mem16mb;
208   uint32_t memint1588;
209
210   uint32_t oldint13;
211   uint32_t oldint15;
212   uint16_t olddosmem;
213 };
214
215 /* Access to objects in the zero page */
216 static inline void
217 wrz_8(uint32_t addr, uint8_t data)
218 {
219   asm volatile("movb %0,%%fs:%1" :: "ri" (data), "m" (*(uint8_t *)addr));
220 }
221 static inline void
222 wrz_16(uint32_t addr, uint16_t data)
223 {
224   asm volatile("movw %0,%%fs:%1" :: "ri" (data), "m" (*(uint16_t *)addr));
225 }
226 static inline void
227 wrz_32(uint32_t addr, uint16_t data)
228 {
229   asm volatile("movl %0,%%fs:%1" :: "ri" (data), "m" (*(uint32_t *)addr));
230 }
231 static inline uint8_t
232 rdz_8(uint32_t addr)
233 {
234   uint8_t data;
235   asm volatile("movb %%fs:%1,%0" : "=r" (data) : "m" (*(uint8_t *)addr));
236   return data;
237 }
238 static inline uint16_t
239 rdz_16(uint32_t addr)
240 {
241   uint16_t data;
242   asm volatile("movw %%fs:%1,%0" : "=r" (data) : "m" (*(uint16_t *)addr));
243   return data;
244 }
245 static inline uint8_t
246 rdz_32(uint32_t addr)
247 {
248   uint32_t data;
249   asm volatile("movl %%fs:%1,%0" : "=r" (data) : "m" (*(uint32_t *)addr));
250   return data;
251 }
252
253 /* Addresses in the zero page */
254 #define BIOS_INT13      (0x13*4) /* INT 13h vector */
255 #define BIOS_INT15      (0x15*4) /* INT 13h vector */
256 #define BIOS_BASEMEM    0x413    /* Amount of DOS memory */
257
258 void setup(void)
259 {
260   unsigned int size = (int) &_binary_memdisk_bin_size;
261   struct memdisk_header *hptr;
262   struct patch_area *pptr;
263   uint16_t driverseg;
264   uint32_t driverptr, driveraddr;
265   uint8_t driveno = 0;
266   uint8_t status;
267   uint16_t exitcode;
268
269   /* Point %fs to the zero page */
270   asm volatile("movw %0,%%fs" :: "r" (0));
271
272   get_mem();
273   parse_mem();
274
275   /* Figure out where it needs to go */
276   hptr = (struct memdisk_header *) &_binary_memdisk_bin_start;
277   pptr = (struct patch_area *)(_binary_memdisk_bin_start + hptr->patch_offs);
278
279   if ( hptr->total_size > dos_mem ) {
280     /* Badness... */
281   }
282
283   pptr->olddosmem = rdz_16(BIOS_BASEMEM);
284
285   driveraddr  = dos_mem - hptr->total_size;
286   driveraddr &= ~0x3FF;
287
288   /* Reserve this range of memory */
289   insertrange(driveraddr, dos_mem-driveraddr, 2);
290   parse_mem();
291
292   pptr->mem1mb     = low_mem  >> 10;
293   pptr->mem16mb    = high_mem >> 16;
294   pptr->memint1588 = (low_mem == 0xf00000)
295     ? ((high_mem > 0x30ffc00) ? 0xffff : (high_mem >> 10)+0x3c00)
296     : (low_mem >> 10);
297
298   driverseg = driveraddr >> 4;
299   driverptr = driverseg  << 16;
300
301   pptr->oldint13 = rdz_32(BIOS_INT13);
302   pptr->oldint15 = rdz_32(BIOS_INT15);
303
304   /* Claim the memory and copy the driver into place */
305   wrz_16(BIOS_BASEMEM, dos_mem >> 10);
306
307   asm volatile("pushw %%es ; "
308                "movw %0,%%es ; "
309                "rep ; movsl %%ds:(%%si), %%es:(%%di) ; "
310                "popw %%es"
311                :: "r" (driverseg),
312                "c" (size >> 2),
313                "S" (&_binary_memdisk_bin_start),
314                "D" (0));
315
316   /* Install the interrupt handlers */
317   wrz_32(BIOS_INT13, driverptr+hptr->int13_offs);
318   wrz_32(BIOS_INT15, driverptr+hptr->int15_offs);
319
320   /* Reboot into the new "disk" */
321   asm volatile("pushw %%es ; "
322                "xorw %%cx,%%cx ; "
323                "movw %%cx,%%es ; "
324                "incw %%cx ; "
325                "movw $0x0201,%%ax ; "
326                "movw $0x7c00,%%bx ; "
327                "int $0x13 ; "
328                "setc %0 ; "
329                "popw %%es"
330                : "=r" (status), "=a" (exitcode)
331                : "d" ((uint16_t)driveno)
332                : "ebx", "ecx", "edx", "esi", "edi", "ebp");
333
334   if ( status ) {
335     /* Badness... */
336   }
337   
338   /* On return the assembly code will jump to the boot vector */
339 }
340