| #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 VPTPTR 0xfffffffe00000000UL |
| |
| #define PA(VA) ((unsigned long)(VA) & 0xfffffffffful) |
| #define VA(PA) ((void *)(PA) + PAGE_OFFSET) |
| |
| #define HZ 1024 |
| |
| struct hwrpb_combine { |
| struct hwrpb_struct hwrpb; |
| struct percpu_struct processor; |
| struct memdesc_struct md; |
| struct memclust_struct mc[2]; |
| }; |
| |
| extern char stack[PAGE_SIZE] __attribute__((section(".sbss"))); |
| extern char _end[] __attribute__((visibility("hidden"), nocommon)); |
| |
| struct pcb_struct pcb __attribute__((section(".sbss"))); |
| |
| static unsigned long page_dir[1024] __attribute__((aligned(PAGE_SIZE))); |
| |
| /* The HWRPB must be aligned because it is exported at INIT_HWRPB. */ |
| struct hwrpb_combine hwrpb __attribute__((aligned(PAGE_SIZE))); |
| |
| static void *last_alloc; |
| |
| static void * |
| alloc (unsigned long size, unsigned long align) |
| { |
| void *p = (void *)(((unsigned long)last_alloc + align - 1) & ~(align - 1)); |
| last_alloc = p + size; |
| return memset (p, 0, size); |
| } |
| |
| static inline unsigned long |
| pt_index(unsigned long addr, int level) |
| { |
| return (addr >> (PAGE_SHIFT + (10 * level))) & 0x3ff; |
| } |
| |
| static inline unsigned long |
| build_pte (void *page) |
| { |
| unsigned long bits; |
| |
| bits = PA((unsigned long)page) << (32 - PAGE_SHIFT); |
| bits += _PAGE_VALID | _PAGE_KRE | _PAGE_KWE; |
| |
| return bits; |
| } |
| |
| static inline void * |
| pte_page (unsigned long pte) |
| { |
| return VA(pte >> 32 << PAGE_SHIFT); |
| } |
| |
| static void |
| set_pte (unsigned long addr, void *page) |
| { |
| unsigned long *pt = page_dir; |
| unsigned long index; |
| |
| index = pt_index(addr, 2); |
| if (pt[index] != 0) |
| pt = pte_page (pt[index]); |
| else |
| { |
| unsigned long *npt = alloc(PAGE_SIZE, PAGE_SIZE); |
| pt[index] = build_pte (npt); |
| pt = npt; |
| } |
| |
| index = pt_index(addr, 1); |
| if (pt[index] != 0) |
| pt = pte_page (pt[index]); |
| else |
| { |
| unsigned long *npt = alloc(PAGE_SIZE, PAGE_SIZE); |
| pt[index] = build_pte (npt); |
| pt = npt; |
| } |
| |
| index = pt_index(addr, 0); |
| pt[index] = build_pte (page); |
| } |
| |
| static void |
| init_page_table(void) |
| { |
| /* Install the self-reference for the virtual page table base register. */ |
| page_dir[pt_index(VPTPTR, 2)] = build_pte(page_dir); |
| |
| set_pte ((unsigned long)INIT_HWRPB, &hwrpb); |
| |
| /* ??? SRM maps some amount of memory at 0x20000000 for use by programs |
| started from the console prompt. Including the bootloader. While |
| we're emulating MILO, don't bother as we jump straight to the kernel |
| loaded into KSEG. */ |
| } |
| |
| static inline unsigned long |
| init_cpuid (void) |
| { |
| unsigned long implver, amask; |
| |
| implver = __builtin_alpha_implver(); |
| amask = ~__builtin_alpha_amask(-1); |
| |
| 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 |
| init_hwrpb (unsigned long memsize) |
| { |
| unsigned long pal_pages; |
| |
| hwrpb.hwrpb.phys_addr = PA(&hwrpb); |
| |
| /* Yes, the 'HWRPB' magic is in big-endian byte ordering. */ |
| hwrpb.hwrpb.id = ( (long)'H' << 56 |
| | (long)'W' << 48 |
| | (long)'R' << 40 |
| | (long)'P' << 32 |
| | (long)'B' << 24); |
| |
| hwrpb.hwrpb.size = sizeof(struct hwrpb_struct); |
| |
| /* The inclusion of MILO here tells the Linux kernel that we do |
| not (yet) support any of the extended console support routines |
| that are in SRM. */ |
| ((int *)hwrpb.hwrpb.ssn)[0] = ( 'M' << 0 |
| | 'I' << 8 |
| | 'L' << 16 |
| | 'O' << 24); |
| ((int *)hwrpb.hwrpb.ssn)[1] = ( ' ' << 0 |
| | 'Q' << 8 |
| | 'E' << 16 |
| | 'M' << 24); |
| ((int *)hwrpb.hwrpb.ssn)[2] = ( 'U' << 0); |
| |
| /* For now, hard-code emulation of sx164. */ |
| hwrpb.hwrpb.cpuid = PCA56_CPU; |
| hwrpb.hwrpb.pagesize = PAGE_SIZE; |
| hwrpb.hwrpb.pa_bits = 40; |
| hwrpb.hwrpb.max_asn = 127; |
| hwrpb.hwrpb.sys_type = ST_DEC_EB164; |
| hwrpb.hwrpb.sys_variation = 15 << 10; |
| hwrpb.hwrpb.sys_revision = 0; |
| hwrpb.processor.type = PCA56_CPU; |
| |
| hwrpb.hwrpb.intr_freq = HZ * 4096; |
| |
| /* ??? What the hell should we put here. Measure like the kernel will? */ |
| hwrpb.hwrpb.cycle_freq = 400000000; |
| |
| hwrpb.hwrpb.vptb = VPTPTR; |
| |
| hwrpb.hwrpb.nr_processors = 1; |
| hwrpb.hwrpb.processor_size = sizeof(struct percpu_struct); |
| hwrpb.hwrpb.processor_offset = offsetof(struct hwrpb_combine, processor); |
| |
| hwrpb.hwrpb.mddt_offset = offsetof(struct hwrpb_combine, md); |
| hwrpb.md.numclusters = 2; |
| |
| pal_pages = (PA(last_alloc) + PAGE_SIZE - 1) >> PAGE_SHIFT; |
| |
| hwrpb.mc[0].numpages = pal_pages; |
| hwrpb.mc[0].usage = 1; |
| hwrpb.mc[1].start_pfn = pal_pages; |
| hwrpb.mc[1].numpages = (memsize >> PAGE_SHIFT) - pal_pages; |
| |
| { |
| 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_pcb (void) |
| { |
| pcb.ksp = (unsigned long)stack + sizeof(stack); |
| pcb.ptbr = PA(page_dir) >> PAGE_SHIFT; |
| pcb.flags = 1; /* FEN */ |
| } |
| |
| void |
| do_hello(void) |
| { |
| uart_puts(COM1, "Hello, World!\n"); |
| asm ("halt"); |
| __builtin_unreachable (); |
| } |
| |
| void |
| do_start(unsigned long memsize, void (*kernel_entry)(void)) |
| { |
| last_alloc = _end; |
| |
| init_page_table(); |
| init_hwrpb(memsize); |
| init_pcb(); |
| uart_init(); |
| |
| { |
| register int variant __asm__("$16") = 2; /* OSF/1 PALcode */ |
| register void (*pc)(void) __asm__("$17"); |
| register unsigned long pa_pcb __asm__("$18"); |
| register unsigned long vptptr __asm__("$19"); |
| |
| pc = (kernel_entry ? kernel_entry : do_hello); |
| pa_pcb = PA(&pcb); |
| vptptr = VPTPTR; |
| asm("call_pal 0x0a" : : "r"(variant), "r"(pc), "r"(pa_pcb), "r"(vptptr)); |
| } |
| __builtin_unreachable (); |
| } |