internal LApic(Virtual_Regions vreg, VirtMem vmem) { /* Initialize the local APIC for the current processor */ /* First, detect support for LAPIC: CPUID EAX=1 sets bit 9 of EDX if present */ uint[] cpuid_1 = libsupcs.x86_64.Cpu.Cpuid(1); if ((cpuid_1[3] & (1 << 9)) != (1 << 9)) { throw new Exception("LAPIC not supported by this processor"); } /* Read the LAPIC base address */ ulong lapic_base_msr = libsupcs.x86_64.Cpu.RdMsr(IA32_APIC_BASE_MSR); /* The base physical address is contained in bits 12 - 35, left shifted by 12 */ lapic_base_paddr = lapic_base_msr & IA32_APIC_BASE_ADDR_MASK; /* Get the lapic id: CPUID EAX=1 sets bits 31-24 of EBX */ ulong lapic_id = (ulong)(cpuid_1[1] >> 24); Formatter.Write("LAPIC: ID: ", Program.arch.DebugOutput); Formatter.Write(lapic_id, "X", Program.arch.DebugOutput); Formatter.Write(", paddr: ", Program.arch.DebugOutput); Formatter.Write(lapic_base_paddr, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* Map in the lapic register space * * Intel 3A:10.4.1: * The LAPIC register space is 4 kiB and must be mapped to an area of memory marked as * strong uncacheable (UC). * * Intel 3A:11.5.2.1, table 11-6: * Setting PCD and PWT bits on a page table entry ensures UC state regardless of MTRRs * */ lapic_base_vaddr = vreg.Alloc(0x1000, 0x1000, "LAPIC " + lapic_id.ToString() + " registers"); vmem.Map(lapic_base_paddr, 0x1000, lapic_base_vaddr, VirtMem.FLAG_writeable | VirtMem.FLAG_cache_disable | VirtMem.FLAG_write_through); /* Intel 3A:10.4.7.1: * * After reset, the LAPIC state is: * * IRR = 0 * ISR = 0 * TMR = 0 * ICR = 0 * LDR = 0 * TPR = 0 * Timer initial count = 0 * Timer current count = 0 * Divide configuration register = 0 * DFR = all 1s * LVT = all 0s except mask bits which are 1 * Version reg = unaffected * LAPIC ID reg = unique ID * Arb ID reg = LAPIC ID * Spurious interrupt reg = 0x000000FF */ }
public Acpi(Virtual_Regions vreg, VirtMem vmem, ulong bda_vaddr, Multiboot.MachineMinorType_x86 bios) { if (bios == Multiboot.MachineMinorType_x86.BIOS) { InitBIOS(vreg, vmem, bda_vaddr); } else if (bios == Multiboot.MachineMinorType_x86.UEFI) { InitUEFI(vreg, vmem, bda_vaddr); } }
public Hpet(Virtual_Regions vreg, VirtMem vmem, ulong paddr) { Formatter.Write("Initialising HPET at physical address ", Program.arch.DebugOutput); Formatter.Write(paddr, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); hpet_reg_space_vaddr = Program.map_in(paddr, 0x1000, "HPET", true, true, true); /* General capabilities register at offset 0 * * bits 0-7 revision id * bits 12-8 one less than number of timers (i.e. id of last timer) * bit 13 count size cap. 1 if main counter is 64 bits wide, else 0 * bit 14 reserved * bit 15 legacy replacement route capable * bits 16-31 vendor ID (as per PCI) * bits 32-63 rate at which main counter increments in femtoseconds (10 ^ -15) */ ulong gen_cap = ReadQwordRegister(0); timer_count = (int)(((gen_cap >> 8) & 0x1f) + 1); main_timer_is_64_bit = ((gen_cap & 0x2000) == 0x2000); counter_clk_period = (uint)(gen_cap >> 32); Formatter.Write("CounterClkPeriod: ", Program.arch.DebugOutput); Formatter.Write((ulong)counter_clk_period, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); double hpet_freq_mhz = Convert.ToDouble((long)counter_clk_period); hpet_freq_mhz *= 0.000000001; hpet_freq_mhz = 1 / hpet_freq_mhz; Program.arch.DebugOutput.Write("HPET frequency is " + hpet_freq_mhz.ToString() + " Mhz\n"); DisableMainCounter(); }
public unsafe UEFI(Virtual_Regions vreg, VirtMem vmem, ulong system_table_paddr) { /* The following assumes a 64-bit platform */ Formatter.Write("UEFI: system table at paddr: ", Program.arch.DebugOutput); Formatter.Write(system_table_paddr, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* Load up the system table */ ulong st_vaddr = Program.map_in(system_table_paddr, UEFIHeaderSize, "UEFI ST Header"); ulong sig = *(ulong *)st_vaddr; uint revision = *(uint *)(st_vaddr + 8); uint hdr_size = *(uint *)(st_vaddr + 12); uint crc32 = *(uint *)(st_vaddr + 16); uint reserved = *(uint *)(st_vaddr + 20); Formatter.Write("UEFI: system table signature: ", Program.arch.DebugOutput); Formatter.Write(sig, "X", Program.arch.DebugOutput); Formatter.Write(", revision: ", Program.arch.DebugOutput); Formatter.Write((ulong)revision, "X", Program.arch.DebugOutput); Formatter.Write(", header size: ", Program.arch.DebugOutput); Formatter.Write((ulong)hdr_size, Program.arch.DebugOutput); Formatter.Write(", crc32: ", Program.arch.DebugOutput); Formatter.Write((ulong)crc32, "X", Program.arch.DebugOutput); Formatter.Write(", reserved: ", Program.arch.DebugOutput); Formatter.Write((ulong)reserved, Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); if (sig != UEFISystemTableSignature) { throw new Exception("UEFI system table bad signature: " + sig.ToString("X16")); } st_vaddr = Program.map_in(system_table_paddr, hdr_size, "UEFI ST"); /* Get the configuration table pointer */ ulong NumberOfTableEntries = *(ulong *)(st_vaddr + 104); ulong ConfigurationTable = *(ulong *)(st_vaddr + 112); Formatter.Write("UEFI: NumberOfTableEntries: ", Program.arch.DebugOutput); Formatter.Write(NumberOfTableEntries, Program.arch.DebugOutput); Formatter.Write(", ConfigurationTable: ", Program.arch.DebugOutput); Formatter.Write(ConfigurationTable, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* Load the configuration table pointer array. This is an array of * struct { * EFI_GUID VendorGuid; * VOID * VendorTable; * }; * located at address ConfigurationTable. * * NB EFI_GUID is a 128 bit buffer */ ulong cfg_vaddr = Program.map_in(ConfigurationTable, NumberOfTableEntries * 24, "UEFI CfgTableArray"); for (ulong i = 0; i < NumberOfTableEntries; i++) { ulong guid1 = *(ulong *)(cfg_vaddr + i * 24); ulong guid2 = *(ulong *)(cfg_vaddr + i * 24 + 8); ulong table = *(ulong *)(cfg_vaddr + i * 24 + 16); Formatter.Write("UEFI: Table GUID: ", Program.arch.DebugOutput); Formatter.Write(guid1, "X", Program.arch.DebugOutput); Formatter.Write(guid2, "X", Program.arch.DebugOutput); Formatter.Write(", Address: ", Program.arch.DebugOutput); Formatter.Write(table, "X", Program.arch.DebugOutput); if (guid1 == ACPI10GUID1 && guid2 == ACPI10GUID2) { Formatter.Write(" (ACPI_10_TABLE)", Program.arch.DebugOutput); acpi_10_table = (void *)table; } if (guid1 == ACPI20GUID1 && guid2 == ACPI20GUID2) { Formatter.Write(" (ACPI_20_TABLE)", Program.arch.DebugOutput); acpi_20_table = (void *)table; } if (guid1 == SMBIOSGUID1 && guid2 == SMBIOSGUID2) { Formatter.Write(" (SMBIOS_TABLE)", Program.arch.DebugOutput); smbios_table = (void *)table; } Formatter.WriteLine(Program.arch.DebugOutput); } }
public unsafe Acpi(Virtual_Regions vreg, VirtMem vmem, Arch.FirmwareConfiguration fwconf) { ulong xsdt = 0; uint rsdt = 0; /* Decide which table to use * * Use XSDT first if available */ if (fwconf.ACPI_20_table != null) { ulong va_acpi_20 = Program.map_in((ulong)fwconf.ACPI_20_table, 36, "RSDP"); uint acpi_20_len = *(uint *)(va_acpi_20 + 20); if (acpi_20_len > 32) { xsdt = *(ulong *)(va_acpi_20 + 24); } if (xsdt == 0) { rsdt = *(uint *)(va_acpi_20 + 16); } } else if (fwconf.ACPI_10_table != null) { ulong va_acpi_10 = Program.map_in((ulong)fwconf.ACPI_10_table, 24, "RSDP"); rsdt = *(uint *)(va_acpi_10 + 16); } if (xsdt == 0 && rsdt == 0) { throw new Exception("ACPI: RSDP not found"); } Formatter.Write("ACPI: RSDT: ", Program.arch.DebugOutput); Formatter.Write((ulong)rsdt, "X", Program.arch.DebugOutput); Formatter.Write(", XSDT: ", Program.arch.DebugOutput); Formatter.Write(xsdt, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); tables = new List <AcpiTable>(); if (xsdt != 0) { /* Use XSDT */ ulong xsdt_va = Program.map_in(xsdt, 36, "XSDT header"); uint len = *(uint *)(xsdt_va + 4); xsdt_va = Program.map_in(xsdt, len, "XSDT"); uint entries = (len - 36) / 8; for (uint i = 0; i < entries; i++) { ulong tbl_address = *(ulong *)(xsdt_va + 36 + i * 8); Formatter.Write("ACPI: XSDT entry: ", Program.arch.DebugOutput); Formatter.Write(tbl_address, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); InterpretTable(tbl_address); } } else if (rsdt != 0) { /* Use RSDT */ ulong rsdt_va = Program.map_in(rsdt, 36, "RSDT header"); uint len = *(uint *)(rsdt_va + 4); rsdt_va = Program.map_in(rsdt, len, "RSDT"); uint entries = (len - 36) / 4; for (uint i = 0; i < entries; i++) { uint tbl_address = *(uint *)(rsdt_va + 36 + i * 4); Formatter.Write("ACPI: RSDT entry: ", Program.arch.DebugOutput); Formatter.Write((ulong)tbl_address, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); InterpretTable(tbl_address); } } }
unsafe void InitBIOS(Virtual_Regions vreg, VirtMem vmem, ulong bda_vaddr) { /* To set up ACPI, we first need to find the Root System Description Pointer (RDSP) * This is a data structure that is in one of two places: * * 1) The extended bios data area, this is pointed to by the word at physical address 0x040e, * left shifted by 4) * 2) The memory from 0xe0000 to 0xfffff * * The RDSP is 16-byte aligned and starts with the string value "RSD PTR " * this is equal to 0x2052545020445352 */ ulong ebda_paddr = ((ulong)*(ushort *)(bda_vaddr + 0x40e)) << 4; ulong ebda_length = 0xa0000 - ebda_paddr; Formatter.Write("EBDA found at: ", Program.arch.DebugOutput); Formatter.Write(ebda_paddr, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* map in the EBDA */ ulong ebda_vaddr = Program.map_in(ebda_paddr, ebda_length, "EBDA"); rdsp_vaddr = 0; ulong cur_addr = ebda_vaddr; while (cur_addr < (ebda_vaddr + ebda_length)) { if (*(ulong *)cur_addr == 0x2052545020445352UL) { if (verify_rdsp(cur_addr)) { rdsp_vaddr = cur_addr; break; } } cur_addr += 16; } if (rdsp_vaddr == 0) { /* we didn't find the RDSP in the EDBA, therefore search the range 0xe0000 to 0xfffff */ ulong search_base_vaddr = Program.map_in(0xe0000, 0x20000, "RDSP Search"); cur_addr = search_base_vaddr; while (cur_addr < (search_base_vaddr + 0x20000)) { if (*(ulong *)cur_addr == 0x2052545020445352UL) { if (verify_rdsp(cur_addr)) { rdsp_vaddr = cur_addr; break; } } cur_addr += 16; } } if (rdsp_vaddr == 0) { throw new Exception("RDSP not found"); } /* We have found the RDSP, now interpret it */ RDSPDescriptor *rdsp = (RDSPDescriptor *)rdsp_vaddr; ulong revision = rdsp->checksum_oemid_revision >> 56; bool use_xsdt = false; ulong sdt_paddr = 0; ulong sdt_length = 0; if (revision == 0) { sdt_paddr = (ulong)rdsp->RSDT_paddr; sdt_length = 36; } else { use_xsdt = true; sdt_paddr = rdsp->XSDT_paddr; sdt_length = (ulong)rdsp->length; } if (use_xsdt) { Formatter.Write("XSDT at ", Program.arch.DebugOutput); } else { Formatter.Write("RDST at ", Program.arch.DebugOutput); } Formatter.Write(sdt_paddr, "X", Program.arch.DebugOutput); Formatter.Write(" length: ", Program.arch.DebugOutput); Formatter.Write(sdt_length, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* Map in the xsdt */ string tab_name = "RSDT"; if (use_xsdt) { tab_name = "XSDT"; } ulong xsdt_vaddr = Program.map_in(sdt_paddr, sdt_length, tab_name); XSDT *xsdt = (XSDT *)xsdt_vaddr; if (use_xsdt) { Formatter.Write("XSDT signature: ", Program.arch.DebugOutput); } else { Formatter.Write("RSDT signature: ", Program.arch.DebugOutput); } Formatter.Write((ulong)xsdt->signature, "X", Program.arch.DebugOutput); Formatter.Write(" length: ", Program.arch.DebugOutput); Formatter.Write((ulong)xsdt->length, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); /* Now remap it based on its new length */ xsdt_vaddr = Program.map_in(sdt_paddr, xsdt->length, tab_name); /* Read in the various tables */ tables = new List <AcpiTable>(); ulong table_pointer = xsdt_vaddr + 36; while (table_pointer < (xsdt_vaddr + xsdt->length)) { if (use_xsdt) { InterpretTable(*(ulong *)table_pointer); table_pointer += 8; } else { InterpretTable((ulong)*(uint *)table_pointer); table_pointer += 4; } } }
unsafe void InitUEFI(Virtual_Regions vreg, VirtMem vmem, ulong system_table_paddr) { System.Diagnostics.Debugger.Break(); }
public static unsafe ulong LoadObject(Virtual_Regions vreg, VirtMem vmem, SymbolTable stab, lib.File s, string name, out ulong tls_size) { ElfReader.Elf64_Ehdr ehdr = ReadHeader(s); /* Load up section headers */ ulong e_shentsize = ehdr.e_shentsize; byte *shdrs = ReadStructure(s, ehdr.e_shoff, ehdr.e_shnum * e_shentsize); /* Load up section string table */ ElfReader.Elf64_Shdr *shdr_shstr = (ElfReader.Elf64_Shdr *)(shdrs + ehdr.e_shstrndx * e_shentsize); byte *sect_shstr = ReadStructure(s, shdr_shstr->sh_offset, shdr_shstr->sh_size); /* Iterate through the sections marked SHF_ALLOC, allocating space as we go */ uint e_shnum = (uint)ehdr.e_shnum; byte *sect_header = shdrs; /* .to files are relocatable but have the entry point set anyway */ ulong text_section = 0; ulong hdr_epoint = ehdr.e_entry; ulong hash_section = 0; ulong hash_len = 0; ElfReader.Elf64_Shdr *sym_tab_hdr = null; ElfReader.Elf64_Shdr *sym_tab_str_hdr = null; /* Map sections to their load address */ Dictionary <uint, ulong> sect_map = new Dictionary <uint, ulong>(); ulong start = 0; tls_size = Program.arch.tysos_tls_length; for (uint i = 0; i < e_shnum; i++) { ElfReader.Elf64_Shdr *cur_shdr = (ElfReader.Elf64_Shdr *)sect_header; // if this .symtab? if (cur_shdr->sh_type == 0x2) { sym_tab_hdr = cur_shdr; if (cur_shdr->sh_link != 0) { sym_tab_str_hdr = (ElfReader.Elf64_Shdr *)(shdrs + cur_shdr->sh_link * e_shentsize); } } if ((cur_shdr->sh_flags & 0x2) == 0x2) { /* SHF_ALLOC */ if ((cur_shdr->sh_flags & (1 << 10)) != 0) { // TLS tls_size += cur_shdr->sh_size; continue; } // get its name byte * name_addr = sect_shstr + cur_shdr->sh_name; string sect_name = new string((sbyte *)name_addr); /* Register with gc if writeable and not executable */ bool gc_data = false; if (((cur_shdr->sh_flags & 0x1) != 0) && ((cur_shdr->sh_flags & 0x4) == 0)) { gc_data = true; } // allocate space for it ulong sect_addr = vreg.AllocRegion(cur_shdr->sh_size, 0x1000, name + sect_name, 0, Virtual_Regions.Region.RegionType.ModuleSection, gc_data).start; cur_shdr->sh_addr = sect_addr; sect_map[i] = sect_addr; if (sect_addr + cur_shdr->sh_size > 0x7effffffff) { throw new Exception("Object section allocated beyond limit of small code model"); } // is this .text? if (sect_name == ".text") { text_section = sect_addr; } // or .hash? if (sect_name == ".hash") { hash_section = sect_addr; hash_len = cur_shdr->sh_size; } // copy the section to its destination if (cur_shdr->sh_type == 0x1) { /* SHT_PROGBITS */ // Convert the VirtualRegion to a managed byte array //byte[] sect_data = libsupcs.TysosArrayType.CreateByteArray((byte*)sect_addr, (int)cur_shdr->sh_size); byte[] sect_data = libsupcs.Array.CreateSZArray <byte>((int)cur_shdr->sh_size, (void *)sect_addr); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin loading section data"); // Read the section data into it s.Seek((long)cur_shdr->sh_offset, System.IO.SeekOrigin.Begin); s.Read(sect_data, 0, (int)cur_shdr->sh_size); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end loading section data"); } else if (cur_shdr->sh_type == 0x8) { /* SHT_NOBITS */ System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin clearing blank section"); libsupcs.MemoryOperations.MemSet((void *)sect_addr, 0, (int)cur_shdr->sh_size); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end clearing blank section"); } } sect_header += e_shentsize; } if (hash_section != 0 && hash_len != 0 && sym_tab_hdr != null && sym_tab_str_hdr != null) { // Load symbol table and hash table System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin loading symbol table"); var sym_data = ReadStructure(s, sym_tab_hdr->sh_offset, sym_tab_hdr->sh_size); sym_tab_hdr->sh_addr = (ulong)sym_data; System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end loading symbol table"); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin loading symbol string table"); var sym_str_data = ReadStructure(s, sym_tab_str_hdr->sh_offset, sym_tab_str_hdr->sh_size); sym_tab_str_hdr->sh_addr = (ulong)sym_str_data; System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end loading symbol string table"); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin loading hash symbol section"); var ht = new ElfReader.ElfHashTable(hash_section, (ulong)sym_data, sym_tab_hdr->sh_entsize, (ulong)sym_str_data, sect_map, (int)sym_tab_hdr->sh_info, sym_tab_hdr->sh_size); stab.symbol_providers.Add(ht); System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end loading hash symbol section"); } else { System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin loading symbols"); /* Iterate through defined symbols, loading them into the symbol table */ sect_header = shdrs; for (uint i = 0; i < e_shnum; i++) { ElfReader.Elf64_Shdr *cur_shdr = (ElfReader.Elf64_Shdr *)sect_header; if (cur_shdr->sh_type == 0x2) { /* SHT_SYMTAB */ // Load up the section data byte *shdr_data = ReadStructure(s, cur_shdr->sh_offset, cur_shdr->sh_size); ulong offset = cur_shdr->sh_info * cur_shdr->sh_entsize; cur_shdr->sh_addr = (ulong)shdr_data; while (offset < cur_shdr->sh_size) { ElfReader.Elf64_Sym *cur_sym = (ElfReader.Elf64_Sym *)(shdr_data + offset); bool is_vis = false; bool is_weak = false; uint st_bind = (cur_sym->st_info_other_shndx >> 4) & 0xf; if (st_bind == 1) { // STB_GLOBAL is_vis = true; } else if (st_bind == 2) { // STB_WEAK is_vis = true; is_weak = true; } if (is_vis) { /* Get the symbol's name */ // If the appropriate string table is not loaded, then load it ElfReader.Elf64_Shdr *strtab = (ElfReader.Elf64_Shdr *)(shdrs + cur_shdr->sh_link * e_shentsize); if (strtab->sh_addr == 0) { strtab->sh_addr = (ulong)ReadStructure(s, strtab->sh_offset, strtab->sh_size); } // Get the name string sym_name = new string((sbyte *)(strtab->sh_addr + cur_sym->st_name)); uint st_shndx = (cur_sym->st_info_other_shndx >> 16) & 0xffff; if (st_shndx != 0) { if (is_weak == false || stab.GetAddress(sym_name) == 0) { ulong sym_addr = ((ElfReader.Elf64_Shdr *)(shdrs + st_shndx * e_shentsize))->sh_addr + cur_sym->st_value; if (sym_name == "_start") { start = sym_addr; } else { stab.Add(sym_name, sym_addr, cur_sym->st_size); } } } } offset += cur_shdr->sh_entsize; } } sect_header += e_shentsize; } System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end loading symbols"); } System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: begin fixing relocations"); /* Iterate through relocations, fixing them up as we go */ sect_header = shdrs; for (uint i = 0; i < e_shnum; i++) { ElfReader.Elf64_Shdr *cur_shdr = (ElfReader.Elf64_Shdr *)sect_header; if (cur_shdr->sh_type == 0x9) { throw new NotImplementedException("rel sections not supported"); } if (cur_shdr->sh_type == 0x4) { /* SHT_RELA */ ElfReader.Elf64_Shdr *cur_symtab = (ElfReader.Elf64_Shdr *)(shdrs + cur_shdr->sh_link * e_shentsize); ElfReader.Elf64_Shdr *rela_sect = (ElfReader.Elf64_Shdr *)(shdrs + cur_shdr->sh_info * e_shentsize); /* Buffer loaded symbols */ ulong[] sym_buf = new ulong[cur_symtab->sh_size / cur_symtab->sh_entsize]; /* Load section */ byte *shdr_data = ReadStructure(s, cur_shdr->sh_offset, cur_shdr->sh_size); cur_shdr->sh_addr = (ulong)shdr_data; ulong offset = 0; while (offset < cur_shdr->sh_size) { ElfReader.Elf64_Rela *cur_rela = (ElfReader.Elf64_Rela *)(shdr_data + offset); ulong r_offset = rela_sect->sh_addr + cur_rela->r_offset; ulong r_sym = cur_rela->r_info >> 32; ulong r_type = cur_rela->r_info & 0xffffffff; long r_addend = cur_rela->r_addend; ElfReader.Elf64_Sym *rela_sym = (ElfReader.Elf64_Sym *)(cur_symtab->sh_addr + r_sym * cur_symtab->sh_entsize); uint st_bind = (rela_sym->st_info_other_shndx >> 4) & 0xf; ulong S = 0; /* Can use direct relocation via the symbol table if: * - not weak * - is defined */ if ((st_bind == 0 || st_bind == 1) && rela_sym->st_shndx != 0) { /* STB_LOCAL symbols have not been loaded into the symbol table * We need to use the value stored in the symbol table */ uint st_shndx = (rela_sym->st_info_other_shndx >> 16) & 0xffff; S = ((ElfReader.Elf64_Shdr *)(shdrs + e_shentsize * st_shndx))->sh_addr + rela_sym->st_value; } else { /* Get the symbol address from the symbol table if not already loaded */ if (sym_buf[r_sym] == 0) { ElfReader.Elf64_Shdr *link_sect = (ElfReader.Elf64_Shdr *)(shdrs + cur_symtab->sh_link * e_shentsize); string sym_name = new string((sbyte *)(link_sect->sh_addr + rela_sym->st_name)); S = stab.GetAddress(sym_name); if (S == 0) { throw new Exception("undefined reference to " + sym_name); } sym_buf[r_sym] = S; } else { S = sym_buf[r_sym]; } } /* Perform the relocation */ switch (r_type) { case 1: // R_X86_64_64: S + A { ulong c = S; if (r_addend > 0) { c += (ulong)r_addend; } else { c -= (ulong)(-r_addend); } *(ulong *)r_offset = c; } break; case 2: // R_X86_64_PC32: S + A - P { if (S > (ulong)long.MaxValue) { throw new Exception("S too large"); } if (r_offset > (ulong)long.MaxValue) { throw new Exception("P too large"); } long c = (long)S + r_addend - (long)r_offset; if (c < int.MinValue || c > int.MaxValue) { throw new Exception("Relocation truncated to fit"); } *(int *)r_offset = (int)c; } break; case 10: // R_X86_64_32: S + A { if (S > (ulong)long.MaxValue) { throw new Exception("S too large"); } long c = (long)S + r_addend; if (c < 0 || c > uint.MaxValue) { throw new Exception("Relocation truncated to fit"); } *(uint *)r_offset = (uint)c; } break; case 11: // R_X86_64_32S: S + A { if (S > (ulong)long.MaxValue) { throw new Exception("S too large"); } long c = (long)S + r_addend; if (c < int.MinValue || c > int.MaxValue) { throw new Exception("Relocation truncated to fit"); } *(int *)r_offset = (int)c; } break; default: throw new Exception("Unsupported relocation type: " + r_type.ToString()); } offset += cur_shdr->sh_entsize; } } sect_header += e_shentsize; } System.Diagnostics.Debugger.Log(0, null, "ElfFileReader.LoadObject: end fixing relocations"); if (start == 0) { // assume .to file - use entry point in header start = text_section + hdr_epoint; } return(start); }
internal override void Init(UIntPtr chunk_vaddr, UIntPtr chunk_length, Multiboot.Header mboot) { /* Set up an x86_64 environment */ /* Get the top of the stack * * cur_rbp points to prev_rbp (rbp within Main) * [prev_rbp + 8] is the rip within tload */ unsafe { ulong cur_rbp = libsupcs.x86_64.Cpu.RBP; ulong prev_rbp = *(ulong *)cur_rbp; init_exit_address = *(ulong *)(prev_rbp + 8); } // Enable sse (used for virtftnptr stores and loads) - see Intel 3a:13.1.4 ulong cr4 = libsupcs.x86_64.Cpu.Cr4; cr4 |= 0x200; // set cr4.OSFXSR libsupcs.x86_64.Cpu.Cr4 = cr4; cr4 |= 0x400; // set cr4.OSXMMXCPT libsupcs.x86_64.Cpu.Cr4 = cr4; ulong cr0 = libsupcs.x86_64.Cpu.Cr0; cr0 |= 0x4; cr0 ^= 0x4; // clear cr0.EM libsupcs.x86_64.Cpu.Cr0 = cr0; cr0 |= 0x2; // set cr0.MP libsupcs.x86_64.Cpu.Cr0 = cr0; cr0 |= (1 << 16); // set cr0.WP libsupcs.x86_64.Cpu.Cr0 = cr0; /* Set-up the MXCSR register * * We want to unmask all exceptions except the precision exception which is thrown if the * result of an operation cannot be stored with complete precision in an xmm register, * e.g. 1/3 - this happens frequently and is expected, so we don't not want to raise a #XM * exception here. The precision mask is bit 12. * * We set the rounding mode (bits 13 and 14) to 00b which is round to even as the Math.Round * function expects this mode * * All other bits are cleared. */ uint mxcsr = 0x1000; libsupcs.x86_64.Cpu.Mxcsr = mxcsr; /* Set up the memory managers */ bda_va = (ulong)chunk_vaddr; pmem_bitmap_va = bda_va + 0x1000; vmem_temppage_va = bda_va + 0x3000; vga_fb_va = bda_va + 0x4000; VirtMem = new Vmem(); PhysMem = new Pmem(); PhysMem.vmem = VirtMem; Multiboot.MachineMinorType_x86 bios = (Multiboot.MachineMinorType_x86)mboot.machine_minor_type; /* Set up the debug outputs */ DebugOutput = new SerialDebug(); /*if (mboot.has_vga && bios == Multiboot.MachineMinorType_x86.BIOS) * { * VirtMem.Map(0, 0x1000, bda_va, 0); // TODO: will this cause Map() to allocate a physical page? * //VirtMem.map_page(bda_va, 0x0); * BootInfoOutput = new Vga(bda_va, vga_fb_va, VirtMem); * } * else*/ BootInfoOutput = DebugOutput; /* Say hi */ Formatter.WriteLine("Tysos x86_64 architecture initialising", DebugOutput); // Only provide free pages to the memory allocator var fmem = new List <EarlyPageProvider.EPPRegion>(); // Iterate through each free block for (int i = 0; i < mboot.mmap.Length; i++) { Multiboot.MemoryMap cur_mmap = mboot.mmap[i]; if (IsUefiFreeMemory(cur_mmap.type)) { // do not use first page if (cur_mmap.base_addr < 0x1000) { if (cur_mmap.length < 0x1000) { continue; } cur_mmap.base_addr += 0x1000; cur_mmap.length -= 0x1000; } // x is the current address to add var x = util.align(cur_mmap.base_addr, 0x1000); while (x < cur_mmap.base_addr + cur_mmap.length - 0x1000) { // is x in a used block? bool in_used = false; for (int j = 0; j < mboot.mmap.Length; j++) { Multiboot.MemoryMap cur_mmap2 = mboot.mmap[j]; if (!IsUefiFreeMemory(cur_mmap2.type)) { if (x >= cur_mmap2.base_addr && x < (cur_mmap2.base_addr + cur_mmap2.length)) { in_used = true; // advance x x = cur_mmap2.base_addr + cur_mmap2.length; break; } } } if (in_used) { continue; // loop and check again if in used block } // Now we know we are not currently in a free block, // but need to ensure we don't cross over the boundary of the next // lowest block, so find this while (true) { // Find lowest used block greater than or equal to x ulong lowest_start = ulong.MaxValue; ulong lowest_len = 0; bool has_lowest = false; for (int j = 0; j < mboot.mmap.Length; j++) { Multiboot.MemoryMap cur_mmap2 = mboot.mmap[j]; if (!IsUefiFreeMemory(cur_mmap2.type)) { if (x < cur_mmap2.base_addr && cur_mmap2.base_addr < lowest_start) { has_lowest = true; lowest_start = cur_mmap2.base_addr; lowest_len = cur_mmap2.length; } } } // if the lowest block is outside the current block, ignore it if (lowest_start >= (cur_mmap.base_addr + cur_mmap.length)) { has_lowest = false; } // now, if has_lowest is true, we set the max here to it, else // we use the total size of the free block, aligned down ulong cur_block_max = (has_lowest ? lowest_start : (cur_mmap.base_addr + cur_mmap.length)) & ~0xFFFUL; // Debug out Formatter.Write("x86_64 PhysMem: adding block ", DebugOutput); Formatter.Write(x, "X", DebugOutput); Formatter.Write(" - ", DebugOutput); Formatter.Write(cur_block_max, "X", DebugOutput); Formatter.WriteLine(DebugOutput); fmem.Add(new EarlyPageProvider.EPPRegion { start = x, length = cur_block_max - x, used = 0 }); // subtract a page size cur_block_max -= 0x1000; while (x <= cur_block_max) { //PhysMem.ReleasePage(x); x += 0x1000; } Formatter.Write(x, "X", DebugOutput); Formatter.WriteLine(DebugOutput); // have we reached the end of the block? if (x >= cur_mmap.base_addr + cur_mmap.length - 0x1000) { break; } else { // if not, we have hit a used block, skip on to after it x = util.align(lowest_start + lowest_len, 0x1000); } } } } } // Use the free memory list to create a direct mapping of all physical memory var pp = new EarlyPageProvider(fmem); ((Vmem)VirtMem).GenerateDirectMapping(fmem, pp); Formatter.WriteLine("Direct physical memory mapping generated @ 0x0xffffff0000000000", DebugOutput); // Now create the actual physical memory manager with the free blocks PhysMem.vmem = VirtMem; for (int i = 0; i < fmem.Count; i++) { var fmemi = fmem[i]; if (fmemi.length > fmemi.used + 0x2000) { PhysMem.Release(fmemi.start, fmemi.length - fmemi.used); } } PageFault.pf_unwinder = new libsupcs.x86_64.Unwinder(); // Display success Formatter.Write("Allocated: ", DebugOutput); Formatter.Write(PhysMem.FreeSpace, DebugOutput); Formatter.WriteLine(" B", DebugOutput); VirtMem.pmem = PhysMem; Formatter.Write("x86_64: mboot.max_tysos: ", DebugOutput); Formatter.Write(mboot.max_tysos, "X", DebugOutput); Formatter.Write(", tysos_virtaddr: ", DebugOutput); Formatter.Write(mboot.tysos_virtaddr, "X", DebugOutput); Formatter.Write(", tysos_size: ", DebugOutput); Formatter.Write(mboot.tysos_size, "X", DebugOutput); Formatter.WriteLine(DebugOutput); /* Set up the virtual region allocator */ if (mboot.max_tysos != 0) { VirtualRegions = new Virtual_Regions(0, mboot.max_tysos); } else { VirtualRegions = new Virtual_Regions(mboot.tysos_virtaddr, mboot.tysos_size); } Formatter.WriteLine("x86_64: Virtual region allocator started", DebugOutput); VirtualRegions.Dump(DebugOutput); /* Set up interrupts */ ulong idt_start = VirtualRegions.Alloc(256 * 16, 0x1000, "idt"); VirtMem.Map(PhysMem.GetPage(), 0x1000, idt_start, VirtMem.FLAG_writeable); Interrupts = new Interrupts(idt_start); Formatter.WriteLine("x86_64: IDT allocated", DebugOutput); unsafe { // Install the page fault handler Interrupts.InstallHandler(14, new Interrupts.ISREC(PageFault.PFHandler)); // Install some default handlers Interrupts.InstallHandler(0, new Interrupts.ISR(Exceptions.DivideError_0_Handler)); Interrupts.InstallHandler(1, new Interrupts.ISR(Exceptions.DebugError_1_Handler)); Interrupts.InstallHandler(2, new Interrupts.ISR(Exceptions.NMIError_2_Handler)); Interrupts.InstallHandler(3, new Interrupts.ISR(Exceptions.BreakPoint_3_Handler)); Interrupts.InstallHandler(4, new Interrupts.ISR(Exceptions.OverflowError_4_Handler)); Interrupts.InstallHandler(5, new Interrupts.ISR(Exceptions.BoundCheckError_5_Handler)); Interrupts.InstallHandler(6, new Interrupts.ISR(Exceptions.InvalidOpcode_6_Handler)); Interrupts.InstallHandler(7, new Interrupts.ISR(Exceptions.DeviceNotPresentError_7_Handler)); Interrupts.InstallHandler(8, new Interrupts.ISREC(Exceptions.DoubleFault_8_Handler)); Interrupts.InstallHandler(10, new Interrupts.ISREC(Exceptions.TSSError_10_Handler)); Interrupts.InstallHandler(11, new Interrupts.ISREC(Exceptions.SegmentNotPresentError_11_Handler)); Interrupts.InstallHandler(12, new Interrupts.ISREC(Exceptions.StackeFaultError_12_Handler)); Interrupts.InstallHandler(13, new Interrupts.ISREC(Exceptions.GeneralProtection_13_Handler)); Interrupts.InstallHandler(16, new Interrupts.ISR(Exceptions.FPUError_16_Handler)); Interrupts.InstallHandler(17, new Interrupts.ISREC(Exceptions.AlignmentCheck_17_Handler)); Interrupts.InstallHandler(18, new Interrupts.ISR(Exceptions.MachineCheckError_18_Handler)); Interrupts.InstallHandler(19, new Interrupts.ISR(Exceptions.SIMD_19_Handler)); } Formatter.WriteLine("x86_64: Default exception handlers installed", DebugOutput); /* Set up the tss and IST stacks */ ulong tss; tss = VirtualRegions.Alloc(0x1000, 0x1000, "tss"); ulong[] ists = new ulong[7]; /* Create 2 ISTs with guard pages */ int ist_count = 2; ulong ist_size = 0x2000; ulong ist_guard_size = 0x1000; for (int i = 0; i < ist_count; i++) { ulong ist_base = VirtualRegions.Alloc(ist_size + ist_guard_size, 0x1000, "IST"); ists[i] = ist_base + ist_guard_size + ist_size; for (ulong addr = ist_base + ist_guard_size; addr < ist_base + ist_guard_size + ist_size; addr += 0x1000) { VirtMem.Map(PhysMem.GetPage(), 0x1000, addr, VirtMem.FLAG_writeable); } } VirtMem.Map(PhysMem.GetPage(), 0x1000, tss, VirtMem.FLAG_writeable); Tss.Init(tss, ists); Formatter.WriteLine("x86_64: TSS installed", DebugOutput); // Now inform the page fault and double fault handlers to use their own stack unsafe { Interrupts.InstallHandler(14, new Interrupts.ISREC(PageFault.PFHandler), 1); Interrupts.InstallHandler(8, new Interrupts.ISREC(Exceptions.DoubleFault_8_Handler), 2); } Formatter.WriteLine("x86_64: Page fault and double fault handlers installed", DebugOutput); /* Set up the current cpu */ Virtual_Regions.Region cpu_reg = VirtualRegions.AllocRegion(0x8000, 0x1000, "BSP cpu", 0, Virtual_Regions.Region.RegionType.CPU_specific, true); VirtMem.Map(PhysMem.GetPage(), 0x1000, cpu_reg.start, VirtMem.FLAG_writeable); x86_64_cpu bsp = new x86_64_cpu(cpu_reg); bsp.InitCurrentCpu(); cpu_structure_setup = true; // Set up the page fault handler's stack switching mechanism /*ulong pfault_ist_block = VirtualRegions.Alloc(VirtMem.page_size * 7, VirtMem.page_size, "PageFault ISTs"); * List<ulong> pfault_ists = new List<ulong>(); * pfault_ists.Add(ist_block + VirtMem.page_size); * for (int i = 0; i < 7; i++) * { * VirtMem.map_page(pfault_ist_block + (ulong)i * VirtMem.page_size); * pfault_ists.Add(pfault_ist_block + (ulong)(i + 1) * VirtMem.page_size); * } * PageFault.cur_ist_idx = 0; * PageFault.tss_addr = tss_start; * PageFault.ist1_offset = (ulong)(((libsupcs.TysosField)typeof(tysos.x86_64.Tss.Tss_struct).GetField("ist1")).Offset); * PageFault.ist_stack = pfault_ists;*/ /* Test the gengc */ Formatter.WriteLine("x86_64: testing gengc", Program.arch.DebugOutput); gc.gengc.heap = new gc.gengc(); Formatter.Write("gengc object created @ ", Program.arch.DebugOutput); Formatter.Write(libsupcs.CastOperations.ReinterpretAsUlong(gc.gengc.heap), "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); unsafe { gc.gengc.heap.Init((void *)heap_small_start, (void *)heap_long_end); /* Initial roots are the current stack, all static fields and the original heap */ gc.gengc.heap.AddRoots((byte *)mboot.stack_low, (byte *)mboot.stack_high); gc.gengc.heap.AddRoots((byte *)mboot.tysos_static_start, (byte *)mboot.tysos_static_end); gc.gengc.heap.AddRoots((byte *)mboot.heap_start, (byte *)mboot.heap_end); } Formatter.WriteLine("heap initialized", Program.arch.DebugOutput); int[] test_array = new int[] { 8, 8, 8, 8, 8, 8, 8 }; Formatter.WriteLine("test array created", Program.arch.DebugOutput); gc.gc.Heap = gc.gc.HeapType.GenGC; Formatter.WriteLine("heap set to gengc", Program.arch.DebugOutput); for (int i = 0; i < test_array.Length; i++) { Formatter.Write((ulong)i, Program.arch.DebugOutput); Formatter.Write(": size ", Program.arch.DebugOutput); Formatter.Write((ulong)test_array[i], Program.arch.DebugOutput); Formatter.Write(" returns ", Program.arch.DebugOutput); ulong addr = gc.gc.Alloc((ulong)test_array[i]); Formatter.Write(addr, "X", Program.arch.DebugOutput); Formatter.WriteLine(Program.arch.DebugOutput); } // Now point the heap to its proper location //ulong heap_start = 0xFFFF800000000000; //ulong heap_len = 0xFFFFFF8000000000 - heap_start; //gc.heap.Init(heap_start, heap_len); /*Formatter.Write("x86_64: setting heap to final location: ", Program.arch.DebugOutput); * Formatter.Write(heap_small_start, "X", Program.arch.DebugOutput); * Formatter.Write(" - ", Program.arch.DebugOutput); * Formatter.Write(heap_long_end, "X", Program.arch.DebugOutput); * Formatter.Write(" ", Program.arch.DebugOutput); * #if NO_BOEHM #if NO_TYSOSGC * gc.gc.Heap = gc.gc.HeapType.Startup; * gc.simple_heap.Init(heap_small_start, heap_long_end); #else * gc.gc.Heap = gc.gc.HeapType.TysosGC; * gc.heap.Init(heap_small_cutoff, heap_small_start, heap_small_end, heap_long_start, heap_long_end); #endif #else * gc.boehm.InitHeap(heap_small_start, heap_long_end); * gc.gc.Heap = gc.gc.HeapType.BoehmGC; #endif * * Formatter.WriteLine("done", Program.arch.DebugOutput); * Formatter.Write("x86_64: new heap of type ", Program.arch.DebugOutput); * Formatter.WriteLine(gc.gc.Heap.ToString(), Program.arch.DebugOutput); */ /* Test a dictionary */ var dtest = new Dictionary <int, int>(new Program.MyGenericEqualityComparer <int>()); dtest[2] = 42; var dout = dtest[2]; Formatter.Write("x86_64: dictionary test: ", DebugOutput); Formatter.Write((ulong)dout, DebugOutput); Formatter.WriteLine(DebugOutput); /* Test enum comparer */ var enumcomp = new metadata.GenericEqualityComparerEnum <metadata.MetadataStream.TableId>(); if (enumcomp.Equals(metadata.MetadataStream.TableId.Assembly, metadata.MetadataStream.TableId.Assembly)) { Formatter.WriteLine("x86_64: enumcomp test passed", DebugOutput); } else { Formatter.WriteLine("x86_64: enumcomp test failed", DebugOutput); } /* Test an enum dictionary */ var dtest2 = new Dictionary <metadata.MetadataStream.TableId, int>(new metadata.GenericEqualityComparerEnum <metadata.MetadataStream.TableId>()); dtest2[metadata.MetadataStream.TableId.TypeDef] = 42; var dout2 = dtest2[metadata.MetadataStream.TableId.TypeDef]; Formatter.Write("x86_64: enum dictionary test: ", DebugOutput); Formatter.Write((ulong)dout, DebugOutput); Formatter.WriteLine(DebugOutput); /* Test MetadataStream ctor */ Formatter.WriteLine("x86_64: Testing metadata ctor", DebugOutput); var mtest = new metadata.MetadataStream(); Formatter.WriteLine("x86_64: done", DebugOutput); /* Set-up the architecture for the JIT engine */ jit.Jit.t = libtysila5.target.Target.targets["x86_64"]; jit.Jit.t.Options["mcmodel"] = "large"; jit.Jit.t.InitIntcalls(); jit.Jit.bness = binary_library.Bitness.Bits64; jit.Jit.jsa = new JitStubAssembler(); /* Initialize firmware */ switch (bios) { case Multiboot.MachineMinorType_x86.UEFI: fwconf = new UEFI(VirtualRegions, VirtMem, mboot.virt_bda); break; case Multiboot.MachineMinorType_x86.BIOS: fwconf = new BIOS(VirtualRegions, VirtMem, mboot.virt_bda); break; default: throw new Exception("Unsupported firmware: " + bios.ToString()); } /* Load ACPI tables */ Acpi acpi = new Acpi(VirtualRegions, VirtMem, fwconf); //tysos.x86_64.Acpi acpi = new tysos.x86_64.Acpi(VirtualRegions, VirtMem, // (bios == Multiboot.MachineMinorType_x86.BIOS) ? bda_va : mboot.virt_bda, // bios); /* Disable the PIC if we have one */ if ((acpi.Apic != null) && (acpi.Apic.Has8259)) { tysos.x86_64.PIC_8295a.Disable(); } /* Set up the local apic for the current processor */ tysos.x86_64.LApic bsp_lapic = new tysos.x86_64.LApic(VirtualRegions, VirtMem); /* Calibrate the lapic */ if (acpi.Hpet == null) { bsp_lapic.CalibrateDirectly(133000000.0); } else { tysos.x86_64.Hpet hpet = new tysos.x86_64.Hpet(VirtualRegions, VirtMem, acpi.Hpet.paddr); bsp_lapic.CalibrateTimerWithHpet(hpet, 1000000); } bsp_lapic.SetSpuriousVector(0x60); unsafe { Interrupts.InstallHandler(0x60, new Interrupts.ISR(tysos.x86_64.LApic.SpuriousApicInterrupt)); } bsp_lapic.SetTimer(true, 100.0, 0x40); // 10 ms timer //bsp_lapic.SetTimer(true, 0x144b50, 0x40); // 10ms timer with 133 Mhz bus and divisor 1, interrupt vector 0x40 unsafe { Interrupts.InstallHandler(0x40, new Interrupts.ISR(tysos.x86_64.LApic.TimerInterrupt)); } SchedulerTimer = bsp_lapic; /* Set up the current cpu */ bsp.CurrentLApic = bsp_lapic; Processors = new List <Cpu>(); Processors.Add(Program.arch.CurrentCpu); /* Set up the task switcher */ Switcher = new tysos.x86_64.TaskSwitcher(); /* Now we have a working heap we can set up the rest of the physical memory */ //SetUpHighMemory(this.PhysMem, mboot); /* Finally, create a list of parameters for handing to the system device enumerator */ if (acpi == null) { throw new Exception("ACPI required"); } ps = new List <tysos.lib.File.Property>(); ps.Add(new tysos.lib.File.Property { Name = "driver", Value = "acpipc" }); foreach (Acpi.AcpiTable table in acpi.tables) { string tab_name = "table_" + table.signature.ToString("X8"); ps.Add(new tysos.lib.File.Property { Name = tab_name, Value = new VirtualMemoryResource64(table.start_vaddr, table.length) }); } ps.Add(new tysos.lib.File.Property { Name = "vmem", Value = new VirtualMemoryResource64(VirtualRegions.devs.start, VirtualRegions.devs.length) }); ps.Add(new tysos.lib.File.Property { Name = "pmem", Value = new PhysicalMemoryResource64(0, UInt64.MaxValue) }); ps.Add(new tysos.lib.File.Property { Name = "io", Value = new x86_64.IOResource(0, 0x10000) }); Formatter.WriteLine("x86_64: Arch initialized", Program.arch.DebugOutput); }
public unsafe BIOS(Virtual_Regions vreg, VirtMem vmem, ulong system_table_paddr) { acpi_table = (void *)system_table_paddr; }