Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
1 /* 2 * fiwix/kernel/multiboot.c 3 * 4 * Copyright 2021, Jordi Sanfeliu. All rights reserved. 5 * Distributed under the terms of the Fiwix License. 6 */ 7 8 #include <fiwix/config.h> 9 #include <fiwix/kernel.h> 10 #include <fiwix/multiboot1.h> 11 #include <fiwix/stdio.h> 12 #include <fiwix/string.h> 13 #include <fiwix/limits.h> 14 #include <fiwix/kparms.h> 15 #include <fiwix/i386elf.h> 16 #include <fiwix/ramdisk.h> 17 #include <fiwix/mm.h> 18 #include <fiwix/bios.h> 19 #include <fiwix/vgacon.h> 20 #include <fiwix/fb.h> 21 #include <fiwix/fbcon.h> 22 23 Elf32_Shdr *symtab, *strtab; 24 25 /* check the validity of a command line parameter */ 26 static int check_parm(struct kparms *parm, const char *value) 27 { 28 int n; 29 30 if(!strcmp(parm->name, "root=")) { 31 for(n = 0; parm->value[n]; n++) { 32 if(!strcmp(parm->value[n], value)) { 33 _rootdev = parm->sysval[n]; 34 strncpy(_rootdevname, value, DEVNAME_MAX); 35 return 0; 36 } 37 } 38 return 1; 39 } 40 if(!strcmp(parm->name, "noramdisk")) { 41 _noramdisk = 1; 42 return 0; 43 } 44 if(!strcmp(parm->name, "ramdisksize=")) { 45 int size = atoi(value); 46 if(!size || size > RAMDISK_MAXSIZE) { 47 printk("WARNING: 'ramdisksize' value is out of limits, defaulting to 4096KB.\n"); 48 _ramdisksize = 0; 49 } else { 50 _ramdisksize = size; 51 } 52 return 0; 53 } 54 if(!strcmp(parm->name, "initrd=")) { 55 if(value[0]) { 56 strncpy(_initrd, value, DEVNAME_MAX); 57 return 0; 58 } 59 } 60 if(!strcmp(parm->name, "rootfstype=")) { 61 for(n = 0; parm->value[n]; n++) { 62 if(!strcmp(parm->value[n], value)) { 63 strncpy(_rootfstype, value, sizeof(_rootfstype)); 64 return 0; 65 } 66 } 67 return 1; 68 } 69 if(!strcmp(parm->name, "console=")) { 70 for(n = 0; parm->value[n]; n++) { 71 if(!strcmp(parm->value[n], value)) { 72 if(parm->sysval[n]) { 73 _syscondev = parm->sysval[n]; 74 return 0; 75 } 76 printk("WARNING: device name for '%s' is not defined!\n", parm->name); 77 } 78 } 79 return 1; 80 } 81 printk("WARNING: the parameter '%s' looks valid but it's not defined!\n", parm->name); 82 return 0; 83 } 84 85 static int parse_arg(const char *arg) 86 { 87 int n; 88 89 /* '--' marks the beginning of the init arguments */ 90 if(!strcmp(arg, "--")) { 91 return 1; 92 } 93 94 for(n = 0; parm_table[n].name; n++) { 95 if(!strncmp(arg, parm_table[n].name, strlen(parm_table[n].name))) { 96 arg += strlen(parm_table[n].name); 97 if(check_parm(&parm_table[n], arg)) { 98 printk("WARNING: invalid value '%s' in the '%s' parameter.\n", arg, parm_table[n].name); 99 } 100 return 0; 101 } 102 } 103 printk("WARNING: invalid cmdline parameter: '%s'.\n", arg); 104 return 0; 105 } 106 107 static char * parse_cmdline(const char *str) 108 { 109 char *from, *to; 110 char arg[CMDL_ARG_LEN]; 111 char c; 112 113 from = to = (char *)str; 114 for(;;) { 115 c = *(str++); 116 if(c == ' ' || !c) { 117 if(to - from < CMDL_ARG_LEN) { 118 memcpy_b(arg, from, to - from); 119 arg[to - from] = NULL; 120 if(arg[0] != NULL) { 121 if(parse_arg(arg)) { 122 while(*(from++)) { 123 if(*from != '-' && *from != ' ') { 124 break; 125 } 126 } 127 return from; 128 } 129 } 130 } else { 131 memcpy_b(arg, from, CMDL_ARG_LEN); 132 arg[CMDL_ARG_LEN - 1] = NULL; 133 printk("WARNING: invalid length of the cmdline parameter '%s'.\n", arg); 134 } 135 from = ++to; 136 if(!c) { 137 break; 138 } 139 continue; 140 } 141 to++; 142 } 143 144 return NULL; 145 } 146 147 /* 148 * This function returns the last address used by kernel symbols or the value 149 * of 'mod_end' (in the module structure) of the last module loaded by GRUB. 150 * 151 * This is intended to setup the kernel stack beyond all these addresses. 152 */ 153 unsigned int get_last_boot_addr(unsigned int info) 154 { 155 struct multiboot_info *mbi; 156 Elf32_Shdr *shdr; 157 struct multiboot_elf_section_header_table *hdr; 158 struct multiboot_mod_list *mod; 159 unsigned short int n; 160 unsigned int addr; 161 162 symtab = strtab = NULL; 163 mbi = (struct multiboot_info *)info; 164 hdr = &(mbi->u.elf_sec); 165 for(n = 0; n < hdr->num; n++) { 166 shdr = (Elf32_Shdr *)(hdr->addr + (n * hdr->size)); 167 if(shdr->sh_type == SHT_SYMTAB) { 168 symtab = shdr; 169 } 170 if(shdr->sh_type == SHT_STRTAB) { 171 strtab = shdr; 172 } 173 } 174 175 addr = strtab->sh_addr + strtab->sh_size; 176 177 /* 178 * https://www.gnu.org/software/grub/manual/multiboot/multiboot.html 179 * 180 * Check if GRUB has loaded some modules and, if so, get the last 181 * address used by the last one. 182 */ 183 if(mbi->flags & MULTIBOOT_INFO_MODS) { 184 mod = (struct multiboot_mod_list *)mbi->mods_addr; 185 for(n = 0; n < mbi->mods_count; n++, mod++) { 186 addr = mod->mod_end; 187 } 188 } 189 190 return P2V(addr); 191 } 192 193 void multiboot(unsigned long magic, unsigned long info) 194 { 195 struct multiboot_info mbi; 196 197 memset_b(&video, 0, sizeof(struct video_parms)); 198 199 if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { 200 printk("WARNING: invalid multiboot magic number: 0x%x. Assuming 4MB of RAM.\n", (unsigned long int)magic); 201 memset_b(&mbi, NULL, sizeof(struct multiboot_info)); 202 _memsize = 640; 203 _extmemsize = 3072; 204 bios_map_init(NULL, 0); 205 video.columns = 80; 206 video.lines = 25; 207 video.flags = VPF_VGA; 208 video.memsize = 384 * 1024; 209 return; 210 } 211 212 memcpy_b(&mbi, (void *)info, sizeof(struct multiboot_info)); 213 214 if(mbi.flags & MULTIBOOT_INFO_BOOT_LOADER_NAME) { 215 printk("bootloader - %s\n", mbi.boot_loader_name); 216 } 217 218 if(!(mbi.flags & MULTIBOOT_INFO_MEMORY)) { 219 printk("WARNING: values in mem_lower and mem_upper are not valid!\n"); 220 } 221 _memsize = (unsigned int)mbi.mem_lower; 222 _extmemsize = (unsigned int)mbi.mem_upper; 223 224 225 if(mbi.flags & MULTIBOOT_INFO_CMDLINE) { 226 int n, len; 227 char c; 228 char *p; 229 230 p = (char *)mbi.cmdline; 231 len = strlen(p); 232 /* suppress 'fiwix' */ 233 for(n = 0; n < len; n++) { 234 c = *(p++); 235 if(c == ' ') { 236 break; 237 } 238 } 239 strcpy(cmdline, p); 240 init_args = parse_cmdline(cmdline); 241 } else { 242 printk("WARNING: no cmdline detected!\n"); 243 } 244 printk("kernel 0x%08X - cmdline='%s'\n", KERNEL_ENTRY_ADDR, cmdline); 245 246 247 if(mbi.flags & MULTIBOOT_INFO_MODS) { 248 int n; 249 struct multiboot_mod_list *mod; 250 251 mod = (struct multiboot_mod_list *)mbi.mods_addr; 252 for(n = 0; n < mbi.mods_count; n++, mod++) { 253 if(!strcmp((char *)mod->cmdline, _initrd)) { 254 printk("initrd 0x%08X-0x%08X file='%s' size=%dKB\n", mod->mod_start, mod->mod_end, mod->cmdline, (mod->mod_end - mod->mod_start) / 1024); 255 ramdisk_table[0].addr = (char *)mod->mod_start; 256 } 257 } 258 } 259 260 261 if(!(mbi.flags & MULTIBOOT_INFO_ELF_SHDR)) { 262 printk("WARNING: ELF section header table is not valid!\n"); 263 } 264 265 if(mbi.flags & MULTIBOOT_INFO_MEM_MAP) { 266 bios_map_init((struct multiboot_mmap_entry *)mbi.mmap_addr, mbi.mmap_length); 267 } else { 268 bios_map_init(NULL, 0); 269 } 270 271 if(mbi.flags & MULTIBOOT_INFO_VBE_INFO) { 272 struct vbe_controller *vbec; 273 struct vbe_mode *vbem; 274 unsigned long int from, to; 275 276 vbec = (struct vbe_controller *)mbi.vbe_control_info; 277 vbem = (struct vbe_mode *)mbi.vbe_mode_info; 278 279 video.flags = VPF_VESAFB; 280 video.address = (unsigned int *)vbem->phys_base; 281 video.port = 0; 282 video.memsize = vbec->total_memory * vbem->win_size * 1024; 283 strcpy((char *)video.signature, (char *)vbec->signature); 284 video.columns = vbem->x_resolution / vbem->x_char_size; 285 video.lines = vbem->y_resolution / vbem->y_char_size; 286 video.fb_version = vbec->version; 287 video.fb_width = vbem->x_resolution; 288 video.fb_height = vbem->y_resolution; 289 video.fb_char_width = vbem->x_char_size; 290 video.fb_char_height = vbem->y_char_size; 291 video.fb_bpp = vbem->bits_per_pixel; 292 video.fb_pixelwidth = vbem->bits_per_pixel / 8; 293 video.fb_pitch = vbem->bytes_per_scanline; 294 video.fb_linesize = video.fb_pitch * video.fb_char_height; 295 video.fb_size = vbem->x_resolution * vbem->y_resolution * video.fb_pixelwidth; 296 video.fb_vsize = video.lines * video.fb_pitch * video.fb_char_height; 297 298 from = (unsigned long int)video.address; 299 to = from + video.memsize; 300 bios_map_add(from, to, MULTIBOOT_MEMORY_AVAILABLE, MULTIBOOT_MEMORY_AVAILABLE); 301 from = (unsigned long int)video.address - KERNEL_BASE_ADDR; 302 to = (from + video.memsize); 303 bios_map_add(from, to, MULTIBOOT_MEMORY_AVAILABLE, MULTIBOOT_MEMORY_RESERVED); 304 } else { 305 video.columns = 80; 306 video.lines = 25; 307 video.flags = VPF_VGA; 308 video.memsize = 384 * 1024; 309 } 310 }