예제 #1
0
파일: Resource.cs 프로젝트: jncronin/tysos
 static void SendLapicEOI(x86_64_cpu c)
 {
     c.CurrentLApic.SendEOI();
 }
예제 #2
0
파일: Arch.cs 프로젝트: jncronin/tysos
        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);
        }