| #include <string.h> |
| #include <stddef.h> |
| #include "hwrpb.h" |
| #include "osf.h" |
| #include "uart.h" |
| |
| #define PAGE_SHIFT 13 |
| #define PAGE_SIZE (1ul << PAGE_SHIFT) |
| #define PAGE_OFFSET 0xfffffc0000000000UL |
| |
| #define HZ 1024 |
| |
| struct pcb_struct pal_pcb __attribute__((section(".sbss"))); |
| |
| unsigned long page_dir[1024] __attribute__((aligned(PAGE_SIZE))); |
| unsigned long page_L1[1024] __attribute__((aligned(PAGE_SIZE))); |
| unsigned long page_L2[1024][1024] __attribute__((aligned(PAGE_SIZE))); |
| |
| struct hwrpb_combine { |
| struct hwrpb_struct hwrpb; |
| struct percpu_struct processor; |
| struct memdesc_struct md; |
| struct memclust_struct mc[2]; |
| } hwrpb __attribute__((section(".data.hwrpb"))) = { |
| /* This is HWRPB\0\0\0. */ |
| .hwrpb.id = 0x4857525042000000, |
| |
| .hwrpb.size = sizeof(struct hwrpb_struct), |
| .hwrpb.pagesize = PAGE_SIZE, |
| .hwrpb.ssn = "QEMU", |
| |
| /* ??? This should match TARGET_PHYS_ADDR_SPACE_BITS from qemu. */ |
| .hwrpb.pa_bits = 44, |
| |
| /* ??? Should we be truethful and say 1 asn, or simply pretend we |
| have ASNs but ignore them? */ |
| .hwrpb.max_asn = 127, |
| |
| /* For now, hard-code emulation of sx164. */ |
| .hwrpb.cpuid = PCA56_CPU, |
| .processor.type = PCA56_CPU, |
| .hwrpb.sys_type = ST_DEC_EB164, |
| .hwrpb.sys_variation = 15 << 10, |
| .hwrpb.sys_revision = 0, |
| |
| .hwrpb.intr_freq = HZ * 4096, |
| |
| /* ??? What the hell should we put here. Measure like the kernel will? */ |
| .hwrpb.cycle_freq = 400000000, |
| |
| .hwrpb.vptb = 0x200000000UL, |
| .hwrpb.nr_processors = 1, |
| .hwrpb.processor_size = sizeof(struct percpu_struct), |
| .hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor), |
| |
| .hwrpb.mddt_offset = offsetof(struct hwrpb_combine, md), |
| .md.numclusters = 2, |
| .mc[0].usage = 2 |
| }; |
| |
| static unsigned long |
| init_cpuid (void) |
| { |
| unsigned long implver, amask; |
| |
| __asm ("implver %0" : "=r"(implver)); |
| __asm ("amask %1,%0" : "=r"(amask) : "r"(-1)); |
| amask = ~amask; |
| |
| switch (implver) |
| { |
| case 0: /* EV4 */ |
| return EV4_CPU; |
| |
| case 1: /* EV5 */ |
| if ((amask & 0x101) == 0x101) /* MAX + BWX */ |
| return PCA56_CPU; |
| if (amask & 1) /* BWX */ |
| return EV56_CPU; |
| return EV5_CPU; |
| |
| case 2: /* EV6 */ |
| if (amask & 4) /* CIX */ |
| return EV67_CPU; |
| return EV6_CPU; |
| } |
| return 0; |
| } |
| |
| static void |
| hwrpb_update_checksum (void) |
| { |
| unsigned long sum = 0, *l; |
| for (l = (unsigned long *) &hwrpb.hwrpb; l < &hwrpb.hwrpb.chksum; ++l) |
| sum += *l; |
| hwrpb.hwrpb.chksum = sum; |
| } |
| |
| static void |
| init_hwrpb (unsigned long memsize) |
| { |
| unsigned long end, pal_pages; |
| |
| hwrpb.hwrpb.phys_addr = (unsigned int)(unsigned long)&hwrpb; |
| |
| /* ??? For some reason GCC wants to use a LITERAL relocation for |
| _end instead of gp-relative relocations. */ |
| __asm ("ldah %0,_end($gp) !gprelhigh\n\tlda %0,_end(%0) !gprellow" |
| : "=r"(end)); |
| |
| pal_pages = ((unsigned int)end + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| |
| hwrpb.mc[0].numpages = pal_pages; |
| hwrpb.mc[1].start_pfn = pal_pages; |
| hwrpb.mc[1].numpages = (memsize >> PAGE_SHIFT) - pal_pages; |
| |
| hwrpb_update_checksum (); |
| } |
| |
| static void |
| init_pcb (void) |
| { |
| pal_pcb.ptbr = (unsigned int)(unsigned long)page_dir; |
| pal_pcb.flags = 1; |
| } |
| |
| static inline unsigned long |
| build_pte (void *page) |
| { |
| unsigned long bits; |
| |
| bits = ((unsigned long)page - PAGE_OFFSET) << (32 - PAGE_SHIFT); |
| bits |= _PAGE_VALID; |
| |
| return bits; |
| } |
| |
| static inline void * |
| pte_page (unsigned long pte) |
| { |
| return (void *)((pte >> 32 << PAGE_SHIFT) + PAGE_OFFSET); |
| } |
| |
| static void |
| set_pte (unsigned long addr, void *page) |
| { |
| unsigned long *pt = page_dir; |
| unsigned long index; |
| |
| index = (addr >> (PAGE_SHIFT+20)) % 1024; |
| if (pt[index] != 0) |
| pt = pte_page (pt[index]); |
| else |
| __builtin_trap(); |
| |
| index = (addr >> (PAGE_SHIFT+10)) % 1024; |
| if (pt[index] != 0) |
| pt = pte_page (pt[index]); |
| else |
| __builtin_trap(); |
| |
| index = (addr >> PAGE_SHIFT) % 1024; |
| pt[index] = build_pte (page); |
| } |
| |
| static void |
| init_page_table (unsigned long memsize, unsigned long pal_pages) |
| { |
| unsigned long i, addr, max_addr, page; |
| |
| page_dir[0] = build_pte (page_L1); |
| |
| for (i = 0; i < 1024; ++i) |
| page_L1[i] = build_pte (page_L2[i]); |
| |
| set_pte ((unsigned long)INIT_HWRPB, &hwrpb); |
| |
| addr = 0x20000000ul; |
| max_addr = 1ul << (PAGE_SHIFT + 20); |
| page = pal_pages << PAGE_SHIFT; |
| |
| while (addr < max_addr && page < memsize) |
| { |
| set_pte (addr, (void *)(PAGE_OFFSET + page)); |
| addr += PAGE_SIZE; |
| page += PAGE_SIZE; |
| } |
| |
| /* SRM places the self-map for the VPTBR in the second entry. */ |
| page_dir[1] = build_pte (page_dir); |
| |
| /* Write the SRM vptptr. */ |
| { |
| register unsigned long a0 __asm__("$16") = max_addr; |
| __asm ("call_pal 0x2d" : : "r"(a0)); |
| } |
| } |
| |
| void |
| do_start(unsigned long memsize) |
| { |
| init_hwrpb (memsize); |
| init_pcb (); |
| init_page_table (memsize, hwrpb.mc[0].numpages); |
| |
| uart_init (); |
| uart_puts (COM1, "Hello, World!\n"); |
| |
| while (1) ; |
| } |