Merge from Etherboot 5.4
[people/oremanj/gpxe.git] / src / arch / i386 / core / multiboot_loader.c
1 /* Multiboot support
2  *
3  * 2003-07-02 mmap fix and header probe by SONE Takeshi
4  */
5
6 struct multiboot_mods {
7         unsigned mod_start;
8         unsigned mod_end;
9         unsigned char *string;
10         unsigned reserved;
11 };
12
13 struct multiboot_mmap {
14         unsigned int size;
15         unsigned int base_addr_low;
16         unsigned int base_addr_high;
17         unsigned int length_low;
18         unsigned int length_high;
19         unsigned int type;
20 };
21
22 /* The structure of a Multiboot 0.6 parameter block.  */
23 struct multiboot_info {
24         unsigned int flags;
25 #define MULTIBOOT_MEM_VALID       0x01
26 #define MULTIBOOT_BOOT_DEV_VALID  0x02
27 #define MULTIBOOT_CMDLINE_VALID   0x04
28 #define MULTIBOOT_MODS_VALID      0x08
29 #define MULTIBOOT_AOUT_SYMS_VALID 0x10
30 #define MULTIBOOT_ELF_SYMS_VALID  0x20
31 #define MULTIBOOT_MMAP_VALID      0x40
32         unsigned int memlower;
33         unsigned int memupper;
34         unsigned int bootdev;
35         unsigned int cmdline;   /* physical address of the command line */
36         unsigned mods_count;
37         struct multiboot_mods *mods_addr;
38         unsigned syms_num;
39         unsigned syms_size;
40         unsigned syms_addr;
41         unsigned syms_shndx;
42         unsigned mmap_length;
43         unsigned  mmap_addr;
44         /* The structure actually ends here, so I might as well put
45          * the ugly e820 parameters here...
46          */
47         struct multiboot_mmap mmap[E820MAX];
48 };
49
50 /* Multiboot image header (minimal part) */
51 struct multiboot_header {
52         unsigned int magic;
53 #define MULTIBOOT_HEADER_MAGIC 0x1BADB002
54         unsigned int flags;
55         unsigned int checksum;
56 };
57
58 static struct multiboot_header *mbheader;
59
60 static struct multiboot_info mbinfo;
61
62 static void multiboot_probe(unsigned char *data, int len)
63 {
64     int offset;
65     struct multiboot_header *h;
66
67     /* Multiboot spec requires the header to be in first 8KB of the image */
68     if (len > 8192)
69             len = 8192;
70
71     for (offset = 0; offset < len; offset += 4) {
72             h = (struct multiboot_header *) (data + offset);
73             if (h->magic == MULTIBOOT_HEADER_MAGIC
74                             && h->magic + h->flags + h->checksum == 0) {
75                     printf("/Multiboot");
76                     mbheader = h;
77                     return;
78             }
79     }
80     mbheader = 0;
81 }
82
83 static inline void multiboot_boot(unsigned long entry)
84 {
85         unsigned char cmdline[512], *c;
86         int i;
87         if (!mbheader)
88                 return;
89         /* Etherboot limits the command line to the kernel name,
90          * default parameters and user prompted parameters.  All of
91          * them are shorter than 256 bytes.  As the kernel name and
92          * the default parameters come from the same BOOTP/DHCP entry
93          * (or if they don't, the parameters are empty), only two
94          * strings of the maximum size are possible.  Note this buffer
95          * can overrun if a stupid file name is chosen.  Oh well.  */
96         c = cmdline;
97         for (i = 0; KERNEL_BUF[i] != 0; i++) {
98                 switch (KERNEL_BUF[i]) {
99                 case ' ':
100                 case '\\':
101                 case '"':
102                         *c++ = '\\';
103                         break;
104                 default:
105                         break;
106                 }
107                 *c++ = KERNEL_BUF[i];
108         }
109         (void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32));
110
111         mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID;
112         mbinfo.memlower = meminfo.basememsize;
113         mbinfo.memupper = meminfo.memsize;
114         mbinfo.bootdev = 0;     /* not booted from disk */
115         mbinfo.cmdline = virt_to_phys(cmdline);
116         for (i = 0; i < (int) meminfo.map_count; i++) {
117                 mbinfo.mmap[i].size = sizeof(struct multiboot_mmap)
118                     - sizeof(unsigned int);
119                 mbinfo.mmap[i].base_addr_low = 
120                     (unsigned int) meminfo.map[i].addr;
121                 mbinfo.mmap[i].base_addr_high = 
122                     (unsigned int) (meminfo.map[i].addr >> 32);
123                 mbinfo.mmap[i].length_low = 
124                     (unsigned int) meminfo.map[i].size;
125                 mbinfo.mmap[i].length_high = 
126                     (unsigned int) (meminfo.map[i].size >> 32);
127                 mbinfo.mmap[i].type = meminfo.map[i].type;
128         }
129         mbinfo.mmap_length = meminfo.map_count * sizeof(struct multiboot_mmap);
130         mbinfo.mmap_addr = virt_to_phys(mbinfo.mmap);
131         
132         /* The Multiboot 0.6 spec requires all segment registers to be
133          * loaded with an unrestricted, writeable segment.
134          * xstart32 does this for us.
135          */
136         
137         /* Start the kernel, passing the Multiboot information record
138          * and the magic number.  */
139         os_regs.eax = 0x2BADB002;
140         os_regs.ebx = virt_to_phys(&mbinfo);
141         xstart32(entry);
142         longjmp(restart_etherboot, -2);
143 }