From bda0e064affa295871e6d1e062aefd9a52b270cc Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Sun, 30 Aug 2009 13:21:25 +0300 Subject: [PATCH] Added new subarch, platform, cpu, driver variants for configuration testing. New files are an exact copy of pb926 files, but will help in testing different configurations and compilations. --- configs/arm.cml | 3 +- include/l4/arch/arm/v6/arm1136/cpu.h | 13 + include/l4/arch/arm/v6/arm11mpcore/cpu.h | 13 + include/l4/arch/arm/v6/mm.h | 160 +++++ include/l4/arch/arm/v6/mmu_ops.h | 34 + include/l4/arch/arm/v6/mutex.h | 0 include/l4/arch/arm/v7/cortexa8/cpu.h | 13 + include/l4/arch/arm/v7/mm.h | 160 +++++ include/l4/arch/arm/v7/mmu_ops.h | 34 + include/l4/arch/arm/v7/mutex.h | 0 include/l4/drivers/irq/gic.h | 13 - include/l4/drivers/irq/gic/gic.h | 56 ++ include/l4/platform/ab926/irq.h | 27 + include/l4/platform/ab926/offsets.h | 60 ++ include/l4/platform/ab926/platform.h | 22 + include/l4/platform/ab926/printascii.h | 15 + include/l4/platform/ab926/uart.h | 20 + include/l4/platform/eb/irq.h | 27 + include/l4/platform/eb/offsets.h | 60 ++ include/l4/platform/eb/platform.h | 22 + include/l4/platform/eb/printascii.h | 15 + include/l4/platform/eb/uart.h | 20 + include/l4/platform/pb11mpcore/irq.h | 27 + include/l4/platform/pb11mpcore/offsets.h | 60 ++ include/l4/platform/pb11mpcore/platform.h | 22 + include/l4/platform/pb11mpcore/printascii.h | 15 + include/l4/platform/pb11mpcore/uart.h | 20 + include/l4/platform/pba8/irq.h | 27 + include/l4/platform/pba8/offsets.h | 60 ++ include/l4/platform/pba8/platform.h | 22 + include/l4/platform/pba8/printascii.h | 15 + include/l4/platform/pba8/uart.h | 20 + src/arch/arm/v6/mm.c | 725 ++++++++++++++++++- src/arch/arm/v6/mmu_ops.S | 155 +++++ src/arch/arm/v6/mutex.S | 90 +++ src/arch/arm/v7/mm.c | 726 ++++++++++++++++++++ src/arch/arm/v7/mmu_ops.S | 155 +++++ src/arch/arm/v7/mutex.S | 90 +++ src/drivers/irq/gic/gic.c | 104 +++ src/platform/ab926/irq.c | 57 ++ src/platform/ab926/platform.c | 62 ++ src/platform/ab926/printascii.S | 110 +++ src/platform/ab926/timer.c | 28 + src/platform/ab926/uart.c | 28 + src/platform/eb/irq.c | 57 ++ src/platform/eb/platform.c | 62 ++ src/platform/eb/printascii.S | 110 +++ src/platform/eb/timer.c | 28 + src/platform/eb/uart.c | 28 + src/platform/pb11mpcore/irq.c | 57 ++ src/platform/pb11mpcore/platform.c | 62 ++ src/platform/pb11mpcore/printascii.S | 110 +++ src/platform/pb11mpcore/timer.c | 28 + src/platform/pb11mpcore/uart.c | 28 + src/platform/pba8/irq.c | 57 ++ src/platform/pba8/platform.c | 62 ++ src/platform/pba8/printascii.S | 110 +++ src/platform/pba8/timer.c | 28 + src/platform/pba8/uart.c | 28 + 59 files changed, 4243 insertions(+), 17 deletions(-) create mode 100644 include/l4/arch/arm/v6/arm1136/cpu.h create mode 100644 include/l4/arch/arm/v6/arm11mpcore/cpu.h create mode 100644 include/l4/arch/arm/v6/mm.h create mode 100644 include/l4/arch/arm/v6/mmu_ops.h create mode 100644 include/l4/arch/arm/v6/mutex.h create mode 100644 include/l4/arch/arm/v7/cortexa8/cpu.h create mode 100644 include/l4/arch/arm/v7/mm.h create mode 100644 include/l4/arch/arm/v7/mmu_ops.h create mode 100644 include/l4/arch/arm/v7/mutex.h delete mode 100644 include/l4/drivers/irq/gic.h create mode 100644 include/l4/drivers/irq/gic/gic.h create mode 100644 include/l4/platform/ab926/irq.h create mode 100644 include/l4/platform/ab926/offsets.h create mode 100644 include/l4/platform/ab926/platform.h create mode 100644 include/l4/platform/ab926/printascii.h create mode 100644 include/l4/platform/ab926/uart.h create mode 100644 include/l4/platform/eb/irq.h create mode 100644 include/l4/platform/eb/offsets.h create mode 100644 include/l4/platform/eb/platform.h create mode 100644 include/l4/platform/eb/printascii.h create mode 100644 include/l4/platform/eb/uart.h create mode 100644 include/l4/platform/pb11mpcore/irq.h create mode 100644 include/l4/platform/pb11mpcore/offsets.h create mode 100644 include/l4/platform/pb11mpcore/platform.h create mode 100644 include/l4/platform/pb11mpcore/printascii.h create mode 100644 include/l4/platform/pb11mpcore/uart.h create mode 100644 include/l4/platform/pba8/irq.h create mode 100644 include/l4/platform/pba8/offsets.h create mode 100644 include/l4/platform/pba8/platform.h create mode 100644 include/l4/platform/pba8/printascii.h create mode 100644 include/l4/platform/pba8/uart.h create mode 100644 src/arch/arm/v6/mutex.S create mode 100644 src/arch/arm/v7/mm.c create mode 100644 src/arch/arm/v7/mmu_ops.S create mode 100644 src/arch/arm/v7/mutex.S create mode 100644 src/drivers/irq/gic/gic.c create mode 100644 src/platform/ab926/irq.c create mode 100644 src/platform/ab926/platform.c create mode 100644 src/platform/ab926/printascii.S create mode 100644 src/platform/ab926/timer.c create mode 100644 src/platform/ab926/uart.c create mode 100644 src/platform/eb/irq.c create mode 100644 src/platform/eb/platform.c create mode 100644 src/platform/eb/printascii.S create mode 100644 src/platform/eb/timer.c create mode 100644 src/platform/eb/uart.c create mode 100644 src/platform/pb11mpcore/irq.c create mode 100644 src/platform/pb11mpcore/platform.c create mode 100644 src/platform/pb11mpcore/printascii.S create mode 100644 src/platform/pb11mpcore/timer.c create mode 100644 src/platform/pb11mpcore/uart.c create mode 100644 src/platform/pba8/irq.c create mode 100644 src/platform/pba8/platform.c create mode 100644 src/platform/pba8/printascii.S create mode 100644 src/platform/pba8/timer.c create mode 100644 src/platform/pba8/uart.c diff --git a/configs/arm.cml b/configs/arm.cml index e5dc5ec..7d2ce4d 100644 --- a/configs/arm.cml +++ b/configs/arm.cml @@ -111,7 +111,8 @@ unless ARM_SUBARCH_V7 suppress ARM_CPU_CORTEXA8 # NOTE: Unlike menus, choices dont take { sym } model of visibility # dependencies. Instead, a choice symbol is declared in a menu, and # suppress statement is used to make sym visible, instead of a -# { sym } model under the choices. +# { sym } model under the choices. (See manual for { sym } usage). + unless ARM_SUBARCH_V5 suppress ARM_PLATFORM_PB926 unless ARCH_ARM suppress arm_menu unless ARM_PLATFORM_PB926 or ARM_PLATFORM_AB926 suppress DRIVER_IRQ_PL190 diff --git a/include/l4/arch/arm/v6/arm1136/cpu.h b/include/l4/arch/arm/v6/arm1136/cpu.h new file mode 100644 index 0000000..2d11b60 --- /dev/null +++ b/include/l4/arch/arm/v6/arm1136/cpu.h @@ -0,0 +1,13 @@ +/* + * + * Copyright (C) 2005 Bahadir Balban + * + */ +#ifndef __ARM926EJS__H__ +#define __ARM926EJS__H__ + + + + + +#endif /* __ARM926EJS__H__ */ diff --git a/include/l4/arch/arm/v6/arm11mpcore/cpu.h b/include/l4/arch/arm/v6/arm11mpcore/cpu.h new file mode 100644 index 0000000..2d11b60 --- /dev/null +++ b/include/l4/arch/arm/v6/arm11mpcore/cpu.h @@ -0,0 +1,13 @@ +/* + * + * Copyright (C) 2005 Bahadir Balban + * + */ +#ifndef __ARM926EJS__H__ +#define __ARM926EJS__H__ + + + + + +#endif /* __ARM926EJS__H__ */ diff --git a/include/l4/arch/arm/v6/mm.h b/include/l4/arch/arm/v6/mm.h new file mode 100644 index 0000000..35b763b --- /dev/null +++ b/include/l4/arch/arm/v6/mm.h @@ -0,0 +1,160 @@ +/* + * ARM v5-specific virtual memory details + * + * Copyright (C) 2007 Bahadir Balban + */ +#ifndef __V5_MM_H__ +#define __V5_MM_H__ + +/* ARM specific definitions */ +#define VIRT_MEM_START 0 +#define VIRT_MEM_END 0xFFFFFFFF +#define ARM_SECTION_SIZE SZ_1MB +#define ARM_SECTION_MASK (ARM_SECTION_SIZE - 1) +#define ARM_SECTION_BITS 20 +#define ARM_PAGE_SIZE SZ_4K +#define ARM_PAGE_MASK 0xFFF +#define ARM_PAGE_BITS 12 + +#define PGD_SIZE SZ_4K * 4 +#define PGD_ENTRY_TOTAL SZ_4K +#define PGD_TYPE_MASK 0x3 +#define PGD_COARSE_ALIGN_MASK 0xFFFFFC00 +#define PGD_SECTION_ALIGN_MASK 0xFFF00000 +#define PGD_FINE_ALIGN_MASK 0xFFFFF000 +#define PGD_TYPE_FAULT 0 +#define PGD_TYPE_COARSE 1 +#define PGD_TYPE_SECTION 2 +#define PGD_TYPE_FINE 3 + +#define PMD_TYPE_MASK 0x3 +#define PMD_TYPE_FAULT 0 +#define PMD_TYPE_LARGE 1 +#define PMD_TYPE_SMALL 2 +#define PMD_TYPE_TINY 3 + +/* Permission field offsets */ +#define SECTION_AP0 10 + +#define PMD_SIZE SZ_1K +#define PMD_ENTRY_TOTAL 256 +#define PMD_MAP_SIZE SZ_1MB + +/* Type-checkable page table elements */ +typedef u32 pgd_t; +typedef u32 pmd_t; +typedef u32 pte_t; + +/* Page global directory made up of pgd_t entries */ +typedef struct pgd_table { + pgd_t entry[PGD_ENTRY_TOTAL]; +} pgd_table_t; + +/* Page middle directory made up of pmd_t entries */ +typedef struct pmd_table { + pmd_t entry[PMD_ENTRY_TOTAL]; +} pmd_table_t; + +/* Applies for both small and large pages */ +#define PAGE_AP0 4 +#define PAGE_AP1 6 +#define PAGE_AP2 8 +#define PAGE_AP3 10 + +/* Permission values with rom and sys bits ignored */ +#define SVC_RW_USR_NONE 1 +#define SVC_RW_USR_RO 2 +#define SVC_RW_USR_RW 3 + +#define PTE_PROT_MASK (0xFF << 4) + +#define CACHEABILITY 3 +#define BUFFERABILITY 2 +#define cacheable (1 << CACHEABILITY) +#define bufferable (1 << BUFFERABILITY) +#define uncacheable 0 +#define unbufferable 0 + +/* Helper macros for common cases */ +#define __MAP_USR_RW_FLAGS (cacheable | bufferable | (SVC_RW_USR_RW << PAGE_AP0) \ + | (SVC_RW_USR_RW << PAGE_AP1) | (SVC_RW_USR_RW << PAGE_AP2) \ + | (SVC_RW_USR_RW << PAGE_AP3)) +#define __MAP_USR_RO_FLAGS (cacheable | bufferable | (SVC_RW_USR_RO << PAGE_AP0) \ + | (SVC_RW_USR_RO << PAGE_AP1) | (SVC_RW_USR_RO << PAGE_AP2) \ + | (SVC_RW_USR_RO << PAGE_AP3)) +#define __MAP_SVC_RW_FLAGS (cacheable | bufferable | (SVC_RW_USR_NONE << PAGE_AP0) \ + | (SVC_RW_USR_NONE << PAGE_AP1) | (SVC_RW_USR_NONE << PAGE_AP2) \ + | (SVC_RW_USR_NONE << PAGE_AP3)) +#define __MAP_SVC_IO_FLAGS (uncacheable | unbufferable | (SVC_RW_USR_NONE << PAGE_AP0) \ + | (SVC_RW_USR_NONE << PAGE_AP1) | (SVC_RW_USR_NONE << PAGE_AP2) \ + | (SVC_RW_USR_NONE << PAGE_AP3)) +#define __MAP_USR_IO_FLAGS (uncacheable | unbufferable | (SVC_RW_USR_RW << PAGE_AP0) \ + | (SVC_RW_USR_RW << PAGE_AP1) | (SVC_RW_USR_RW << PAGE_AP2) \ + | (SVC_RW_USR_RW << PAGE_AP3)) + +/* Abort information */ + +/*FIXME: Carry all these definitions to an abort.h, Also carry all abort code to abort.c. Much neater!!! */ + +/* Abort type */ +#define ARM_PABT 1 +#define ARM_DABT 0 +/* The kernel makes use of bit 8 (Always Zero) of FSR to define which type of abort */ +#define set_abort_type(fsr, x) { fsr &= ~(1 << 8); fsr |= ((x & 1) << 8); } +#define ARM_FSR_MASK 0xF +#define is_prefetch_abort(fsr) ((fsr >> 8) & 0x1) +#define is_data_abort(fsr) (!is_prefetch_abort(fsr)) + +/* + * v5 Architecture-defined data abort values for FSR ordered + * in highest to lowest priority. + */ +#define DABT_TERMINAL 0x2 +#define DABT_VECTOR 0x0 /* Obsolete */ +#define DABT_ALIGN 0x1 +#define DABT_EXT_XLATE_LEVEL1 0xC +#define DABT_EXT_XLATE_LEVEL2 0xE +#define DABT_XLATE_SECT 0x5 +#define DABT_XLATE_PAGE 0x7 +#define DABT_DOMAIN_SECT 0x9 +#define DABT_DOMAIN_PAGE 0xB +#define DABT_PERM_SECT 0xD +#define DABT_PERM_PAGE 0xF +#define DABT_EXT_LFETCH_SECT 0x4 +#define DABT_EXT_LFETCH_PAGE 0x6 +#define DABT_EXT_NON_LFETCH_SECT 0x8 +#define DABT_EXT_NON_LFETCH_PAGE 0xA + +#define TASK_PGD(x) (x)->space->pgd + +#define STACK_ALIGNMENT 8 + +/* Kernel's data about the fault */ +typedef struct fault_kdata { + u32 faulty_pc; + u32 fsr; + u32 far; + pte_t pte; +} __attribute__ ((__packed__)) fault_kdata_t; + +void arch_hardware_flush(pgd_table_t *pgd); +void add_section_mapping_init(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags); + +void add_boot_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags); + +struct address_space; +int delete_page_tables(struct address_space *space); +int copy_user_tables(struct address_space *new, struct address_space *orig); +pgd_table_t *copy_page_tables(pgd_table_t *from); +void remap_as_pages(void *vstart, void *vend); + +int pgd_count_pmds(pgd_table_t *pgd); +pgd_table_t *realloc_page_tables(void); +void remove_section_mapping(unsigned long vaddr); + +void copy_pgds_by_vrange(pgd_table_t *to, pgd_table_t *from, + unsigned long start, unsigned long end); + +#endif /* __V5_MM_H__ */ diff --git a/include/l4/arch/arm/v6/mmu_ops.h b/include/l4/arch/arm/v6/mmu_ops.h new file mode 100644 index 0000000..f7f0b88 --- /dev/null +++ b/include/l4/arch/arm/v6/mmu_ops.h @@ -0,0 +1,34 @@ +#ifndef __MMU__OPS__H__ +#define __MMU__OPS__H__ +/* + * Prototypes for low level mmu operations + * + * Copyright (C) 2005 Bahadir Balban + * + */ + +void arm_set_ttb(unsigned int); +void arm_set_domain(unsigned int); +unsigned int arm_get_domain(void); +void arm_enable_mmu(void); +void arm_enable_icache(void); +void arm_enable_dcache(void); +void arm_enable_wbuffer(void); +void arm_enable_high_vectors(void); +void arm_invalidate_cache(void); +void arm_invalidate_icache(void); +void arm_invalidate_dcache(void); +void arm_clean_invalidate_dcache(void); +void arm_clean_invalidate_cache(void); +void arm_drain_writebuffer(void); +void arm_invalidate_tlb(void); +void arm_invalidate_itlb(void); +void arm_invalidate_dtlb(void); + +static inline void arm_enable_caches(void) +{ + arm_enable_icache(); + arm_enable_dcache(); +} + +#endif /* __MMU__OPS__H__ */ diff --git a/include/l4/arch/arm/v6/mutex.h b/include/l4/arch/arm/v6/mutex.h new file mode 100644 index 0000000..e69de29 diff --git a/include/l4/arch/arm/v7/cortexa8/cpu.h b/include/l4/arch/arm/v7/cortexa8/cpu.h new file mode 100644 index 0000000..2d11b60 --- /dev/null +++ b/include/l4/arch/arm/v7/cortexa8/cpu.h @@ -0,0 +1,13 @@ +/* + * + * Copyright (C) 2005 Bahadir Balban + * + */ +#ifndef __ARM926EJS__H__ +#define __ARM926EJS__H__ + + + + + +#endif /* __ARM926EJS__H__ */ diff --git a/include/l4/arch/arm/v7/mm.h b/include/l4/arch/arm/v7/mm.h new file mode 100644 index 0000000..35b763b --- /dev/null +++ b/include/l4/arch/arm/v7/mm.h @@ -0,0 +1,160 @@ +/* + * ARM v5-specific virtual memory details + * + * Copyright (C) 2007 Bahadir Balban + */ +#ifndef __V5_MM_H__ +#define __V5_MM_H__ + +/* ARM specific definitions */ +#define VIRT_MEM_START 0 +#define VIRT_MEM_END 0xFFFFFFFF +#define ARM_SECTION_SIZE SZ_1MB +#define ARM_SECTION_MASK (ARM_SECTION_SIZE - 1) +#define ARM_SECTION_BITS 20 +#define ARM_PAGE_SIZE SZ_4K +#define ARM_PAGE_MASK 0xFFF +#define ARM_PAGE_BITS 12 + +#define PGD_SIZE SZ_4K * 4 +#define PGD_ENTRY_TOTAL SZ_4K +#define PGD_TYPE_MASK 0x3 +#define PGD_COARSE_ALIGN_MASK 0xFFFFFC00 +#define PGD_SECTION_ALIGN_MASK 0xFFF00000 +#define PGD_FINE_ALIGN_MASK 0xFFFFF000 +#define PGD_TYPE_FAULT 0 +#define PGD_TYPE_COARSE 1 +#define PGD_TYPE_SECTION 2 +#define PGD_TYPE_FINE 3 + +#define PMD_TYPE_MASK 0x3 +#define PMD_TYPE_FAULT 0 +#define PMD_TYPE_LARGE 1 +#define PMD_TYPE_SMALL 2 +#define PMD_TYPE_TINY 3 + +/* Permission field offsets */ +#define SECTION_AP0 10 + +#define PMD_SIZE SZ_1K +#define PMD_ENTRY_TOTAL 256 +#define PMD_MAP_SIZE SZ_1MB + +/* Type-checkable page table elements */ +typedef u32 pgd_t; +typedef u32 pmd_t; +typedef u32 pte_t; + +/* Page global directory made up of pgd_t entries */ +typedef struct pgd_table { + pgd_t entry[PGD_ENTRY_TOTAL]; +} pgd_table_t; + +/* Page middle directory made up of pmd_t entries */ +typedef struct pmd_table { + pmd_t entry[PMD_ENTRY_TOTAL]; +} pmd_table_t; + +/* Applies for both small and large pages */ +#define PAGE_AP0 4 +#define PAGE_AP1 6 +#define PAGE_AP2 8 +#define PAGE_AP3 10 + +/* Permission values with rom and sys bits ignored */ +#define SVC_RW_USR_NONE 1 +#define SVC_RW_USR_RO 2 +#define SVC_RW_USR_RW 3 + +#define PTE_PROT_MASK (0xFF << 4) + +#define CACHEABILITY 3 +#define BUFFERABILITY 2 +#define cacheable (1 << CACHEABILITY) +#define bufferable (1 << BUFFERABILITY) +#define uncacheable 0 +#define unbufferable 0 + +/* Helper macros for common cases */ +#define __MAP_USR_RW_FLAGS (cacheable | bufferable | (SVC_RW_USR_RW << PAGE_AP0) \ + | (SVC_RW_USR_RW << PAGE_AP1) | (SVC_RW_USR_RW << PAGE_AP2) \ + | (SVC_RW_USR_RW << PAGE_AP3)) +#define __MAP_USR_RO_FLAGS (cacheable | bufferable | (SVC_RW_USR_RO << PAGE_AP0) \ + | (SVC_RW_USR_RO << PAGE_AP1) | (SVC_RW_USR_RO << PAGE_AP2) \ + | (SVC_RW_USR_RO << PAGE_AP3)) +#define __MAP_SVC_RW_FLAGS (cacheable | bufferable | (SVC_RW_USR_NONE << PAGE_AP0) \ + | (SVC_RW_USR_NONE << PAGE_AP1) | (SVC_RW_USR_NONE << PAGE_AP2) \ + | (SVC_RW_USR_NONE << PAGE_AP3)) +#define __MAP_SVC_IO_FLAGS (uncacheable | unbufferable | (SVC_RW_USR_NONE << PAGE_AP0) \ + | (SVC_RW_USR_NONE << PAGE_AP1) | (SVC_RW_USR_NONE << PAGE_AP2) \ + | (SVC_RW_USR_NONE << PAGE_AP3)) +#define __MAP_USR_IO_FLAGS (uncacheable | unbufferable | (SVC_RW_USR_RW << PAGE_AP0) \ + | (SVC_RW_USR_RW << PAGE_AP1) | (SVC_RW_USR_RW << PAGE_AP2) \ + | (SVC_RW_USR_RW << PAGE_AP3)) + +/* Abort information */ + +/*FIXME: Carry all these definitions to an abort.h, Also carry all abort code to abort.c. Much neater!!! */ + +/* Abort type */ +#define ARM_PABT 1 +#define ARM_DABT 0 +/* The kernel makes use of bit 8 (Always Zero) of FSR to define which type of abort */ +#define set_abort_type(fsr, x) { fsr &= ~(1 << 8); fsr |= ((x & 1) << 8); } +#define ARM_FSR_MASK 0xF +#define is_prefetch_abort(fsr) ((fsr >> 8) & 0x1) +#define is_data_abort(fsr) (!is_prefetch_abort(fsr)) + +/* + * v5 Architecture-defined data abort values for FSR ordered + * in highest to lowest priority. + */ +#define DABT_TERMINAL 0x2 +#define DABT_VECTOR 0x0 /* Obsolete */ +#define DABT_ALIGN 0x1 +#define DABT_EXT_XLATE_LEVEL1 0xC +#define DABT_EXT_XLATE_LEVEL2 0xE +#define DABT_XLATE_SECT 0x5 +#define DABT_XLATE_PAGE 0x7 +#define DABT_DOMAIN_SECT 0x9 +#define DABT_DOMAIN_PAGE 0xB +#define DABT_PERM_SECT 0xD +#define DABT_PERM_PAGE 0xF +#define DABT_EXT_LFETCH_SECT 0x4 +#define DABT_EXT_LFETCH_PAGE 0x6 +#define DABT_EXT_NON_LFETCH_SECT 0x8 +#define DABT_EXT_NON_LFETCH_PAGE 0xA + +#define TASK_PGD(x) (x)->space->pgd + +#define STACK_ALIGNMENT 8 + +/* Kernel's data about the fault */ +typedef struct fault_kdata { + u32 faulty_pc; + u32 fsr; + u32 far; + pte_t pte; +} __attribute__ ((__packed__)) fault_kdata_t; + +void arch_hardware_flush(pgd_table_t *pgd); +void add_section_mapping_init(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags); + +void add_boot_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags); + +struct address_space; +int delete_page_tables(struct address_space *space); +int copy_user_tables(struct address_space *new, struct address_space *orig); +pgd_table_t *copy_page_tables(pgd_table_t *from); +void remap_as_pages(void *vstart, void *vend); + +int pgd_count_pmds(pgd_table_t *pgd); +pgd_table_t *realloc_page_tables(void); +void remove_section_mapping(unsigned long vaddr); + +void copy_pgds_by_vrange(pgd_table_t *to, pgd_table_t *from, + unsigned long start, unsigned long end); + +#endif /* __V5_MM_H__ */ diff --git a/include/l4/arch/arm/v7/mmu_ops.h b/include/l4/arch/arm/v7/mmu_ops.h new file mode 100644 index 0000000..f7f0b88 --- /dev/null +++ b/include/l4/arch/arm/v7/mmu_ops.h @@ -0,0 +1,34 @@ +#ifndef __MMU__OPS__H__ +#define __MMU__OPS__H__ +/* + * Prototypes for low level mmu operations + * + * Copyright (C) 2005 Bahadir Balban + * + */ + +void arm_set_ttb(unsigned int); +void arm_set_domain(unsigned int); +unsigned int arm_get_domain(void); +void arm_enable_mmu(void); +void arm_enable_icache(void); +void arm_enable_dcache(void); +void arm_enable_wbuffer(void); +void arm_enable_high_vectors(void); +void arm_invalidate_cache(void); +void arm_invalidate_icache(void); +void arm_invalidate_dcache(void); +void arm_clean_invalidate_dcache(void); +void arm_clean_invalidate_cache(void); +void arm_drain_writebuffer(void); +void arm_invalidate_tlb(void); +void arm_invalidate_itlb(void); +void arm_invalidate_dtlb(void); + +static inline void arm_enable_caches(void) +{ + arm_enable_icache(); + arm_enable_dcache(); +} + +#endif /* __MMU__OPS__H__ */ diff --git a/include/l4/arch/arm/v7/mutex.h b/include/l4/arch/arm/v7/mutex.h new file mode 100644 index 0000000..e69de29 diff --git a/include/l4/drivers/irq/gic.h b/include/l4/drivers/irq/gic.h deleted file mode 100644 index a69af61..0000000 --- a/include/l4/drivers/irq/gic.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * - * Copyright (C) 2007 Bahadir Balban - * - */ -#ifndef __GIC_H__ -#define __GIC_H__ - - - - - -#endif /* __GIC_H__ */ diff --git a/include/l4/drivers/irq/gic/gic.h b/include/l4/drivers/irq/gic/gic.h new file mode 100644 index 0000000..65adb79 --- /dev/null +++ b/include/l4/drivers/irq/gic/gic.h @@ -0,0 +1,56 @@ +/* + * PL190 Primecell Vectored Interrupt Controller offsets + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#ifndef __PL190_VIC_H__ +#define __PL190_VIC_H__ + +#include INC_PLAT(platform.h) + +#define PL190_BASE PLATFORM_IRQCTRL_BASE +#define PL190_SIC_BASE PLATFORM_SIRQCTRL_BASE + +/* VIC register offsets */ +#define PL190_VIC_IRQSTATUS (PL190_BASE + 0x00) +#define PL190_VIC_FIQSTATUS (PL190_BASE + 0x04) +#define PL190_VIC_RAWINTR (PL190_BASE + 0x08) +#define PL190_VIC_INTSELECT (PL190_BASE + 0x0C) +#define PL190_VIC_INTENABLE (PL190_BASE + 0x10) +#define PL190_VIC_INTENCLEAR (PL190_BASE + 0x14) +#define PL190_VIC_SOFTINT (PL190_BASE + 0x18) +#define PL190_VIC_SOFTINTCLEAR (PL190_BASE + 0x1C) +#define PL190_VIC_PROTECTION (PL190_BASE + 0x20) +#define PL190_VIC_VECTADDR (PL190_BASE + 0x30) +#define PL190_VIC_DEFVECTADDR (PL190_BASE + 0x34) +#define PL190_VIC_VECTADDR0 (PL190_BASE + 0x100) +/* 15 PIC_VECTADDR registers up to 0x13C */ +#define PL190_VIC_VECTCNTL0 (PL190_BASE + 0x200) +/* 15 PIC_VECTCNTL registers up to 0x23C */ + +#define PL190_SIC_STATUS (PL190_SIC_BASE + 0x0) +#define PL190_SIC_RAWSTAT (PL190_SIC_BASE + 0x04) +#define PL190_SIC_ENABLE (PL190_SIC_BASE + 0x08) +#define PL190_SIC_ENSET (PL190_SIC_BASE + 0x08) +#define PL190_SIC_ENCLR (PL190_SIC_BASE + 0x0C) +#define PL190_SIC_SOFTINTSET (PL190_SIC_BASE + 0x10) +#define PL190_SIC_SOFTINTCLR (PL190_SIC_BASE + 0x14) +#define PL190_SIC_PICENABLE (PL190_SIC_BASE + 0x20) +#define PL190_SIC_PICENSET (PL190_SIC_BASE + 0x20) +#define PL190_SIC_PICENCLR (PL190_SIC_BASE + 0x24) + +void pl190_vic_init(void); +void pl190_ack_irq(int irq); +void pl190_mask_irq(int irq); +void pl190_unmask_irq(int irq); +int pl190_read_irq(void); + +int pl190_sic_read_irq(void); +void pl190_sic_mask_irq(int irq); +void pl190_sic_mask_irq(int irq); +void pl190_sic_ack_irq(int irq); +void pl190_sic_unmask_irq(int irq); +void pl190_sic_init(void); +#endif /* __PL190_VIC_H__ */ diff --git a/include/l4/platform/ab926/irq.h b/include/l4/platform/ab926/irq.h new file mode 100644 index 0000000..ecfad92 --- /dev/null +++ b/include/l4/platform/ab926/irq.h @@ -0,0 +1,27 @@ +#ifndef __PLATFORM_IRQ_H__ +#define __PLATFORM_IRQ_H__ + +#define IRQ_CHIPS_MAX 2 +#define IRQS_MAX 64 + +/* IRQ indices. */ +#define IRQ_TIMER01 4 +#define IRQ_TIMER23 5 +#define IRQ_RTC 10 +#define IRQ_UART0 12 +#define IRQ_UART1 13 +#define IRQ_UART2 14 +#define IRQ_SIC 31 + +/* Cascading definitions */ + +#define PIC_IRQS_MAX 31 /* Total irqs on PIC */ +/* The local irq line of the dummy peripheral on this chip */ +#define LOCALIRQ_DUMMY 15 +/* The irq index offset of this chip, is the maximum of previous chip + 1 */ +#define SIRQ_CHIP_OFFSET (PIC_IRQS_MAX + 1) +/* The global irq number of dummy is the local irq line + it's chip offset */ +#define IRQ_DUMMY (LOCALIRQ_DUMMY + SIRQ_CHIP_OFFSET) + + +#endif /* __PLATFORM_IRQ_H__ */ diff --git a/include/l4/platform/ab926/offsets.h b/include/l4/platform/ab926/offsets.h new file mode 100644 index 0000000..4a49729 --- /dev/null +++ b/include/l4/platform/ab926/offsets.h @@ -0,0 +1,60 @@ +/* + * Describes physical memory layout of pb926 platform. + * + * Copyright (C) 2007 Bahadir Balban + */ + +#ifndef __PLATFORM_PB926_OFFSETS_H__ +#define __PLATFORM_PB926_OFFSETS_H__ + +/* Physical memory base */ +#define PHYS_MEM_START 0x00000000 /* inclusive */ +#define PHYS_MEM_END 0x08000000 /* 128 MB, exclusive */ + +/* + * These bases taken from where kernel is `physically' linked at, + * also used to calculate virtual-to-physical translation offset. + * See the linker script for their sources. PHYS_ADDR_BASE can't + * use a linker variable because it's referred from assembler. + */ +#define PHYS_ADDR_BASE 0x100000 + +/* Device memory base */ +#define PB926_DEV_PHYS 0x10000000 + +/* Device offsets in physical memory */ +#define PB926_SYSTEM_REGISTERS 0x10000000 /* System registers */ +#define PB926_SYSCTRL_BASE 0x101E0000 /* System controller */ +#define PB926_WATCHDOG_BASE 0x101E1000 /* Watchdog */ +#define PB926_TIMER01_BASE 0x101E2000 /* Timers 0 and 1 */ +#define PB926_TIMER23_BASE 0x101E3000 /* Timers 2 and 3 */ +#define PB926_RTC_BASE 0x101E8000 /* Real Time Clock */ +#define PB926_VIC_BASE 0x10140000 /* Primary Vectored IC */ +#define PB926_SIC_BASE 0x10003000 /* Secondary IC */ +#define PB926_UART0_BASE 0x101F1000 /* Console port (UART0) */ + +/* + * Uart virtual address until a file-based console access + * is available for userspace + */ +#define USERSPACE_UART_BASE 0x500000 + +/* + * Device offsets in virtual memory. They offset to some virtual + * device base address. Each page on this virtual base is consecutively + * allocated to devices. Nice and smooth. + */ +#define PB926_TIMER01_VOFFSET 0x00000000 +#define PB926_UART0_VOFFSET 0x00001000 +#define PB926_VIC_VOFFSET 0x00002000 +#define PB926_SIC_VOFFSET 0x00003000 +#define PB926_SYSREGS_VOFFSET 0x00005000 +#define PB926_SYSCTRL_VOFFSET 0x00006000 + +#define PB926_UART0_VBASE (IO_AREA0_VADDR + PB926_UART0_VOFFSET) +#define PB926_TIMER01_VBASE (IO_AREA0_VADDR + PB926_TIMER01_VOFFSET) +#define PB926_SYSCTRL_VBASE (IO_AREA0_VADDR + PB926_SYSCTRL_VOFFSET) +#define PB926_VIC_VBASE (IO_AREA0_VADDR + PB926_VIC_VOFFSET) +#define PB926_SIC_VBASE (IO_AREA0_VADDR + PB926_SIC_VOFFSET) +#endif /* __PLATFORM_PB926_OFFSETS_H__ */ + diff --git a/include/l4/platform/ab926/platform.h b/include/l4/platform/ab926/platform.h new file mode 100644 index 0000000..dd4f4cb --- /dev/null +++ b/include/l4/platform/ab926/platform.h @@ -0,0 +1,22 @@ +#ifndef __PLATFORM_PB926_PLATFORM_H__ +#define __PLATFORM_PB926_PLATFORM_H__ +/* + * Platform specific ties between drivers and generic APIs used by the kernel. + * E.g. system timer and console. + * + * Copyright (C) Bahadir Balban 2007 + */ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#define PLATFORM_TIMER_BASE PB926_TIMER01_VBASE +#define PLATFORM_SP810_BASE PB926_SYSCTRL_VBASE +#define PLATFORM_IRQCTRL_BASE PB926_VIC_VBASE +#define PLATFORM_SIRQCTRL_BASE PB926_SIC_VBASE + +void platform_irq_enable(int irq); +void platform_irq_disable(int irq); +void timer_start(void); +#endif /* __PLATFORM_PB926_PLATFORM_H__ */ diff --git a/include/l4/platform/ab926/printascii.h b/include/l4/platform/ab926/printascii.h new file mode 100644 index 0000000..0d0be28 --- /dev/null +++ b/include/l4/platform/ab926/printascii.h @@ -0,0 +1,15 @@ +#ifndef __PLATFORM__PB926__PRINTASCII__H__ +#define __PLATFORM__PB926__PRINTASCII__H__ + +#define dprintk(str, val) \ +{ \ + printascii(str); \ + printascii("0x"); \ + printhex8((val)); \ + printascii("\n"); \ +} + +void printascii(char *str); +void printhex8(unsigned int); + +#endif /* __PLATFORM__PB926__PRINTASCII__H__ */ diff --git a/include/l4/platform/ab926/uart.h b/include/l4/platform/ab926/uart.h new file mode 100644 index 0000000..366590f --- /dev/null +++ b/include/l4/platform/ab926/uart.h @@ -0,0 +1,20 @@ +/* + * Platform specific ties to generic uart functions that putc expects. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#ifndef __PLATFORM_PB926_UART_H__ +#define __PLATFORM_PB926_UART_H__ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#include + +void uart_init(void); +void uart_putc(char c); + +#endif /* __PLATFORM_PB926_UART_H__ */ diff --git a/include/l4/platform/eb/irq.h b/include/l4/platform/eb/irq.h new file mode 100644 index 0000000..ecfad92 --- /dev/null +++ b/include/l4/platform/eb/irq.h @@ -0,0 +1,27 @@ +#ifndef __PLATFORM_IRQ_H__ +#define __PLATFORM_IRQ_H__ + +#define IRQ_CHIPS_MAX 2 +#define IRQS_MAX 64 + +/* IRQ indices. */ +#define IRQ_TIMER01 4 +#define IRQ_TIMER23 5 +#define IRQ_RTC 10 +#define IRQ_UART0 12 +#define IRQ_UART1 13 +#define IRQ_UART2 14 +#define IRQ_SIC 31 + +/* Cascading definitions */ + +#define PIC_IRQS_MAX 31 /* Total irqs on PIC */ +/* The local irq line of the dummy peripheral on this chip */ +#define LOCALIRQ_DUMMY 15 +/* The irq index offset of this chip, is the maximum of previous chip + 1 */ +#define SIRQ_CHIP_OFFSET (PIC_IRQS_MAX + 1) +/* The global irq number of dummy is the local irq line + it's chip offset */ +#define IRQ_DUMMY (LOCALIRQ_DUMMY + SIRQ_CHIP_OFFSET) + + +#endif /* __PLATFORM_IRQ_H__ */ diff --git a/include/l4/platform/eb/offsets.h b/include/l4/platform/eb/offsets.h new file mode 100644 index 0000000..4a49729 --- /dev/null +++ b/include/l4/platform/eb/offsets.h @@ -0,0 +1,60 @@ +/* + * Describes physical memory layout of pb926 platform. + * + * Copyright (C) 2007 Bahadir Balban + */ + +#ifndef __PLATFORM_PB926_OFFSETS_H__ +#define __PLATFORM_PB926_OFFSETS_H__ + +/* Physical memory base */ +#define PHYS_MEM_START 0x00000000 /* inclusive */ +#define PHYS_MEM_END 0x08000000 /* 128 MB, exclusive */ + +/* + * These bases taken from where kernel is `physically' linked at, + * also used to calculate virtual-to-physical translation offset. + * See the linker script for their sources. PHYS_ADDR_BASE can't + * use a linker variable because it's referred from assembler. + */ +#define PHYS_ADDR_BASE 0x100000 + +/* Device memory base */ +#define PB926_DEV_PHYS 0x10000000 + +/* Device offsets in physical memory */ +#define PB926_SYSTEM_REGISTERS 0x10000000 /* System registers */ +#define PB926_SYSCTRL_BASE 0x101E0000 /* System controller */ +#define PB926_WATCHDOG_BASE 0x101E1000 /* Watchdog */ +#define PB926_TIMER01_BASE 0x101E2000 /* Timers 0 and 1 */ +#define PB926_TIMER23_BASE 0x101E3000 /* Timers 2 and 3 */ +#define PB926_RTC_BASE 0x101E8000 /* Real Time Clock */ +#define PB926_VIC_BASE 0x10140000 /* Primary Vectored IC */ +#define PB926_SIC_BASE 0x10003000 /* Secondary IC */ +#define PB926_UART0_BASE 0x101F1000 /* Console port (UART0) */ + +/* + * Uart virtual address until a file-based console access + * is available for userspace + */ +#define USERSPACE_UART_BASE 0x500000 + +/* + * Device offsets in virtual memory. They offset to some virtual + * device base address. Each page on this virtual base is consecutively + * allocated to devices. Nice and smooth. + */ +#define PB926_TIMER01_VOFFSET 0x00000000 +#define PB926_UART0_VOFFSET 0x00001000 +#define PB926_VIC_VOFFSET 0x00002000 +#define PB926_SIC_VOFFSET 0x00003000 +#define PB926_SYSREGS_VOFFSET 0x00005000 +#define PB926_SYSCTRL_VOFFSET 0x00006000 + +#define PB926_UART0_VBASE (IO_AREA0_VADDR + PB926_UART0_VOFFSET) +#define PB926_TIMER01_VBASE (IO_AREA0_VADDR + PB926_TIMER01_VOFFSET) +#define PB926_SYSCTRL_VBASE (IO_AREA0_VADDR + PB926_SYSCTRL_VOFFSET) +#define PB926_VIC_VBASE (IO_AREA0_VADDR + PB926_VIC_VOFFSET) +#define PB926_SIC_VBASE (IO_AREA0_VADDR + PB926_SIC_VOFFSET) +#endif /* __PLATFORM_PB926_OFFSETS_H__ */ + diff --git a/include/l4/platform/eb/platform.h b/include/l4/platform/eb/platform.h new file mode 100644 index 0000000..dd4f4cb --- /dev/null +++ b/include/l4/platform/eb/platform.h @@ -0,0 +1,22 @@ +#ifndef __PLATFORM_PB926_PLATFORM_H__ +#define __PLATFORM_PB926_PLATFORM_H__ +/* + * Platform specific ties between drivers and generic APIs used by the kernel. + * E.g. system timer and console. + * + * Copyright (C) Bahadir Balban 2007 + */ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#define PLATFORM_TIMER_BASE PB926_TIMER01_VBASE +#define PLATFORM_SP810_BASE PB926_SYSCTRL_VBASE +#define PLATFORM_IRQCTRL_BASE PB926_VIC_VBASE +#define PLATFORM_SIRQCTRL_BASE PB926_SIC_VBASE + +void platform_irq_enable(int irq); +void platform_irq_disable(int irq); +void timer_start(void); +#endif /* __PLATFORM_PB926_PLATFORM_H__ */ diff --git a/include/l4/platform/eb/printascii.h b/include/l4/platform/eb/printascii.h new file mode 100644 index 0000000..0d0be28 --- /dev/null +++ b/include/l4/platform/eb/printascii.h @@ -0,0 +1,15 @@ +#ifndef __PLATFORM__PB926__PRINTASCII__H__ +#define __PLATFORM__PB926__PRINTASCII__H__ + +#define dprintk(str, val) \ +{ \ + printascii(str); \ + printascii("0x"); \ + printhex8((val)); \ + printascii("\n"); \ +} + +void printascii(char *str); +void printhex8(unsigned int); + +#endif /* __PLATFORM__PB926__PRINTASCII__H__ */ diff --git a/include/l4/platform/eb/uart.h b/include/l4/platform/eb/uart.h new file mode 100644 index 0000000..366590f --- /dev/null +++ b/include/l4/platform/eb/uart.h @@ -0,0 +1,20 @@ +/* + * Platform specific ties to generic uart functions that putc expects. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#ifndef __PLATFORM_PB926_UART_H__ +#define __PLATFORM_PB926_UART_H__ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#include + +void uart_init(void); +void uart_putc(char c); + +#endif /* __PLATFORM_PB926_UART_H__ */ diff --git a/include/l4/platform/pb11mpcore/irq.h b/include/l4/platform/pb11mpcore/irq.h new file mode 100644 index 0000000..ecfad92 --- /dev/null +++ b/include/l4/platform/pb11mpcore/irq.h @@ -0,0 +1,27 @@ +#ifndef __PLATFORM_IRQ_H__ +#define __PLATFORM_IRQ_H__ + +#define IRQ_CHIPS_MAX 2 +#define IRQS_MAX 64 + +/* IRQ indices. */ +#define IRQ_TIMER01 4 +#define IRQ_TIMER23 5 +#define IRQ_RTC 10 +#define IRQ_UART0 12 +#define IRQ_UART1 13 +#define IRQ_UART2 14 +#define IRQ_SIC 31 + +/* Cascading definitions */ + +#define PIC_IRQS_MAX 31 /* Total irqs on PIC */ +/* The local irq line of the dummy peripheral on this chip */ +#define LOCALIRQ_DUMMY 15 +/* The irq index offset of this chip, is the maximum of previous chip + 1 */ +#define SIRQ_CHIP_OFFSET (PIC_IRQS_MAX + 1) +/* The global irq number of dummy is the local irq line + it's chip offset */ +#define IRQ_DUMMY (LOCALIRQ_DUMMY + SIRQ_CHIP_OFFSET) + + +#endif /* __PLATFORM_IRQ_H__ */ diff --git a/include/l4/platform/pb11mpcore/offsets.h b/include/l4/platform/pb11mpcore/offsets.h new file mode 100644 index 0000000..4a49729 --- /dev/null +++ b/include/l4/platform/pb11mpcore/offsets.h @@ -0,0 +1,60 @@ +/* + * Describes physical memory layout of pb926 platform. + * + * Copyright (C) 2007 Bahadir Balban + */ + +#ifndef __PLATFORM_PB926_OFFSETS_H__ +#define __PLATFORM_PB926_OFFSETS_H__ + +/* Physical memory base */ +#define PHYS_MEM_START 0x00000000 /* inclusive */ +#define PHYS_MEM_END 0x08000000 /* 128 MB, exclusive */ + +/* + * These bases taken from where kernel is `physically' linked at, + * also used to calculate virtual-to-physical translation offset. + * See the linker script for their sources. PHYS_ADDR_BASE can't + * use a linker variable because it's referred from assembler. + */ +#define PHYS_ADDR_BASE 0x100000 + +/* Device memory base */ +#define PB926_DEV_PHYS 0x10000000 + +/* Device offsets in physical memory */ +#define PB926_SYSTEM_REGISTERS 0x10000000 /* System registers */ +#define PB926_SYSCTRL_BASE 0x101E0000 /* System controller */ +#define PB926_WATCHDOG_BASE 0x101E1000 /* Watchdog */ +#define PB926_TIMER01_BASE 0x101E2000 /* Timers 0 and 1 */ +#define PB926_TIMER23_BASE 0x101E3000 /* Timers 2 and 3 */ +#define PB926_RTC_BASE 0x101E8000 /* Real Time Clock */ +#define PB926_VIC_BASE 0x10140000 /* Primary Vectored IC */ +#define PB926_SIC_BASE 0x10003000 /* Secondary IC */ +#define PB926_UART0_BASE 0x101F1000 /* Console port (UART0) */ + +/* + * Uart virtual address until a file-based console access + * is available for userspace + */ +#define USERSPACE_UART_BASE 0x500000 + +/* + * Device offsets in virtual memory. They offset to some virtual + * device base address. Each page on this virtual base is consecutively + * allocated to devices. Nice and smooth. + */ +#define PB926_TIMER01_VOFFSET 0x00000000 +#define PB926_UART0_VOFFSET 0x00001000 +#define PB926_VIC_VOFFSET 0x00002000 +#define PB926_SIC_VOFFSET 0x00003000 +#define PB926_SYSREGS_VOFFSET 0x00005000 +#define PB926_SYSCTRL_VOFFSET 0x00006000 + +#define PB926_UART0_VBASE (IO_AREA0_VADDR + PB926_UART0_VOFFSET) +#define PB926_TIMER01_VBASE (IO_AREA0_VADDR + PB926_TIMER01_VOFFSET) +#define PB926_SYSCTRL_VBASE (IO_AREA0_VADDR + PB926_SYSCTRL_VOFFSET) +#define PB926_VIC_VBASE (IO_AREA0_VADDR + PB926_VIC_VOFFSET) +#define PB926_SIC_VBASE (IO_AREA0_VADDR + PB926_SIC_VOFFSET) +#endif /* __PLATFORM_PB926_OFFSETS_H__ */ + diff --git a/include/l4/platform/pb11mpcore/platform.h b/include/l4/platform/pb11mpcore/platform.h new file mode 100644 index 0000000..dd4f4cb --- /dev/null +++ b/include/l4/platform/pb11mpcore/platform.h @@ -0,0 +1,22 @@ +#ifndef __PLATFORM_PB926_PLATFORM_H__ +#define __PLATFORM_PB926_PLATFORM_H__ +/* + * Platform specific ties between drivers and generic APIs used by the kernel. + * E.g. system timer and console. + * + * Copyright (C) Bahadir Balban 2007 + */ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#define PLATFORM_TIMER_BASE PB926_TIMER01_VBASE +#define PLATFORM_SP810_BASE PB926_SYSCTRL_VBASE +#define PLATFORM_IRQCTRL_BASE PB926_VIC_VBASE +#define PLATFORM_SIRQCTRL_BASE PB926_SIC_VBASE + +void platform_irq_enable(int irq); +void platform_irq_disable(int irq); +void timer_start(void); +#endif /* __PLATFORM_PB926_PLATFORM_H__ */ diff --git a/include/l4/platform/pb11mpcore/printascii.h b/include/l4/platform/pb11mpcore/printascii.h new file mode 100644 index 0000000..0d0be28 --- /dev/null +++ b/include/l4/platform/pb11mpcore/printascii.h @@ -0,0 +1,15 @@ +#ifndef __PLATFORM__PB926__PRINTASCII__H__ +#define __PLATFORM__PB926__PRINTASCII__H__ + +#define dprintk(str, val) \ +{ \ + printascii(str); \ + printascii("0x"); \ + printhex8((val)); \ + printascii("\n"); \ +} + +void printascii(char *str); +void printhex8(unsigned int); + +#endif /* __PLATFORM__PB926__PRINTASCII__H__ */ diff --git a/include/l4/platform/pb11mpcore/uart.h b/include/l4/platform/pb11mpcore/uart.h new file mode 100644 index 0000000..366590f --- /dev/null +++ b/include/l4/platform/pb11mpcore/uart.h @@ -0,0 +1,20 @@ +/* + * Platform specific ties to generic uart functions that putc expects. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#ifndef __PLATFORM_PB926_UART_H__ +#define __PLATFORM_PB926_UART_H__ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#include + +void uart_init(void); +void uart_putc(char c); + +#endif /* __PLATFORM_PB926_UART_H__ */ diff --git a/include/l4/platform/pba8/irq.h b/include/l4/platform/pba8/irq.h new file mode 100644 index 0000000..ecfad92 --- /dev/null +++ b/include/l4/platform/pba8/irq.h @@ -0,0 +1,27 @@ +#ifndef __PLATFORM_IRQ_H__ +#define __PLATFORM_IRQ_H__ + +#define IRQ_CHIPS_MAX 2 +#define IRQS_MAX 64 + +/* IRQ indices. */ +#define IRQ_TIMER01 4 +#define IRQ_TIMER23 5 +#define IRQ_RTC 10 +#define IRQ_UART0 12 +#define IRQ_UART1 13 +#define IRQ_UART2 14 +#define IRQ_SIC 31 + +/* Cascading definitions */ + +#define PIC_IRQS_MAX 31 /* Total irqs on PIC */ +/* The local irq line of the dummy peripheral on this chip */ +#define LOCALIRQ_DUMMY 15 +/* The irq index offset of this chip, is the maximum of previous chip + 1 */ +#define SIRQ_CHIP_OFFSET (PIC_IRQS_MAX + 1) +/* The global irq number of dummy is the local irq line + it's chip offset */ +#define IRQ_DUMMY (LOCALIRQ_DUMMY + SIRQ_CHIP_OFFSET) + + +#endif /* __PLATFORM_IRQ_H__ */ diff --git a/include/l4/platform/pba8/offsets.h b/include/l4/platform/pba8/offsets.h new file mode 100644 index 0000000..4a49729 --- /dev/null +++ b/include/l4/platform/pba8/offsets.h @@ -0,0 +1,60 @@ +/* + * Describes physical memory layout of pb926 platform. + * + * Copyright (C) 2007 Bahadir Balban + */ + +#ifndef __PLATFORM_PB926_OFFSETS_H__ +#define __PLATFORM_PB926_OFFSETS_H__ + +/* Physical memory base */ +#define PHYS_MEM_START 0x00000000 /* inclusive */ +#define PHYS_MEM_END 0x08000000 /* 128 MB, exclusive */ + +/* + * These bases taken from where kernel is `physically' linked at, + * also used to calculate virtual-to-physical translation offset. + * See the linker script for their sources. PHYS_ADDR_BASE can't + * use a linker variable because it's referred from assembler. + */ +#define PHYS_ADDR_BASE 0x100000 + +/* Device memory base */ +#define PB926_DEV_PHYS 0x10000000 + +/* Device offsets in physical memory */ +#define PB926_SYSTEM_REGISTERS 0x10000000 /* System registers */ +#define PB926_SYSCTRL_BASE 0x101E0000 /* System controller */ +#define PB926_WATCHDOG_BASE 0x101E1000 /* Watchdog */ +#define PB926_TIMER01_BASE 0x101E2000 /* Timers 0 and 1 */ +#define PB926_TIMER23_BASE 0x101E3000 /* Timers 2 and 3 */ +#define PB926_RTC_BASE 0x101E8000 /* Real Time Clock */ +#define PB926_VIC_BASE 0x10140000 /* Primary Vectored IC */ +#define PB926_SIC_BASE 0x10003000 /* Secondary IC */ +#define PB926_UART0_BASE 0x101F1000 /* Console port (UART0) */ + +/* + * Uart virtual address until a file-based console access + * is available for userspace + */ +#define USERSPACE_UART_BASE 0x500000 + +/* + * Device offsets in virtual memory. They offset to some virtual + * device base address. Each page on this virtual base is consecutively + * allocated to devices. Nice and smooth. + */ +#define PB926_TIMER01_VOFFSET 0x00000000 +#define PB926_UART0_VOFFSET 0x00001000 +#define PB926_VIC_VOFFSET 0x00002000 +#define PB926_SIC_VOFFSET 0x00003000 +#define PB926_SYSREGS_VOFFSET 0x00005000 +#define PB926_SYSCTRL_VOFFSET 0x00006000 + +#define PB926_UART0_VBASE (IO_AREA0_VADDR + PB926_UART0_VOFFSET) +#define PB926_TIMER01_VBASE (IO_AREA0_VADDR + PB926_TIMER01_VOFFSET) +#define PB926_SYSCTRL_VBASE (IO_AREA0_VADDR + PB926_SYSCTRL_VOFFSET) +#define PB926_VIC_VBASE (IO_AREA0_VADDR + PB926_VIC_VOFFSET) +#define PB926_SIC_VBASE (IO_AREA0_VADDR + PB926_SIC_VOFFSET) +#endif /* __PLATFORM_PB926_OFFSETS_H__ */ + diff --git a/include/l4/platform/pba8/platform.h b/include/l4/platform/pba8/platform.h new file mode 100644 index 0000000..dd4f4cb --- /dev/null +++ b/include/l4/platform/pba8/platform.h @@ -0,0 +1,22 @@ +#ifndef __PLATFORM_PB926_PLATFORM_H__ +#define __PLATFORM_PB926_PLATFORM_H__ +/* + * Platform specific ties between drivers and generic APIs used by the kernel. + * E.g. system timer and console. + * + * Copyright (C) Bahadir Balban 2007 + */ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#define PLATFORM_TIMER_BASE PB926_TIMER01_VBASE +#define PLATFORM_SP810_BASE PB926_SYSCTRL_VBASE +#define PLATFORM_IRQCTRL_BASE PB926_VIC_VBASE +#define PLATFORM_SIRQCTRL_BASE PB926_SIC_VBASE + +void platform_irq_enable(int irq); +void platform_irq_disable(int irq); +void timer_start(void); +#endif /* __PLATFORM_PB926_PLATFORM_H__ */ diff --git a/include/l4/platform/pba8/printascii.h b/include/l4/platform/pba8/printascii.h new file mode 100644 index 0000000..0d0be28 --- /dev/null +++ b/include/l4/platform/pba8/printascii.h @@ -0,0 +1,15 @@ +#ifndef __PLATFORM__PB926__PRINTASCII__H__ +#define __PLATFORM__PB926__PRINTASCII__H__ + +#define dprintk(str, val) \ +{ \ + printascii(str); \ + printascii("0x"); \ + printhex8((val)); \ + printascii("\n"); \ +} + +void printascii(char *str); +void printhex8(unsigned int); + +#endif /* __PLATFORM__PB926__PRINTASCII__H__ */ diff --git a/include/l4/platform/pba8/uart.h b/include/l4/platform/pba8/uart.h new file mode 100644 index 0000000..366590f --- /dev/null +++ b/include/l4/platform/pba8/uart.h @@ -0,0 +1,20 @@ +/* + * Platform specific ties to generic uart functions that putc expects. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#ifndef __PLATFORM_PB926_UART_H__ +#define __PLATFORM_PB926_UART_H__ + +#include INC_PLAT(offsets.h) +#include INC_GLUE(memlayout.h) + +#define PLATFORM_CONSOLE_BASE PB926_UART0_VBASE +#include + +void uart_init(void); +void uart_putc(char c); + +#endif /* __PLATFORM_PB926_UART_H__ */ diff --git a/src/arch/arm/v6/mm.c b/src/arch/arm/v6/mm.c index 190504b..1b7e339 100644 --- a/src/arch/arm/v6/mm.c +++ b/src/arch/arm/v6/mm.c @@ -1,7 +1,726 @@ /* - * - * Copyright Bahadir Balban (C) 2005 - * + * Copyright (C) 2007 Bahadir Balban */ +#include +#include +#include +#include +#include +#include +#include +#include +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_PLAT(printascii.h) +#include INC_GLUE(memlayout.h) +#include INC_ARCH(linker.h) +#include INC_ARCH(asm.h) +#include INC_API(kip.h) + +/* + * These are indices into arrays with pgd_t or pmd_t sized elements, + * therefore the index must be divided by appropriate element size + */ +#define PGD_INDEX(x) (((((unsigned long)(x)) >> 18) & 0x3FFC) / sizeof(pgd_t)) +/* Strip out the page offset in this megabyte from a total of 256 pages. */ +#define PMD_INDEX(x) (((((unsigned long)(x)) >> 10) & 0x3FC) / sizeof (pmd_t)) + +/* + * Removes initial mappings needed for transition to virtual memory. + * Used one-time only. + */ +void remove_section_mapping(unsigned long vaddr) +{ + pgd_table_t *pgd = &init_pgd;; + pgd_t pgd_i = PGD_INDEX(vaddr); + if (!((pgd->entry[pgd_i] & PGD_TYPE_MASK) + & PGD_TYPE_SECTION)) + while(1); + pgd->entry[pgd_i] = 0; + pgd->entry[pgd_i] |= PGD_TYPE_FAULT; + arm_invalidate_tlb(); +} + +/* + * Maps given section-aligned @paddr to @vaddr using enough number + * of section-units to fulfill @size in sections. Note this overwrites + * a mapping if same virtual address was already mapped. + */ +void __add_section_mapping_init(unsigned int paddr, + unsigned int vaddr, + unsigned int size, + unsigned int flags) +{ + pte_t *ppte; + unsigned int l1_ptab; + unsigned int l1_offset; + + /* 1st level page table address */ + l1_ptab = virt_to_phys(&init_pgd); + + /* Get the section offset for this vaddr */ + l1_offset = (vaddr >> 18) & 0x3FFC; + + /* The beginning entry for mapping */ + ppte = (unsigned int *)(l1_ptab + l1_offset); + for(int i = 0; i < size; i++) { + *ppte = 0; /* Clear out old value */ + *ppte |= paddr; /* Assign physical address */ + *ppte |= PGD_TYPE_SECTION; /* Assign translation type */ + /* Domain is 0, therefore no writes. */ + /* Only kernel access allowed */ + *ppte |= (SVC_RW_USR_NONE << SECTION_AP0); + /* Cacheability/Bufferability flags */ + *ppte |= flags; + ppte++; /* Next section entry */ + paddr += ARM_SECTION_SIZE; /* Next physical section */ + } + return; +} + +void add_section_mapping_init(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + unsigned int psection; + unsigned int vsection; + + /* Align each address to the pages they reside in */ + psection = paddr & ~ARM_SECTION_MASK; + vsection = vaddr & ~ARM_SECTION_MASK; + + if(size == 0) + return; + + __add_section_mapping_init(psection, vsection, size, flags); + + return; +} + +/* TODO: Make sure to flush tlb entry and caches */ +void __add_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int flags, pmd_table_t *pmd) +{ + unsigned int pmd_i = PMD_INDEX(vaddr); + pmd->entry[pmd_i] = paddr; + pmd->entry[pmd_i] |= PMD_TYPE_SMALL; /* Small page type */ + pmd->entry[pmd_i] |= flags; + + /* TODO: Is both required? Investigate */ + + /* TEST: + * I think cleaning or invalidating the cache is not required, + * because the entries in the cache aren't for the new mapping anyway. + * It's required if a mapping is removed, but not when newly added. + */ + arm_clean_invalidate_cache(); + + /* TEST: tlb must be flushed because a new mapping is present in page + * tables, and tlb is inconsistent with the page tables */ + arm_invalidate_tlb(); +} + +/* Return whether a pmd associated with @vaddr is mapped on a pgd or not. */ +pmd_table_t *pmd_exists(pgd_table_t *pgd, unsigned long vaddr) +{ + unsigned int pgd_i = PGD_INDEX(vaddr); + + /* Return true if non-zero pgd entry */ + switch (pgd->entry[pgd_i] & PGD_TYPE_MASK) { + case PGD_TYPE_COARSE: + return (pmd_table_t *) + phys_to_virt((pgd->entry[pgd_i] & + PGD_COARSE_ALIGN_MASK)); + break; + + case PGD_TYPE_FAULT: + return 0; + break; + + case PGD_TYPE_SECTION: + dprintk("Warning, a section is already mapped " + "where a coarse page mapping is attempted:", + (u32)(pgd->entry[pgd_i] + & PGD_SECTION_ALIGN_MASK)); + BUG(); + break; + + case PGD_TYPE_FINE: + dprintk("Warning, a fine page table is already mapped " + "where a coarse page mapping is attempted:", + (u32)(pgd->entry[pgd_i] + & PGD_FINE_ALIGN_MASK)); + printk("Fine tables are unsupported. "); + printk("What is this doing here?"); + BUG(); + break; + + default: + dprintk("Unrecognised pmd type @ pgd index:", pgd_i); + BUG(); + break; + } + return 0; +} + +/* Convert a virtual address to a pte if it exists in the page tables. */ +pte_t virt_to_pte_from_pgd(unsigned long virtual, pgd_table_t *pgd) +{ + pmd_table_t *pmd = pmd_exists(pgd, virtual); + + if (pmd) + return (pte_t)pmd->entry[PMD_INDEX(virtual)]; + else + return (pte_t)0; +} + +/* Convert a virtual address to a pte if it exists in the page tables. */ +pte_t virt_to_pte(unsigned long virtual) +{ + return virt_to_pte_from_pgd(virtual, TASK_PGD(current)); +} + +unsigned long virt_to_phys_by_pgd(unsigned long vaddr, pgd_table_t *pgd) +{ + pte_t pte = virt_to_pte_from_pgd(vaddr, pgd); + return pte & ~PAGE_MASK; +} + +unsigned long virt_to_phys_by_task(unsigned long vaddr, struct ktcb *task) +{ + return virt_to_phys_by_pgd(vaddr, TASK_PGD(task)); +} + +void attach_pmd(pgd_table_t *pgd, pmd_table_t *pmd, unsigned int vaddr) +{ + u32 pgd_i = PGD_INDEX(vaddr); + u32 pmd_phys = virt_to_phys(pmd); + + /* Domain is 0, therefore no writes. */ + pgd->entry[pgd_i] = (pgd_t)pmd_phys; + pgd->entry[pgd_i] |= PGD_TYPE_COARSE; +} + +/* + * Same as normal mapping but with some boot tweaks. + */ +void add_boot_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + pmd_table_t *pmd; + pgd_table_t *pgd = &init_pgd; + unsigned int numpages = (size >> PAGE_BITS); + + if (size < PAGE_SIZE) { + printascii("Error: Mapping size must be in bytes not pages.\n"); + while(1); + } + if (size & PAGE_MASK) + numpages++; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + /* Map all consecutive pages that cover given size */ + for (int i = 0; i < numpages; i++) { + /* Check if another mapping already has a pmd attached. */ + pmd = pmd_exists(pgd, vaddr); + if (!pmd) { + /* + * If this is the first vaddr in + * this pmd, allocate new pmd + */ + pmd = alloc_boot_pmd(); + + /* Attach pmd to its entry in pgd */ + attach_pmd(pgd, pmd, vaddr); + } + + /* Attach paddr to this pmd */ + __add_mapping(page_align(paddr), + page_align(vaddr), flags, pmd); + + /* Go to the next page to be mapped */ + paddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } +} + +/* + * Maps @paddr to @vaddr, covering @size bytes also allocates new pmd if + * necessary. This flavor explicitly supplies the pgd to modify. This is useful + * when modifying userspace of processes that are not currently running. (Only + * makes sense for userspace mappings since kernel mappings are common.) + */ +void add_mapping_pgd(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags, + pgd_table_t *pgd) +{ + pmd_table_t *pmd; + unsigned int numpages = (size >> PAGE_BITS); + if (size < PAGE_SIZE) { + printascii("Error: Mapping size must be in bytes not pages.\n"); + while(1); + } + if (size & PAGE_MASK) + numpages++; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + /* Map all consecutive pages that cover given size */ + for (int i = 0; i < numpages; i++) { + /* Check if another mapping already has a pmd attached. */ + pmd = pmd_exists(pgd, vaddr); + if (!pmd) { + /* + * If this is the first vaddr in + * this pmd, allocate new pmd + */ + pmd = alloc_pmd(); + + /* Attach pmd to its entry in pgd */ + attach_pmd(pgd, pmd, vaddr); + } + + /* Attach paddr to this pmd */ + __add_mapping(page_align(paddr), + page_align(vaddr), flags, pmd); + + /* Go to the next page to be mapped */ + paddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } +} + +void add_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + add_mapping_pgd(paddr, vaddr, size, flags, TASK_PGD(current)); +} + +/* + * Checks if a virtual address range has same or more permissive + * flags than the given ones, returns 0 if not, and 1 if OK. + */ +int check_mapping_pgd(unsigned long vaddr, unsigned long size, + unsigned int flags, pgd_table_t *pgd) +{ + unsigned int npages = __pfn(align_up(size, PAGE_SIZE)); + pte_t pte; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + for (int i = 0; i < npages; i++) { + pte = virt_to_pte_from_pgd(vaddr + i * PAGE_SIZE, pgd); + + /* Check if pte perms are equal or gt given flags */ + if ((pte & PTE_PROT_MASK) >= (flags & PTE_PROT_MASK)) + continue; + else + return 0; + } + + return 1; +} + +int check_mapping(unsigned long vaddr, unsigned long size, + unsigned int flags) +{ + return check_mapping_pgd(vaddr, size, flags, TASK_PGD(current)); +} + +/* FIXME: Empty PMDs should be returned here !!! */ +int __remove_mapping(pmd_table_t *pmd, unsigned long vaddr) +{ + pmd_t pmd_i = PMD_INDEX(vaddr); + int ret; + + switch (pmd->entry[pmd_i] & PMD_TYPE_MASK) { + case PMD_TYPE_FAULT: + ret = -ENOENT; + break; + case PMD_TYPE_LARGE: + pmd->entry[pmd_i] = 0; + pmd->entry[pmd_i] |= PMD_TYPE_FAULT; + ret = 0; + break; + case PMD_TYPE_SMALL: + pmd->entry[pmd_i] = 0; + pmd->entry[pmd_i] |= PMD_TYPE_FAULT; + ret = 0; + break; + default: + printk("Unknown page mapping in pmd. Assuming bug.\n"); + BUG(); + } + return ret; +} + +/* + * Tell if a pgd index is a common kernel index. This is used to distinguish + * common kernel entries in a pgd, when copying page tables. + */ +int is_kern_pgdi(int i) +{ + if ((i >= PGD_INDEX(KERNEL_AREA_START) && i < PGD_INDEX(KERNEL_AREA_END)) || + (i >= PGD_INDEX(IO_AREA_START) && i < PGD_INDEX(IO_AREA_END)) || + (i == PGD_INDEX(USER_KIP_PAGE)) || + (i == PGD_INDEX(ARM_HIGH_VECTOR)) || + (i == PGD_INDEX(ARM_SYSCALL_VECTOR)) || + (i == PGD_INDEX(USERSPACE_UART_BASE))) + return 1; + else + return 0; +} + +/* + * Removes all userspace mappings from a pgd. Frees any pmds that it + * detects to be user pmds + */ +int remove_mapping_pgd_all_user(pgd_table_t *pgd) +{ + pmd_table_t *pmd; + + /* Traverse through all pgd entries */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + + /* Detect a pgd entry that is not a kernel entry */ + if (!is_kern_pgdi(i)) { + + /* Detect a pmd entry */ + if (((pgd->entry[i] & PGD_TYPE_MASK) + == PGD_TYPE_COARSE)) { + + /* Obtain the user pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free it */ + free_pmd(pmd); + } + + /* Clear the pgd entry */ + pgd->entry[i] = PGD_TYPE_FAULT; + } + } + + return 0; +} + +int remove_mapping_pgd(unsigned long vaddr, pgd_table_t *pgd) +{ + pgd_t pgd_i = PGD_INDEX(vaddr); + pmd_table_t *pmd; + pmd_t pmd_i; + int ret; + + /* + * Clean the cache to main memory before removing the mapping. Otherwise + * entries in the cache for this mapping will cause tranlation faults + * if they're cleaned to main memory after the mapping is removed. + */ + arm_clean_invalidate_cache(); + + /* TEST: + * Can't think of a valid reason to flush tlbs here, but keeping it just + * to be safe. REMOVE: Remove it if it's unnecessary. + */ + arm_invalidate_tlb(); + + /* Return true if non-zero pgd entry */ + switch (pgd->entry[pgd_i] & PGD_TYPE_MASK) { + case PGD_TYPE_COARSE: + // printk("Removing coarse mapping @ 0x%x\n", vaddr); + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[pgd_i] + & PGD_COARSE_ALIGN_MASK)); + pmd_i = PMD_INDEX(vaddr); + ret = __remove_mapping(pmd, vaddr); + break; + + case PGD_TYPE_FAULT: + ret = -1; + break; + + case PGD_TYPE_SECTION: + printk("Removing section mapping for 0x%lx", + vaddr); + pgd->entry[pgd_i] = 0; + pgd->entry[pgd_i] |= PGD_TYPE_FAULT; + ret = 0; + break; + + case PGD_TYPE_FINE: + printk("Table mapped is a fine page table.\n" + "Fine tables are unsupported. Assuming bug.\n"); + BUG(); + break; + + default: + dprintk("Unrecognised pmd type @ pgd index:", pgd_i); + printk("Assuming bug.\n"); + BUG(); + break; + } + /* The tlb must be invalidated here because it might have cached the + * old translation for this mapping. */ + arm_invalidate_tlb(); + + return ret; +} + +int remove_mapping(unsigned long vaddr) +{ + return remove_mapping_pgd(vaddr, TASK_PGD(current)); +} + +int delete_page_tables(struct address_space *space) +{ + remove_mapping_pgd_all_user(space->pgd); + free_pgd(space->pgd); + return 0; +} + +/* + * Copies userspace entries of one task to another. In order to do that, + * it allocates new pmds and copies the original values into new ones. + */ +int copy_user_tables(struct address_space *new, struct address_space *orig_space) +{ + pgd_table_t *to = new->pgd, *from = orig_space->pgd; + pmd_table_t *pmd, *orig; + + /* Allocate and copy all pmds that will be exclusive to new task. */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry that is not a kernel pmd? */ + if (!is_kern_pgdi(i) && + ((from->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE)) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) + goto out_error; + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((from->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + to->entry[i] = (pgd_t)virt_to_phys(pmd); + to->entry[i] |= PGD_TYPE_COARSE; + } + } + + return 0; + +out_error: + /* Find all non-kernel pmds we have just allocated and free them */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Non-kernel pmd that has just been allocated. */ + if (!is_kern_pgdi(i) && + (to->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Obtain the pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((to->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free pmd */ + free_pmd(pmd); + } + } + return -ENOMEM; +} + +int pgd_count_pmds(pgd_table_t *pgd) +{ + int npmd = 0; + + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) + if ((pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) + npmd++; + return npmd; +} + +/* + * Allocates and copies all levels of page tables from one task to another. + * Useful when forking. + * + * The copied page tables end up having shared pmds for kernel entries + * and private copies of same pmds for user entries. + */ +pgd_table_t *copy_page_tables(pgd_table_t *from) +{ + pmd_table_t *pmd, *orig; + pgd_table_t *pgd; + + /* Allocate and copy pgd. This includes all kernel entries */ + if (!(pgd = alloc_pgd())) + return PTR_ERR(-ENOMEM); + + /* First copy whole pgd entries */ + memcpy(pgd, from, sizeof(pgd_table_t)); + + /* Allocate and copy all pmds that will be exclusive to new task. */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry that is not a kernel pmd? */ + if (!is_kern_pgdi(i) && + ((pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE)) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) + goto out_error; + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + pgd->entry[i] = (pgd_t)virt_to_phys(pmd); + pgd->entry[i] |= PGD_TYPE_COARSE; + } + } + + return pgd; + +out_error: + /* Find all allocated non-kernel pmds and free them */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Non-kernel pmd that has just been allocated. */ + if (!is_kern_pgdi(i) && + (pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Obtain the pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free pmd */ + free_pmd(pmd); + } + } + /* Free the pgd */ + free_pgd(pgd); + return PTR_ERR(-ENOMEM); +} + +extern pmd_table_t *pmd_array; + +/* + * Jumps from boot pmd/pgd page tables to tables allocated from the cache. + */ +pgd_table_t *realloc_page_tables(void) +{ + pgd_table_t *pgd_new = alloc_pgd(); + pgd_table_t *pgd_old = &init_pgd; + pmd_table_t *orig, *pmd; + + /* Copy whole pgd entries */ + memcpy(pgd_new, pgd_old, sizeof(pgd_table_t)); + + /* Allocate and copy all pmds */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry */ + if ((pgd_old->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) { + printk("FATAL: PMD allocation " + "failed during system initialization\n"); + BUG(); + } + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((pgd_old->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + pgd_new->entry[i] = (pgd_t)virt_to_phys(pmd); + pgd_new->entry[i] |= PGD_TYPE_COARSE; + } + } + + /* Switch the virtual memory system into new area */ + arm_clean_invalidate_cache(); + arm_drain_writebuffer(); + arm_invalidate_tlb(); + arm_set_ttb(virt_to_phys(pgd_new)); + arm_invalidate_tlb(); + + printk("%s: Initial page tables moved from 0x%x to 0x%x physical\n", + __KERNELNAME__, virt_to_phys(pgd_old), + virt_to_phys(pgd_new)); + + return pgd_new; +} + +/* + * Useful for upgrading to page-grained control over a section mapping: + * Remaps a section mapping in pages. It allocates a pmd, (at all times because + * there can't really be an already existing pmd for a section mapping) fills + * in the page information, and origaces the direct section physical translation + * with the address of the pmd. Flushes the caches/tlbs. + */ +void remap_as_pages(void *vstart, void *vend) +{ + unsigned long pstart = virt_to_phys(vstart); + unsigned long pend = virt_to_phys(vend); + unsigned long paddr = pstart; + pgd_t pgd_i = PGD_INDEX(vstart); + pmd_t pmd_i = PMD_INDEX(vstart); + pgd_table_t *pgd = &init_pgd; + pmd_table_t *pmd = alloc_boot_pmd(); + u32 pmd_phys = virt_to_phys(pmd); + int numpages = __pfn(pend - pstart); + + /* Fill in the pmd first */ + for (int n = 0; n < numpages; n++) { + pmd->entry[pmd_i + n] = paddr; + pmd->entry[pmd_i + n] |= PMD_TYPE_SMALL; /* Small page type */ + pmd->entry[pmd_i + n] |= space_flags_to_ptflags(MAP_SVC_DEFAULT_FLAGS); + paddr += PAGE_SIZE; + } + + /* Fill in the type to produce a complete pmd translator information */ + pmd_phys |= PGD_TYPE_COARSE; + + /* Make sure memory is coherent first. */ + arm_clean_invalidate_cache(); + arm_invalidate_tlb(); + + /* Replace the direct section physical address with pmd's address */ + pgd->entry[pgd_i] = (pgd_t)pmd_phys; + printk("%s: Kernel area 0x%lx - 0x%lx remapped as %d pages\n", __KERNELNAME__, + (unsigned long)vstart, (unsigned long)vend, numpages); +} + +void copy_pgds_by_vrange(pgd_table_t *to, pgd_table_t *from, + unsigned long start, unsigned long end) +{ + unsigned long start_i = PGD_INDEX(start); + unsigned long end_i = PGD_INDEX(end); + unsigned long irange = (end_i != 0) ? (end_i - start_i) + : (PGD_ENTRY_TOTAL - start_i); + + memcpy(&to->entry[start_i], &from->entry[start_i], + irange * sizeof(pgd_t)); +} + + +/* Scheduler uses this to switch context */ +void arch_hardware_flush(pgd_table_t *pgd) +{ + arm_clean_invalidate_cache(); + arm_invalidate_tlb(); + arm_set_ttb(virt_to_phys(pgd)); + arm_invalidate_tlb(); +} + diff --git a/src/arch/arm/v6/mmu_ops.S b/src/arch/arm/v6/mmu_ops.S index e69de29..eb6d9da 100644 --- a/src/arch/arm/v6/mmu_ops.S +++ b/src/arch/arm/v6/mmu_ops.S @@ -0,0 +1,155 @@ +/* + * low-level mmu operations + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include INC_ARCH(asm.h) + +#define C15_id c0 +#define C15_control c1 +#define C15_ttb c2 +#define C15_dom c3 +#define C15_fsr c5 +#define C15_far c6 +#define C15_tlb c8 + +#define C15_C0_M 0x0001 /* MMU */ +#define C15_C0_A 0x0002 /* Alignment */ +#define C15_C0_C 0x0004 /* (D) Cache */ +#define C15_C0_W 0x0008 /* Write buffer */ +#define C15_C0_B 0x0080 /* Endianness */ +#define C15_C0_S 0x0100 /* System */ +#define C15_C0_R 0x0200 /* ROM */ +#define C15_C0_Z 0x0800 /* Branch Prediction */ +#define C15_C0_I 0x1000 /* I cache */ +#define C15_C0_V 0x2000 /* High vectors */ + +/* FIXME: Make sure the ops that need r0 dont trash r0, or if they do, + * save it on stack before these operations. + */ + +/* + * In ARM terminology, flushing the cache means invalidating its contents. + * Cleaning the cache means, writing the contents of the cache back to + * main memory. In write-back caches the cache must be cleaned before + * flushing otherwise in-cache data is lost. + */ + +BEGIN_PROC(arm_set_ttb) + mcr p15, 0, r0, C15_ttb, c0, 0 + mov pc, lr +END_PROC(arm_set_ttb) + +BEGIN_PROC(arm_get_domain) + mrc p15, 0, r0, C15_dom, c0, 0 + mov pc, lr +END_PROC(arm_get_domain) + +BEGIN_PROC(arm_set_domain) + mcr p15, 0, r0, C15_dom, c0, 0 + mov pc, lr +END_PROC(arm_set_domain) + +BEGIN_PROC(arm_enable_mmu) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_M + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_mmu) + +BEGIN_PROC(arm_enable_icache) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_I + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_icache) + +BEGIN_PROC(arm_enable_dcache) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_C + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_dcache) + +BEGIN_PROC(arm_enable_wbuffer) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_W + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_wbuffer) + +BEGIN_PROC(arm_enable_high_vectors) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_V + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_high_vectors) + +BEGIN_PROC(arm_invalidate_cache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c7 @ Flush I cache and D cache + mov pc, lr +END_PROC(arm_invalidate_cache) + +BEGIN_PROC(arm_invalidate_icache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c5, 0 @ Flush I cache + mov pc, lr +END_PROC(arm_invalidate_icache) + +BEGIN_PROC(arm_invalidate_dcache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c6, 0 @ Flush D cache + mov pc, lr +END_PROC(arm_invalidate_dcache) + +BEGIN_PROC(arm_clean_dcache) + mcr p15, 0 , pc, c7, c10, 3 @ Test/clean dcache line + bne arm_clean_dcache + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_dcache) + +BEGIN_PROC(arm_clean_invalidate_dcache) +1: + mrc p15, 0, pc, c7, c14, 3 @ Test/clean/flush dcache line + @ COMMENT: Why use PC? + bne 1b + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_invalidate_dcache) + +BEGIN_PROC(arm_clean_invalidate_cache) +1: + mrc p15, 0, r15, c7, c14, 3 @ Test/clean/flush dcache line + @ COMMENT: Why use PC? + bne 1b + mcr p15, 0, ip, c7, c5, 0 @ Flush icache + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_invalidate_cache) + +BEGIN_PROC(arm_drain_writebuffer) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c10, 4 + mov pc, lr +END_PROC(arm_drain_writebuffer) + +BEGIN_PROC(arm_invalidate_tlb) + mcr p15, 0, ip, c8, c7 + mov pc, lr +END_PROC(arm_invalidate_tlb) + +BEGIN_PROC(arm_invalidate_itlb) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c8, c5, 0 + mov pc, lr +END_PROC(arm_invalidate_itlb) + +BEGIN_PROC(arm_invalidate_dtlb) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c8, c6, 0 + mov pc, lr +END_PROC(arm_invalidate_dtlb) + diff --git a/src/arch/arm/v6/mutex.S b/src/arch/arm/v6/mutex.S new file mode 100644 index 0000000..bc81708 --- /dev/null +++ b/src/arch/arm/v6/mutex.S @@ -0,0 +1,90 @@ +/* + * ARM v5 Binary semaphore (mutex) implementation. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#include INC_ARCH(asm.h) + +/* Recap on swp: + * swp rx, ry, [rz] + * In one instruction: + * 1) Stores the value in ry into location pointed by rz. + * 2) Loads the value in the location of rz into rx. + * By doing so, in one instruction one can attempt to lock + * a word, and discover whether it was already locked. + */ + +#define MUTEX_UNLOCKED 0 +#define MUTEX_LOCKED 1 + +BEGIN_PROC(__spin_lock) + mov r1, #1 +__spin: + swp r2, r1, [r0] + cmp r2, #0 + bne __spin + mov pc, lr +END_PROC(__spin_lock) + +BEGIN_PROC(__spin_unlock) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 @ Debug check. +1: + bne 1b + mov pc, lr +END_PROC(__spin_unlock) + + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_lock) + mov r1, #1 + swp r2, r1, [r0] + cmp r2, #0 + movne r0, #0 + moveq r0, #1 + mov pc, lr +END_PROC(__mutex_lock) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_unlock) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 +1: @ Debug check. + bne 1b + mov pc, lr +END_PROC(__mutex_unlock) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_inc) + swp r2, r1, [r0] + mov r1, #1 + swp r2, r1, [r0] + cmp r2, #0 + movne r0, #0 + moveq r0, #1 + mov pc, lr +END_PROC(__mutex_inc) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_dec) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 +1: @ Debug check. + bne 1b + mov pc, lr +END_PROC(__mutex_dec) + + diff --git a/src/arch/arm/v7/mm.c b/src/arch/arm/v7/mm.c new file mode 100644 index 0000000..1b7e339 --- /dev/null +++ b/src/arch/arm/v7/mm.c @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_PLAT(printascii.h) +#include INC_GLUE(memlayout.h) +#include INC_ARCH(linker.h) +#include INC_ARCH(asm.h) +#include INC_API(kip.h) + +/* + * These are indices into arrays with pgd_t or pmd_t sized elements, + * therefore the index must be divided by appropriate element size + */ +#define PGD_INDEX(x) (((((unsigned long)(x)) >> 18) & 0x3FFC) / sizeof(pgd_t)) +/* Strip out the page offset in this megabyte from a total of 256 pages. */ +#define PMD_INDEX(x) (((((unsigned long)(x)) >> 10) & 0x3FC) / sizeof (pmd_t)) + +/* + * Removes initial mappings needed for transition to virtual memory. + * Used one-time only. + */ +void remove_section_mapping(unsigned long vaddr) +{ + pgd_table_t *pgd = &init_pgd;; + pgd_t pgd_i = PGD_INDEX(vaddr); + if (!((pgd->entry[pgd_i] & PGD_TYPE_MASK) + & PGD_TYPE_SECTION)) + while(1); + pgd->entry[pgd_i] = 0; + pgd->entry[pgd_i] |= PGD_TYPE_FAULT; + arm_invalidate_tlb(); +} + +/* + * Maps given section-aligned @paddr to @vaddr using enough number + * of section-units to fulfill @size in sections. Note this overwrites + * a mapping if same virtual address was already mapped. + */ +void __add_section_mapping_init(unsigned int paddr, + unsigned int vaddr, + unsigned int size, + unsigned int flags) +{ + pte_t *ppte; + unsigned int l1_ptab; + unsigned int l1_offset; + + /* 1st level page table address */ + l1_ptab = virt_to_phys(&init_pgd); + + /* Get the section offset for this vaddr */ + l1_offset = (vaddr >> 18) & 0x3FFC; + + /* The beginning entry for mapping */ + ppte = (unsigned int *)(l1_ptab + l1_offset); + for(int i = 0; i < size; i++) { + *ppte = 0; /* Clear out old value */ + *ppte |= paddr; /* Assign physical address */ + *ppte |= PGD_TYPE_SECTION; /* Assign translation type */ + /* Domain is 0, therefore no writes. */ + /* Only kernel access allowed */ + *ppte |= (SVC_RW_USR_NONE << SECTION_AP0); + /* Cacheability/Bufferability flags */ + *ppte |= flags; + ppte++; /* Next section entry */ + paddr += ARM_SECTION_SIZE; /* Next physical section */ + } + return; +} + +void add_section_mapping_init(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + unsigned int psection; + unsigned int vsection; + + /* Align each address to the pages they reside in */ + psection = paddr & ~ARM_SECTION_MASK; + vsection = vaddr & ~ARM_SECTION_MASK; + + if(size == 0) + return; + + __add_section_mapping_init(psection, vsection, size, flags); + + return; +} + +/* TODO: Make sure to flush tlb entry and caches */ +void __add_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int flags, pmd_table_t *pmd) +{ + unsigned int pmd_i = PMD_INDEX(vaddr); + pmd->entry[pmd_i] = paddr; + pmd->entry[pmd_i] |= PMD_TYPE_SMALL; /* Small page type */ + pmd->entry[pmd_i] |= flags; + + /* TODO: Is both required? Investigate */ + + /* TEST: + * I think cleaning or invalidating the cache is not required, + * because the entries in the cache aren't for the new mapping anyway. + * It's required if a mapping is removed, but not when newly added. + */ + arm_clean_invalidate_cache(); + + /* TEST: tlb must be flushed because a new mapping is present in page + * tables, and tlb is inconsistent with the page tables */ + arm_invalidate_tlb(); +} + +/* Return whether a pmd associated with @vaddr is mapped on a pgd or not. */ +pmd_table_t *pmd_exists(pgd_table_t *pgd, unsigned long vaddr) +{ + unsigned int pgd_i = PGD_INDEX(vaddr); + + /* Return true if non-zero pgd entry */ + switch (pgd->entry[pgd_i] & PGD_TYPE_MASK) { + case PGD_TYPE_COARSE: + return (pmd_table_t *) + phys_to_virt((pgd->entry[pgd_i] & + PGD_COARSE_ALIGN_MASK)); + break; + + case PGD_TYPE_FAULT: + return 0; + break; + + case PGD_TYPE_SECTION: + dprintk("Warning, a section is already mapped " + "where a coarse page mapping is attempted:", + (u32)(pgd->entry[pgd_i] + & PGD_SECTION_ALIGN_MASK)); + BUG(); + break; + + case PGD_TYPE_FINE: + dprintk("Warning, a fine page table is already mapped " + "where a coarse page mapping is attempted:", + (u32)(pgd->entry[pgd_i] + & PGD_FINE_ALIGN_MASK)); + printk("Fine tables are unsupported. "); + printk("What is this doing here?"); + BUG(); + break; + + default: + dprintk("Unrecognised pmd type @ pgd index:", pgd_i); + BUG(); + break; + } + return 0; +} + +/* Convert a virtual address to a pte if it exists in the page tables. */ +pte_t virt_to_pte_from_pgd(unsigned long virtual, pgd_table_t *pgd) +{ + pmd_table_t *pmd = pmd_exists(pgd, virtual); + + if (pmd) + return (pte_t)pmd->entry[PMD_INDEX(virtual)]; + else + return (pte_t)0; +} + +/* Convert a virtual address to a pte if it exists in the page tables. */ +pte_t virt_to_pte(unsigned long virtual) +{ + return virt_to_pte_from_pgd(virtual, TASK_PGD(current)); +} + +unsigned long virt_to_phys_by_pgd(unsigned long vaddr, pgd_table_t *pgd) +{ + pte_t pte = virt_to_pte_from_pgd(vaddr, pgd); + return pte & ~PAGE_MASK; +} + +unsigned long virt_to_phys_by_task(unsigned long vaddr, struct ktcb *task) +{ + return virt_to_phys_by_pgd(vaddr, TASK_PGD(task)); +} + +void attach_pmd(pgd_table_t *pgd, pmd_table_t *pmd, unsigned int vaddr) +{ + u32 pgd_i = PGD_INDEX(vaddr); + u32 pmd_phys = virt_to_phys(pmd); + + /* Domain is 0, therefore no writes. */ + pgd->entry[pgd_i] = (pgd_t)pmd_phys; + pgd->entry[pgd_i] |= PGD_TYPE_COARSE; +} + +/* + * Same as normal mapping but with some boot tweaks. + */ +void add_boot_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + pmd_table_t *pmd; + pgd_table_t *pgd = &init_pgd; + unsigned int numpages = (size >> PAGE_BITS); + + if (size < PAGE_SIZE) { + printascii("Error: Mapping size must be in bytes not pages.\n"); + while(1); + } + if (size & PAGE_MASK) + numpages++; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + /* Map all consecutive pages that cover given size */ + for (int i = 0; i < numpages; i++) { + /* Check if another mapping already has a pmd attached. */ + pmd = pmd_exists(pgd, vaddr); + if (!pmd) { + /* + * If this is the first vaddr in + * this pmd, allocate new pmd + */ + pmd = alloc_boot_pmd(); + + /* Attach pmd to its entry in pgd */ + attach_pmd(pgd, pmd, vaddr); + } + + /* Attach paddr to this pmd */ + __add_mapping(page_align(paddr), + page_align(vaddr), flags, pmd); + + /* Go to the next page to be mapped */ + paddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } +} + +/* + * Maps @paddr to @vaddr, covering @size bytes also allocates new pmd if + * necessary. This flavor explicitly supplies the pgd to modify. This is useful + * when modifying userspace of processes that are not currently running. (Only + * makes sense for userspace mappings since kernel mappings are common.) + */ +void add_mapping_pgd(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags, + pgd_table_t *pgd) +{ + pmd_table_t *pmd; + unsigned int numpages = (size >> PAGE_BITS); + + + if (size < PAGE_SIZE) { + printascii("Error: Mapping size must be in bytes not pages.\n"); + while(1); + } + if (size & PAGE_MASK) + numpages++; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + /* Map all consecutive pages that cover given size */ + for (int i = 0; i < numpages; i++) { + /* Check if another mapping already has a pmd attached. */ + pmd = pmd_exists(pgd, vaddr); + if (!pmd) { + /* + * If this is the first vaddr in + * this pmd, allocate new pmd + */ + pmd = alloc_pmd(); + + /* Attach pmd to its entry in pgd */ + attach_pmd(pgd, pmd, vaddr); + } + + /* Attach paddr to this pmd */ + __add_mapping(page_align(paddr), + page_align(vaddr), flags, pmd); + + /* Go to the next page to be mapped */ + paddr += PAGE_SIZE; + vaddr += PAGE_SIZE; + } +} + +void add_mapping(unsigned int paddr, unsigned int vaddr, + unsigned int size, unsigned int flags) +{ + add_mapping_pgd(paddr, vaddr, size, flags, TASK_PGD(current)); +} + +/* + * Checks if a virtual address range has same or more permissive + * flags than the given ones, returns 0 if not, and 1 if OK. + */ +int check_mapping_pgd(unsigned long vaddr, unsigned long size, + unsigned int flags, pgd_table_t *pgd) +{ + unsigned int npages = __pfn(align_up(size, PAGE_SIZE)); + pte_t pte; + + /* Convert generic map flags to pagetable-specific */ + BUG_ON(!(flags = space_flags_to_ptflags(flags))); + + for (int i = 0; i < npages; i++) { + pte = virt_to_pte_from_pgd(vaddr + i * PAGE_SIZE, pgd); + + /* Check if pte perms are equal or gt given flags */ + if ((pte & PTE_PROT_MASK) >= (flags & PTE_PROT_MASK)) + continue; + else + return 0; + } + + return 1; +} + +int check_mapping(unsigned long vaddr, unsigned long size, + unsigned int flags) +{ + return check_mapping_pgd(vaddr, size, flags, TASK_PGD(current)); +} + +/* FIXME: Empty PMDs should be returned here !!! */ +int __remove_mapping(pmd_table_t *pmd, unsigned long vaddr) +{ + pmd_t pmd_i = PMD_INDEX(vaddr); + int ret; + + switch (pmd->entry[pmd_i] & PMD_TYPE_MASK) { + case PMD_TYPE_FAULT: + ret = -ENOENT; + break; + case PMD_TYPE_LARGE: + pmd->entry[pmd_i] = 0; + pmd->entry[pmd_i] |= PMD_TYPE_FAULT; + ret = 0; + break; + case PMD_TYPE_SMALL: + pmd->entry[pmd_i] = 0; + pmd->entry[pmd_i] |= PMD_TYPE_FAULT; + ret = 0; + break; + default: + printk("Unknown page mapping in pmd. Assuming bug.\n"); + BUG(); + } + return ret; +} + +/* + * Tell if a pgd index is a common kernel index. This is used to distinguish + * common kernel entries in a pgd, when copying page tables. + */ +int is_kern_pgdi(int i) +{ + if ((i >= PGD_INDEX(KERNEL_AREA_START) && i < PGD_INDEX(KERNEL_AREA_END)) || + (i >= PGD_INDEX(IO_AREA_START) && i < PGD_INDEX(IO_AREA_END)) || + (i == PGD_INDEX(USER_KIP_PAGE)) || + (i == PGD_INDEX(ARM_HIGH_VECTOR)) || + (i == PGD_INDEX(ARM_SYSCALL_VECTOR)) || + (i == PGD_INDEX(USERSPACE_UART_BASE))) + return 1; + else + return 0; +} + +/* + * Removes all userspace mappings from a pgd. Frees any pmds that it + * detects to be user pmds + */ +int remove_mapping_pgd_all_user(pgd_table_t *pgd) +{ + pmd_table_t *pmd; + + /* Traverse through all pgd entries */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + + /* Detect a pgd entry that is not a kernel entry */ + if (!is_kern_pgdi(i)) { + + /* Detect a pmd entry */ + if (((pgd->entry[i] & PGD_TYPE_MASK) + == PGD_TYPE_COARSE)) { + + /* Obtain the user pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free it */ + free_pmd(pmd); + } + + /* Clear the pgd entry */ + pgd->entry[i] = PGD_TYPE_FAULT; + } + } + + return 0; +} + +int remove_mapping_pgd(unsigned long vaddr, pgd_table_t *pgd) +{ + pgd_t pgd_i = PGD_INDEX(vaddr); + pmd_table_t *pmd; + pmd_t pmd_i; + int ret; + + /* + * Clean the cache to main memory before removing the mapping. Otherwise + * entries in the cache for this mapping will cause tranlation faults + * if they're cleaned to main memory after the mapping is removed. + */ + arm_clean_invalidate_cache(); + + /* TEST: + * Can't think of a valid reason to flush tlbs here, but keeping it just + * to be safe. REMOVE: Remove it if it's unnecessary. + */ + arm_invalidate_tlb(); + + /* Return true if non-zero pgd entry */ + switch (pgd->entry[pgd_i] & PGD_TYPE_MASK) { + case PGD_TYPE_COARSE: + // printk("Removing coarse mapping @ 0x%x\n", vaddr); + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[pgd_i] + & PGD_COARSE_ALIGN_MASK)); + pmd_i = PMD_INDEX(vaddr); + ret = __remove_mapping(pmd, vaddr); + break; + + case PGD_TYPE_FAULT: + ret = -1; + break; + + case PGD_TYPE_SECTION: + printk("Removing section mapping for 0x%lx", + vaddr); + pgd->entry[pgd_i] = 0; + pgd->entry[pgd_i] |= PGD_TYPE_FAULT; + ret = 0; + break; + + case PGD_TYPE_FINE: + printk("Table mapped is a fine page table.\n" + "Fine tables are unsupported. Assuming bug.\n"); + BUG(); + break; + + default: + dprintk("Unrecognised pmd type @ pgd index:", pgd_i); + printk("Assuming bug.\n"); + BUG(); + break; + } + /* The tlb must be invalidated here because it might have cached the + * old translation for this mapping. */ + arm_invalidate_tlb(); + + return ret; +} + +int remove_mapping(unsigned long vaddr) +{ + return remove_mapping_pgd(vaddr, TASK_PGD(current)); +} + +int delete_page_tables(struct address_space *space) +{ + remove_mapping_pgd_all_user(space->pgd); + free_pgd(space->pgd); + return 0; +} + +/* + * Copies userspace entries of one task to another. In order to do that, + * it allocates new pmds and copies the original values into new ones. + */ +int copy_user_tables(struct address_space *new, struct address_space *orig_space) +{ + pgd_table_t *to = new->pgd, *from = orig_space->pgd; + pmd_table_t *pmd, *orig; + + /* Allocate and copy all pmds that will be exclusive to new task. */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry that is not a kernel pmd? */ + if (!is_kern_pgdi(i) && + ((from->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE)) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) + goto out_error; + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((from->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + to->entry[i] = (pgd_t)virt_to_phys(pmd); + to->entry[i] |= PGD_TYPE_COARSE; + } + } + + return 0; + +out_error: + /* Find all non-kernel pmds we have just allocated and free them */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Non-kernel pmd that has just been allocated. */ + if (!is_kern_pgdi(i) && + (to->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Obtain the pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((to->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free pmd */ + free_pmd(pmd); + } + } + return -ENOMEM; +} + +int pgd_count_pmds(pgd_table_t *pgd) +{ + int npmd = 0; + + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) + if ((pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) + npmd++; + return npmd; +} + +/* + * Allocates and copies all levels of page tables from one task to another. + * Useful when forking. + * + * The copied page tables end up having shared pmds for kernel entries + * and private copies of same pmds for user entries. + */ +pgd_table_t *copy_page_tables(pgd_table_t *from) +{ + pmd_table_t *pmd, *orig; + pgd_table_t *pgd; + + /* Allocate and copy pgd. This includes all kernel entries */ + if (!(pgd = alloc_pgd())) + return PTR_ERR(-ENOMEM); + + /* First copy whole pgd entries */ + memcpy(pgd, from, sizeof(pgd_table_t)); + + /* Allocate and copy all pmds that will be exclusive to new task. */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry that is not a kernel pmd? */ + if (!is_kern_pgdi(i) && + ((pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE)) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) + goto out_error; + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + pgd->entry[i] = (pgd_t)virt_to_phys(pmd); + pgd->entry[i] |= PGD_TYPE_COARSE; + } + } + + return pgd; + +out_error: + /* Find all allocated non-kernel pmds and free them */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Non-kernel pmd that has just been allocated. */ + if (!is_kern_pgdi(i) && + (pgd->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Obtain the pmd handle */ + pmd = (pmd_table_t *) + phys_to_virt((pgd->entry[i] & + PGD_COARSE_ALIGN_MASK)); + /* Free pmd */ + free_pmd(pmd); + } + } + /* Free the pgd */ + free_pgd(pgd); + return PTR_ERR(-ENOMEM); +} + +extern pmd_table_t *pmd_array; + +/* + * Jumps from boot pmd/pgd page tables to tables allocated from the cache. + */ +pgd_table_t *realloc_page_tables(void) +{ + pgd_table_t *pgd_new = alloc_pgd(); + pgd_table_t *pgd_old = &init_pgd; + pmd_table_t *orig, *pmd; + + /* Copy whole pgd entries */ + memcpy(pgd_new, pgd_old, sizeof(pgd_table_t)); + + /* Allocate and copy all pmds */ + for (int i = 0; i < PGD_ENTRY_TOTAL; i++) { + /* Detect a pmd entry */ + if ((pgd_old->entry[i] & PGD_TYPE_MASK) == PGD_TYPE_COARSE) { + /* Allocate new pmd */ + if (!(pmd = alloc_pmd())) { + printk("FATAL: PMD allocation " + "failed during system initialization\n"); + BUG(); + } + + /* Find original pmd */ + orig = (pmd_table_t *) + phys_to_virt((pgd_old->entry[i] & + PGD_COARSE_ALIGN_MASK)); + + /* Copy original to new */ + memcpy(pmd, orig, sizeof(pmd_table_t)); + + /* Replace original pmd entry in pgd with new */ + pgd_new->entry[i] = (pgd_t)virt_to_phys(pmd); + pgd_new->entry[i] |= PGD_TYPE_COARSE; + } + } + + /* Switch the virtual memory system into new area */ + arm_clean_invalidate_cache(); + arm_drain_writebuffer(); + arm_invalidate_tlb(); + arm_set_ttb(virt_to_phys(pgd_new)); + arm_invalidate_tlb(); + + printk("%s: Initial page tables moved from 0x%x to 0x%x physical\n", + __KERNELNAME__, virt_to_phys(pgd_old), + virt_to_phys(pgd_new)); + + return pgd_new; +} + +/* + * Useful for upgrading to page-grained control over a section mapping: + * Remaps a section mapping in pages. It allocates a pmd, (at all times because + * there can't really be an already existing pmd for a section mapping) fills + * in the page information, and origaces the direct section physical translation + * with the address of the pmd. Flushes the caches/tlbs. + */ +void remap_as_pages(void *vstart, void *vend) +{ + unsigned long pstart = virt_to_phys(vstart); + unsigned long pend = virt_to_phys(vend); + unsigned long paddr = pstart; + pgd_t pgd_i = PGD_INDEX(vstart); + pmd_t pmd_i = PMD_INDEX(vstart); + pgd_table_t *pgd = &init_pgd; + pmd_table_t *pmd = alloc_boot_pmd(); + u32 pmd_phys = virt_to_phys(pmd); + int numpages = __pfn(pend - pstart); + + /* Fill in the pmd first */ + for (int n = 0; n < numpages; n++) { + pmd->entry[pmd_i + n] = paddr; + pmd->entry[pmd_i + n] |= PMD_TYPE_SMALL; /* Small page type */ + pmd->entry[pmd_i + n] |= space_flags_to_ptflags(MAP_SVC_DEFAULT_FLAGS); + paddr += PAGE_SIZE; + } + + /* Fill in the type to produce a complete pmd translator information */ + pmd_phys |= PGD_TYPE_COARSE; + + /* Make sure memory is coherent first. */ + arm_clean_invalidate_cache(); + arm_invalidate_tlb(); + + /* Replace the direct section physical address with pmd's address */ + pgd->entry[pgd_i] = (pgd_t)pmd_phys; + printk("%s: Kernel area 0x%lx - 0x%lx remapped as %d pages\n", __KERNELNAME__, + (unsigned long)vstart, (unsigned long)vend, numpages); +} + +void copy_pgds_by_vrange(pgd_table_t *to, pgd_table_t *from, + unsigned long start, unsigned long end) +{ + unsigned long start_i = PGD_INDEX(start); + unsigned long end_i = PGD_INDEX(end); + unsigned long irange = (end_i != 0) ? (end_i - start_i) + : (PGD_ENTRY_TOTAL - start_i); + + memcpy(&to->entry[start_i], &from->entry[start_i], + irange * sizeof(pgd_t)); +} + + +/* Scheduler uses this to switch context */ +void arch_hardware_flush(pgd_table_t *pgd) +{ + arm_clean_invalidate_cache(); + arm_invalidate_tlb(); + arm_set_ttb(virt_to_phys(pgd)); + arm_invalidate_tlb(); +} + diff --git a/src/arch/arm/v7/mmu_ops.S b/src/arch/arm/v7/mmu_ops.S new file mode 100644 index 0000000..eb6d9da --- /dev/null +++ b/src/arch/arm/v7/mmu_ops.S @@ -0,0 +1,155 @@ +/* + * low-level mmu operations + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include INC_ARCH(asm.h) + +#define C15_id c0 +#define C15_control c1 +#define C15_ttb c2 +#define C15_dom c3 +#define C15_fsr c5 +#define C15_far c6 +#define C15_tlb c8 + +#define C15_C0_M 0x0001 /* MMU */ +#define C15_C0_A 0x0002 /* Alignment */ +#define C15_C0_C 0x0004 /* (D) Cache */ +#define C15_C0_W 0x0008 /* Write buffer */ +#define C15_C0_B 0x0080 /* Endianness */ +#define C15_C0_S 0x0100 /* System */ +#define C15_C0_R 0x0200 /* ROM */ +#define C15_C0_Z 0x0800 /* Branch Prediction */ +#define C15_C0_I 0x1000 /* I cache */ +#define C15_C0_V 0x2000 /* High vectors */ + +/* FIXME: Make sure the ops that need r0 dont trash r0, or if they do, + * save it on stack before these operations. + */ + +/* + * In ARM terminology, flushing the cache means invalidating its contents. + * Cleaning the cache means, writing the contents of the cache back to + * main memory. In write-back caches the cache must be cleaned before + * flushing otherwise in-cache data is lost. + */ + +BEGIN_PROC(arm_set_ttb) + mcr p15, 0, r0, C15_ttb, c0, 0 + mov pc, lr +END_PROC(arm_set_ttb) + +BEGIN_PROC(arm_get_domain) + mrc p15, 0, r0, C15_dom, c0, 0 + mov pc, lr +END_PROC(arm_get_domain) + +BEGIN_PROC(arm_set_domain) + mcr p15, 0, r0, C15_dom, c0, 0 + mov pc, lr +END_PROC(arm_set_domain) + +BEGIN_PROC(arm_enable_mmu) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_M + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_mmu) + +BEGIN_PROC(arm_enable_icache) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_I + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_icache) + +BEGIN_PROC(arm_enable_dcache) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_C + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_dcache) + +BEGIN_PROC(arm_enable_wbuffer) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_W + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_wbuffer) + +BEGIN_PROC(arm_enable_high_vectors) + mrc p15, 0, r0, C15_control, c0, 0 + orr r0, r0, #C15_C0_V + mcr p15, 0, r0, C15_control, c0, 0 + mov pc, lr +END_PROC(arm_enable_high_vectors) + +BEGIN_PROC(arm_invalidate_cache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c7 @ Flush I cache and D cache + mov pc, lr +END_PROC(arm_invalidate_cache) + +BEGIN_PROC(arm_invalidate_icache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c5, 0 @ Flush I cache + mov pc, lr +END_PROC(arm_invalidate_icache) + +BEGIN_PROC(arm_invalidate_dcache) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c6, 0 @ Flush D cache + mov pc, lr +END_PROC(arm_invalidate_dcache) + +BEGIN_PROC(arm_clean_dcache) + mcr p15, 0 , pc, c7, c10, 3 @ Test/clean dcache line + bne arm_clean_dcache + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_dcache) + +BEGIN_PROC(arm_clean_invalidate_dcache) +1: + mrc p15, 0, pc, c7, c14, 3 @ Test/clean/flush dcache line + @ COMMENT: Why use PC? + bne 1b + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_invalidate_dcache) + +BEGIN_PROC(arm_clean_invalidate_cache) +1: + mrc p15, 0, r15, c7, c14, 3 @ Test/clean/flush dcache line + @ COMMENT: Why use PC? + bne 1b + mcr p15, 0, ip, c7, c5, 0 @ Flush icache + mcr p15, 0, ip, c7, c10, 4 @ Drain WB + mov pc, lr +END_PROC(arm_clean_invalidate_cache) + +BEGIN_PROC(arm_drain_writebuffer) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c7, c10, 4 + mov pc, lr +END_PROC(arm_drain_writebuffer) + +BEGIN_PROC(arm_invalidate_tlb) + mcr p15, 0, ip, c8, c7 + mov pc, lr +END_PROC(arm_invalidate_tlb) + +BEGIN_PROC(arm_invalidate_itlb) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c8, c5, 0 + mov pc, lr +END_PROC(arm_invalidate_itlb) + +BEGIN_PROC(arm_invalidate_dtlb) + mov r0, #0 @ FIX THIS + mcr p15, 0, r0, c8, c6, 0 + mov pc, lr +END_PROC(arm_invalidate_dtlb) + diff --git a/src/arch/arm/v7/mutex.S b/src/arch/arm/v7/mutex.S new file mode 100644 index 0000000..bc81708 --- /dev/null +++ b/src/arch/arm/v7/mutex.S @@ -0,0 +1,90 @@ +/* + * ARM v5 Binary semaphore (mutex) implementation. + * + * Copyright (C) 2007 Bahadir Balban + * + */ + +#include INC_ARCH(asm.h) + +/* Recap on swp: + * swp rx, ry, [rz] + * In one instruction: + * 1) Stores the value in ry into location pointed by rz. + * 2) Loads the value in the location of rz into rx. + * By doing so, in one instruction one can attempt to lock + * a word, and discover whether it was already locked. + */ + +#define MUTEX_UNLOCKED 0 +#define MUTEX_LOCKED 1 + +BEGIN_PROC(__spin_lock) + mov r1, #1 +__spin: + swp r2, r1, [r0] + cmp r2, #0 + bne __spin + mov pc, lr +END_PROC(__spin_lock) + +BEGIN_PROC(__spin_unlock) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 @ Debug check. +1: + bne 1b + mov pc, lr +END_PROC(__spin_unlock) + + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_lock) + mov r1, #1 + swp r2, r1, [r0] + cmp r2, #0 + movne r0, #0 + moveq r0, #1 + mov pc, lr +END_PROC(__mutex_lock) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_unlock) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 +1: @ Debug check. + bne 1b + mov pc, lr +END_PROC(__mutex_unlock) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_inc) + swp r2, r1, [r0] + mov r1, #1 + swp r2, r1, [r0] + cmp r2, #0 + movne r0, #0 + moveq r0, #1 + mov pc, lr +END_PROC(__mutex_inc) + +/* + * @r0: Address of mutex location. + */ +BEGIN_PROC(__mutex_dec) + mov r1, #0 + swp r2, r1, [r0] + cmp r2, #1 +1: @ Debug check. + bne 1b + mov pc, lr +END_PROC(__mutex_dec) + + diff --git a/src/drivers/irq/gic/gic.c b/src/drivers/irq/gic/gic.c new file mode 100644 index 0000000..2b8a310 --- /dev/null +++ b/src/drivers/irq/gic/gic.c @@ -0,0 +1,104 @@ +/* + * PL190 Vectored irq controller support. + * + * This is more pb926 specific as it also touches the SIC, a partial irq + * controller.Normally, irq controller must be independent and singular. Later + * other generic code should make thlongwork in cascaded setup. + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include +#include + +/* FIXME: Fix the stupid uart driver and change to single definition of this! */ +#if defined(read) +#undef read +#endif +#if defined(write) +#undef write +#endif + +#define read(a) *((volatile unsigned int *)(a)) +#define write(v, a) (*((volatile unsigned int *)(a)) = v) +#define setbit(bitvect, a) write(read(a) | (bitvect), a) +#define clrbit(bitvect, a) write(read(a) & ~(bitvect), a) +#define devio(base, reg, bitvect, setclr) \ + ((setclr) ? setbit(bitvect, (base + reg)) \ + : clrbit(bitvect, (base + reg))) + +/* Returns the irq number on this chip converting the irq bitvector */ +int pl190_read_irq(void) +{ + /* This also correctly returns a negative value for a spurious irq. */ + return 31 - __clz(read(PL190_VIC_IRQSTATUS)); +} + +void pl190_mask_irq(int irq) +{ + /* Reading WO registers blows QEMU/PB926. + * setbit((1 << irq), PL190_VIC_INTENCLEAR); */ + write(1 << irq, PL190_VIC_INTENCLEAR); +} + +/* Ack is same as mask */ +void pl190_ack_irq(int irq) +{ + pl190_mask_irq(irq); +} + +void pl190_unmask_irq(int irq) +{ + setbit(1 << irq, PL190_VIC_INTENABLE); +} + +int pl190_sic_read_irq(void) +{ + return 32 - __clz(read(PL190_SIC_STATUS)); +} + +void pl190_sic_mask_irq(int irq) +{ + write(1 << irq, PL190_SIC_ENCLR); +} + +void pl190_sic_ack_irq(int irq) +{ + pl190_sic_mask_irq(irq); +} + +void pl190_sic_unmask_irq(int irq) +{ + setbit(1 << irq, PL190_SIC_ENSET); +} + +/* Initialises the primary and secondary interrupt controllers */ +void pl190_vic_init(void) +{ + /* Clear all interrupts */ + write(0, PL190_VIC_INTENABLE); + write(0xFFFFFFFF, PL190_VIC_INTENCLEAR); + + /* Set all irqs as normal IRQs (i.e. not FIQ) */ + write(0, PL190_VIC_INTSELECT); + /* TODO: Is there a SIC_IRQ_SELECT for irq/fiq ??? */ + + /* Disable user-mode access to VIC registers */ + write(1, PL190_VIC_PROTECTION); + + /* Clear software interrupts */ + write(0xFFFFFFFF, PL190_VIC_SOFTINTCLEAR); + + /* At this point, all interrupts are cleared and disabled. + * the controllers are ready to receive interrupts, if enabled. */ + return; +} + +void pl190_sic_init(void) +{ + write(0, PL190_SIC_ENABLE); + write(0xFFFFFFFF, PL190_SIC_ENCLR); + /* Disable SIC-to-PIC direct routing of individual irq lines on SIC */ + write(0xFFFFFFFF, PL190_SIC_PICENCLR); +} + diff --git a/src/platform/ab926/irq.c b/src/platform/ab926/irq.c new file mode 100644 index 0000000..7b63abf --- /dev/null +++ b/src/platform/ab926/irq.c @@ -0,0 +1,57 @@ +/* + * Support for generic irq handling using platform irq controller (PL190) + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include INC_PLAT(irq.h) +#include INC_PLAT(platform.h) +#include INC_ARCH(exception.h) +#include +#include + +struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = { + [0] = { + .name = "Vectored irq controller", + .level = 0, + .cascade = IRQ_SIC, + .offset = 0, + .ops = { + .init = pl190_vic_init, + .read_irq = pl190_read_irq, + .ack_and_mask = pl190_mask_irq, + .unmask = pl190_unmask_irq, + }, + }, + [1] = { + .name = "Secondary irq controller", + .level = 1, + .cascade = IRQ_NIL, + .offset = SIRQ_CHIP_OFFSET, + .ops = { + .init = pl190_sic_init, + .read_irq = pl190_sic_read_irq, + .ack_and_mask = pl190_sic_mask_irq, + .unmask = pl190_sic_unmask_irq, + }, + }, +}; + +static int platform_timer_handler(void) +{ + sp804_irq_handler(); + return do_timer_irq(); +} + +/* Built-in irq handlers initialised at compile time. + * Else register with register_irq() */ +struct irq_desc irq_desc_array[IRQS_MAX] = { + [IRQ_TIMER01] = { + .name = "Timer01", + .chip = &irq_chip_array[0], + .handler = platform_timer_handler, + }, +}; + diff --git a/src/platform/ab926/platform.c b/src/platform/ab926/platform.c new file mode 100644 index 0000000..d59c955 --- /dev/null +++ b/src/platform/ab926/platform.c @@ -0,0 +1,62 @@ +/* + * PB926 platform-specific initialisation and setup + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include +#include +#include +#include INC_ARCH(linker.h) +#include INC_PLAT(printascii.h) +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_GLUE(memlayout.h) +#include INC_PLAT(offsets.h) +#include INC_PLAT(platform.h) +#include INC_PLAT(uart.h) +#include INC_PLAT(irq.h) +#include INC_ARCH(asm.h) + +void init_platform_console(void) +{ + add_boot_mapping(PB926_UART0_BASE, PL011_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + + /* + * Map same UART IO area to userspace so that primitive uart-based + * userspace printf can work. Note, this raw mapping is to be + * removed in the future, when file-based io is implemented. + */ + add_boot_mapping(PB926_UART0_BASE, USERSPACE_UART_BASE, PAGE_SIZE, + MAP_USR_IO_FLAGS); + + uart_init(); +} + +void init_platform_timer(void) +{ + add_boot_mapping(PB926_TIMER01_BASE, PLATFORM_TIMER_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SYSCTRL_BASE, PB926_SYSCTRL_VBASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + timer_init(); +} + +void init_platform_irq_controller() +{ + add_boot_mapping(PB926_VIC_BASE, PLATFORM_IRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SIC_BASE, PLATFORM_SIRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + irq_controllers_init(); +} + +void platform_init(void) +{ + init_platform_console(); + init_platform_timer(); + init_platform_irq_controller(); +} + diff --git a/src/platform/ab926/printascii.S b/src/platform/ab926/printascii.S new file mode 100644 index 0000000..e727537 --- /dev/null +++ b/src/platform/ab926/printascii.S @@ -0,0 +1,110 @@ +/* + * Basic UART printing. + */ +#include INC_ARCH(asm.h) +#include INC_GLUE(memlayout.h) + +#define UART_DATA_OFFSET 0x0 + + .macro uart_address rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x10000000 + orreq \rx, \rx, #0x001F0000 + orreq \rx, \rx, #0x00001000 + /* FIXME: This offset is incorrect */ + movne \rx, #0xf9000000 @#IO_AREA0_VADDR + addne \rx, \rx, #PB926_UART0_VOFFSET @ UART0 page offset from + @ virtual io area base. + .endm + + .macro uart_send, ry, rx + strb \ry, [\rx, #UART_DATA_OFFSET] + .endm + + .macro uart_wait, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 5 + bne 501b + .endm + + .macro uart_busy, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 3 + bne 501b + .endm + + .text +/* + * Useful debugging routines + */ +BEGIN_PROC(printhex8) + mov r1, #8 + b printhex + +BEGIN_PROC(printhex4) + mov r1, #4 + b printhex + +BEGIN_PROC(printhex2) + mov r1, #2 +printhex: adr r2, hexbuf +@printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + + + .align + + @ vmem-linked image has strings in vmem addresses. This replaces + @ the reference with corresponding physical address. Note this + @ won't work if memory offsets aren't clear cut values for + @ orr'ing and bic'ing. rm = mmu bits rs = string address. + .macro get_straddr rs, rm + mrc p15, 0, \rm, c1, c0 @ Get MMU bits. + tst \rm, #1 @ MMU enabled? + @subeq \rs, \rs, #KERNEL_AREA_START + biceq \rs, \rs, #KERNEL_AREA_START @ Clear Virtual mem offset. + @orreq \rs, \rs, #PHYS_ADDR_BASE @ Add Phy mem offset. + .endm + +BEGIN_PROC(printascii) + get_straddr r0, r1 + uart_address r3 + b 2f +1: uart_wait r2, r3 + uart_send r1, r3 + uart_busy r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr +END_PROC(printascii) + +BEGIN_PROC(printch) + uart_address r3 + mov r1, r0 + mov r0, #0 + b 1b + +hexbuf: .space 16 + diff --git a/src/platform/ab926/timer.c b/src/platform/ab926/timer.c new file mode 100644 index 0000000..fc290af --- /dev/null +++ b/src/platform/ab926/timer.c @@ -0,0 +1,28 @@ +/* + * Ties up platform timer with generic timer api + * + * Copyright (C) 2007 Bahadir Balban + * + */ +#include +#include +#include INC_PLAT(platform.h) +#include +#include + +void timer_init(void) +{ + /* Set timer 0 to 1MHz */ + sp810_set_timclk(0, 1); + + /* Initialise timer */ + sp804_init(); +} + +void timer_start(void) +{ + irq_enable(IRQ_TIMER01); + sp804_set_irq(0, 1); /* Enable timer0 irq */ + sp804_enable(0, 1); /* Enable timer0 */ +} + diff --git a/src/platform/ab926/uart.c b/src/platform/ab926/uart.c new file mode 100644 index 0000000..35d94f5 --- /dev/null +++ b/src/platform/ab926/uart.c @@ -0,0 +1,28 @@ +/* + * Ties up platform's uart driver functions with generic API + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include INC_PLAT(platform.h) + +#include + +extern struct pl011_uart uart; + +void uart_init() +{ + uart.base = PL011_BASE; + uart.ops.initialise(&uart); +} + +/* Generic uart function that lib/putchar.c expects to see implemented */ +void uart_putc(char c) +{ + int res; + /* Platform specific uart implementation */ + do { + res = uart.ops.tx_char(c); + } while (res < 0); +} + diff --git a/src/platform/eb/irq.c b/src/platform/eb/irq.c new file mode 100644 index 0000000..7b63abf --- /dev/null +++ b/src/platform/eb/irq.c @@ -0,0 +1,57 @@ +/* + * Support for generic irq handling using platform irq controller (PL190) + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include INC_PLAT(irq.h) +#include INC_PLAT(platform.h) +#include INC_ARCH(exception.h) +#include +#include + +struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = { + [0] = { + .name = "Vectored irq controller", + .level = 0, + .cascade = IRQ_SIC, + .offset = 0, + .ops = { + .init = pl190_vic_init, + .read_irq = pl190_read_irq, + .ack_and_mask = pl190_mask_irq, + .unmask = pl190_unmask_irq, + }, + }, + [1] = { + .name = "Secondary irq controller", + .level = 1, + .cascade = IRQ_NIL, + .offset = SIRQ_CHIP_OFFSET, + .ops = { + .init = pl190_sic_init, + .read_irq = pl190_sic_read_irq, + .ack_and_mask = pl190_sic_mask_irq, + .unmask = pl190_sic_unmask_irq, + }, + }, +}; + +static int platform_timer_handler(void) +{ + sp804_irq_handler(); + return do_timer_irq(); +} + +/* Built-in irq handlers initialised at compile time. + * Else register with register_irq() */ +struct irq_desc irq_desc_array[IRQS_MAX] = { + [IRQ_TIMER01] = { + .name = "Timer01", + .chip = &irq_chip_array[0], + .handler = platform_timer_handler, + }, +}; + diff --git a/src/platform/eb/platform.c b/src/platform/eb/platform.c new file mode 100644 index 0000000..d59c955 --- /dev/null +++ b/src/platform/eb/platform.c @@ -0,0 +1,62 @@ +/* + * PB926 platform-specific initialisation and setup + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include +#include +#include +#include INC_ARCH(linker.h) +#include INC_PLAT(printascii.h) +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_GLUE(memlayout.h) +#include INC_PLAT(offsets.h) +#include INC_PLAT(platform.h) +#include INC_PLAT(uart.h) +#include INC_PLAT(irq.h) +#include INC_ARCH(asm.h) + +void init_platform_console(void) +{ + add_boot_mapping(PB926_UART0_BASE, PL011_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + + /* + * Map same UART IO area to userspace so that primitive uart-based + * userspace printf can work. Note, this raw mapping is to be + * removed in the future, when file-based io is implemented. + */ + add_boot_mapping(PB926_UART0_BASE, USERSPACE_UART_BASE, PAGE_SIZE, + MAP_USR_IO_FLAGS); + + uart_init(); +} + +void init_platform_timer(void) +{ + add_boot_mapping(PB926_TIMER01_BASE, PLATFORM_TIMER_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SYSCTRL_BASE, PB926_SYSCTRL_VBASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + timer_init(); +} + +void init_platform_irq_controller() +{ + add_boot_mapping(PB926_VIC_BASE, PLATFORM_IRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SIC_BASE, PLATFORM_SIRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + irq_controllers_init(); +} + +void platform_init(void) +{ + init_platform_console(); + init_platform_timer(); + init_platform_irq_controller(); +} + diff --git a/src/platform/eb/printascii.S b/src/platform/eb/printascii.S new file mode 100644 index 0000000..e727537 --- /dev/null +++ b/src/platform/eb/printascii.S @@ -0,0 +1,110 @@ +/* + * Basic UART printing. + */ +#include INC_ARCH(asm.h) +#include INC_GLUE(memlayout.h) + +#define UART_DATA_OFFSET 0x0 + + .macro uart_address rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x10000000 + orreq \rx, \rx, #0x001F0000 + orreq \rx, \rx, #0x00001000 + /* FIXME: This offset is incorrect */ + movne \rx, #0xf9000000 @#IO_AREA0_VADDR + addne \rx, \rx, #PB926_UART0_VOFFSET @ UART0 page offset from + @ virtual io area base. + .endm + + .macro uart_send, ry, rx + strb \ry, [\rx, #UART_DATA_OFFSET] + .endm + + .macro uart_wait, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 5 + bne 501b + .endm + + .macro uart_busy, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 3 + bne 501b + .endm + + .text +/* + * Useful debugging routines + */ +BEGIN_PROC(printhex8) + mov r1, #8 + b printhex + +BEGIN_PROC(printhex4) + mov r1, #4 + b printhex + +BEGIN_PROC(printhex2) + mov r1, #2 +printhex: adr r2, hexbuf +@printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + + + .align + + @ vmem-linked image has strings in vmem addresses. This replaces + @ the reference with corresponding physical address. Note this + @ won't work if memory offsets aren't clear cut values for + @ orr'ing and bic'ing. rm = mmu bits rs = string address. + .macro get_straddr rs, rm + mrc p15, 0, \rm, c1, c0 @ Get MMU bits. + tst \rm, #1 @ MMU enabled? + @subeq \rs, \rs, #KERNEL_AREA_START + biceq \rs, \rs, #KERNEL_AREA_START @ Clear Virtual mem offset. + @orreq \rs, \rs, #PHYS_ADDR_BASE @ Add Phy mem offset. + .endm + +BEGIN_PROC(printascii) + get_straddr r0, r1 + uart_address r3 + b 2f +1: uart_wait r2, r3 + uart_send r1, r3 + uart_busy r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr +END_PROC(printascii) + +BEGIN_PROC(printch) + uart_address r3 + mov r1, r0 + mov r0, #0 + b 1b + +hexbuf: .space 16 + diff --git a/src/platform/eb/timer.c b/src/platform/eb/timer.c new file mode 100644 index 0000000..fc290af --- /dev/null +++ b/src/platform/eb/timer.c @@ -0,0 +1,28 @@ +/* + * Ties up platform timer with generic timer api + * + * Copyright (C) 2007 Bahadir Balban + * + */ +#include +#include +#include INC_PLAT(platform.h) +#include +#include + +void timer_init(void) +{ + /* Set timer 0 to 1MHz */ + sp810_set_timclk(0, 1); + + /* Initialise timer */ + sp804_init(); +} + +void timer_start(void) +{ + irq_enable(IRQ_TIMER01); + sp804_set_irq(0, 1); /* Enable timer0 irq */ + sp804_enable(0, 1); /* Enable timer0 */ +} + diff --git a/src/platform/eb/uart.c b/src/platform/eb/uart.c new file mode 100644 index 0000000..35d94f5 --- /dev/null +++ b/src/platform/eb/uart.c @@ -0,0 +1,28 @@ +/* + * Ties up platform's uart driver functions with generic API + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include INC_PLAT(platform.h) + +#include + +extern struct pl011_uart uart; + +void uart_init() +{ + uart.base = PL011_BASE; + uart.ops.initialise(&uart); +} + +/* Generic uart function that lib/putchar.c expects to see implemented */ +void uart_putc(char c) +{ + int res; + /* Platform specific uart implementation */ + do { + res = uart.ops.tx_char(c); + } while (res < 0); +} + diff --git a/src/platform/pb11mpcore/irq.c b/src/platform/pb11mpcore/irq.c new file mode 100644 index 0000000..7b63abf --- /dev/null +++ b/src/platform/pb11mpcore/irq.c @@ -0,0 +1,57 @@ +/* + * Support for generic irq handling using platform irq controller (PL190) + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include INC_PLAT(irq.h) +#include INC_PLAT(platform.h) +#include INC_ARCH(exception.h) +#include +#include + +struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = { + [0] = { + .name = "Vectored irq controller", + .level = 0, + .cascade = IRQ_SIC, + .offset = 0, + .ops = { + .init = pl190_vic_init, + .read_irq = pl190_read_irq, + .ack_and_mask = pl190_mask_irq, + .unmask = pl190_unmask_irq, + }, + }, + [1] = { + .name = "Secondary irq controller", + .level = 1, + .cascade = IRQ_NIL, + .offset = SIRQ_CHIP_OFFSET, + .ops = { + .init = pl190_sic_init, + .read_irq = pl190_sic_read_irq, + .ack_and_mask = pl190_sic_mask_irq, + .unmask = pl190_sic_unmask_irq, + }, + }, +}; + +static int platform_timer_handler(void) +{ + sp804_irq_handler(); + return do_timer_irq(); +} + +/* Built-in irq handlers initialised at compile time. + * Else register with register_irq() */ +struct irq_desc irq_desc_array[IRQS_MAX] = { + [IRQ_TIMER01] = { + .name = "Timer01", + .chip = &irq_chip_array[0], + .handler = platform_timer_handler, + }, +}; + diff --git a/src/platform/pb11mpcore/platform.c b/src/platform/pb11mpcore/platform.c new file mode 100644 index 0000000..d59c955 --- /dev/null +++ b/src/platform/pb11mpcore/platform.c @@ -0,0 +1,62 @@ +/* + * PB926 platform-specific initialisation and setup + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include +#include +#include +#include INC_ARCH(linker.h) +#include INC_PLAT(printascii.h) +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_GLUE(memlayout.h) +#include INC_PLAT(offsets.h) +#include INC_PLAT(platform.h) +#include INC_PLAT(uart.h) +#include INC_PLAT(irq.h) +#include INC_ARCH(asm.h) + +void init_platform_console(void) +{ + add_boot_mapping(PB926_UART0_BASE, PL011_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + + /* + * Map same UART IO area to userspace so that primitive uart-based + * userspace printf can work. Note, this raw mapping is to be + * removed in the future, when file-based io is implemented. + */ + add_boot_mapping(PB926_UART0_BASE, USERSPACE_UART_BASE, PAGE_SIZE, + MAP_USR_IO_FLAGS); + + uart_init(); +} + +void init_platform_timer(void) +{ + add_boot_mapping(PB926_TIMER01_BASE, PLATFORM_TIMER_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SYSCTRL_BASE, PB926_SYSCTRL_VBASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + timer_init(); +} + +void init_platform_irq_controller() +{ + add_boot_mapping(PB926_VIC_BASE, PLATFORM_IRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SIC_BASE, PLATFORM_SIRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + irq_controllers_init(); +} + +void platform_init(void) +{ + init_platform_console(); + init_platform_timer(); + init_platform_irq_controller(); +} + diff --git a/src/platform/pb11mpcore/printascii.S b/src/platform/pb11mpcore/printascii.S new file mode 100644 index 0000000..e727537 --- /dev/null +++ b/src/platform/pb11mpcore/printascii.S @@ -0,0 +1,110 @@ +/* + * Basic UART printing. + */ +#include INC_ARCH(asm.h) +#include INC_GLUE(memlayout.h) + +#define UART_DATA_OFFSET 0x0 + + .macro uart_address rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x10000000 + orreq \rx, \rx, #0x001F0000 + orreq \rx, \rx, #0x00001000 + /* FIXME: This offset is incorrect */ + movne \rx, #0xf9000000 @#IO_AREA0_VADDR + addne \rx, \rx, #PB926_UART0_VOFFSET @ UART0 page offset from + @ virtual io area base. + .endm + + .macro uart_send, ry, rx + strb \ry, [\rx, #UART_DATA_OFFSET] + .endm + + .macro uart_wait, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 5 + bne 501b + .endm + + .macro uart_busy, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 3 + bne 501b + .endm + + .text +/* + * Useful debugging routines + */ +BEGIN_PROC(printhex8) + mov r1, #8 + b printhex + +BEGIN_PROC(printhex4) + mov r1, #4 + b printhex + +BEGIN_PROC(printhex2) + mov r1, #2 +printhex: adr r2, hexbuf +@printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + + + .align + + @ vmem-linked image has strings in vmem addresses. This replaces + @ the reference with corresponding physical address. Note this + @ won't work if memory offsets aren't clear cut values for + @ orr'ing and bic'ing. rm = mmu bits rs = string address. + .macro get_straddr rs, rm + mrc p15, 0, \rm, c1, c0 @ Get MMU bits. + tst \rm, #1 @ MMU enabled? + @subeq \rs, \rs, #KERNEL_AREA_START + biceq \rs, \rs, #KERNEL_AREA_START @ Clear Virtual mem offset. + @orreq \rs, \rs, #PHYS_ADDR_BASE @ Add Phy mem offset. + .endm + +BEGIN_PROC(printascii) + get_straddr r0, r1 + uart_address r3 + b 2f +1: uart_wait r2, r3 + uart_send r1, r3 + uart_busy r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr +END_PROC(printascii) + +BEGIN_PROC(printch) + uart_address r3 + mov r1, r0 + mov r0, #0 + b 1b + +hexbuf: .space 16 + diff --git a/src/platform/pb11mpcore/timer.c b/src/platform/pb11mpcore/timer.c new file mode 100644 index 0000000..fc290af --- /dev/null +++ b/src/platform/pb11mpcore/timer.c @@ -0,0 +1,28 @@ +/* + * Ties up platform timer with generic timer api + * + * Copyright (C) 2007 Bahadir Balban + * + */ +#include +#include +#include INC_PLAT(platform.h) +#include +#include + +void timer_init(void) +{ + /* Set timer 0 to 1MHz */ + sp810_set_timclk(0, 1); + + /* Initialise timer */ + sp804_init(); +} + +void timer_start(void) +{ + irq_enable(IRQ_TIMER01); + sp804_set_irq(0, 1); /* Enable timer0 irq */ + sp804_enable(0, 1); /* Enable timer0 */ +} + diff --git a/src/platform/pb11mpcore/uart.c b/src/platform/pb11mpcore/uart.c new file mode 100644 index 0000000..35d94f5 --- /dev/null +++ b/src/platform/pb11mpcore/uart.c @@ -0,0 +1,28 @@ +/* + * Ties up platform's uart driver functions with generic API + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include INC_PLAT(platform.h) + +#include + +extern struct pl011_uart uart; + +void uart_init() +{ + uart.base = PL011_BASE; + uart.ops.initialise(&uart); +} + +/* Generic uart function that lib/putchar.c expects to see implemented */ +void uart_putc(char c) +{ + int res; + /* Platform specific uart implementation */ + do { + res = uart.ops.tx_char(c); + } while (res < 0); +} + diff --git a/src/platform/pba8/irq.c b/src/platform/pba8/irq.c new file mode 100644 index 0000000..7b63abf --- /dev/null +++ b/src/platform/pba8/irq.c @@ -0,0 +1,57 @@ +/* + * Support for generic irq handling using platform irq controller (PL190) + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include +#include +#include INC_PLAT(irq.h) +#include INC_PLAT(platform.h) +#include INC_ARCH(exception.h) +#include +#include + +struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = { + [0] = { + .name = "Vectored irq controller", + .level = 0, + .cascade = IRQ_SIC, + .offset = 0, + .ops = { + .init = pl190_vic_init, + .read_irq = pl190_read_irq, + .ack_and_mask = pl190_mask_irq, + .unmask = pl190_unmask_irq, + }, + }, + [1] = { + .name = "Secondary irq controller", + .level = 1, + .cascade = IRQ_NIL, + .offset = SIRQ_CHIP_OFFSET, + .ops = { + .init = pl190_sic_init, + .read_irq = pl190_sic_read_irq, + .ack_and_mask = pl190_sic_mask_irq, + .unmask = pl190_sic_unmask_irq, + }, + }, +}; + +static int platform_timer_handler(void) +{ + sp804_irq_handler(); + return do_timer_irq(); +} + +/* Built-in irq handlers initialised at compile time. + * Else register with register_irq() */ +struct irq_desc irq_desc_array[IRQS_MAX] = { + [IRQ_TIMER01] = { + .name = "Timer01", + .chip = &irq_chip_array[0], + .handler = platform_timer_handler, + }, +}; + diff --git a/src/platform/pba8/platform.c b/src/platform/pba8/platform.c new file mode 100644 index 0000000..d59c955 --- /dev/null +++ b/src/platform/pba8/platform.c @@ -0,0 +1,62 @@ +/* + * PB926 platform-specific initialisation and setup + * + * Copyright (C) 2007 Bahadir Balban + */ + +#include +#include +#include +#include INC_ARCH(linker.h) +#include INC_PLAT(printascii.h) +#include INC_SUBARCH(mm.h) +#include INC_SUBARCH(mmu_ops.h) +#include INC_GLUE(memory.h) +#include INC_GLUE(memlayout.h) +#include INC_PLAT(offsets.h) +#include INC_PLAT(platform.h) +#include INC_PLAT(uart.h) +#include INC_PLAT(irq.h) +#include INC_ARCH(asm.h) + +void init_platform_console(void) +{ + add_boot_mapping(PB926_UART0_BASE, PL011_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + + /* + * Map same UART IO area to userspace so that primitive uart-based + * userspace printf can work. Note, this raw mapping is to be + * removed in the future, when file-based io is implemented. + */ + add_boot_mapping(PB926_UART0_BASE, USERSPACE_UART_BASE, PAGE_SIZE, + MAP_USR_IO_FLAGS); + + uart_init(); +} + +void init_platform_timer(void) +{ + add_boot_mapping(PB926_TIMER01_BASE, PLATFORM_TIMER_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SYSCTRL_BASE, PB926_SYSCTRL_VBASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + timer_init(); +} + +void init_platform_irq_controller() +{ + add_boot_mapping(PB926_VIC_BASE, PLATFORM_IRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + add_boot_mapping(PB926_SIC_BASE, PLATFORM_SIRQCTRL_BASE, PAGE_SIZE, + MAP_IO_DEFAULT_FLAGS); + irq_controllers_init(); +} + +void platform_init(void) +{ + init_platform_console(); + init_platform_timer(); + init_platform_irq_controller(); +} + diff --git a/src/platform/pba8/printascii.S b/src/platform/pba8/printascii.S new file mode 100644 index 0000000..e727537 --- /dev/null +++ b/src/platform/pba8/printascii.S @@ -0,0 +1,110 @@ +/* + * Basic UART printing. + */ +#include INC_ARCH(asm.h) +#include INC_GLUE(memlayout.h) + +#define UART_DATA_OFFSET 0x0 + + .macro uart_address rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x10000000 + orreq \rx, \rx, #0x001F0000 + orreq \rx, \rx, #0x00001000 + /* FIXME: This offset is incorrect */ + movne \rx, #0xf9000000 @#IO_AREA0_VADDR + addne \rx, \rx, #PB926_UART0_VOFFSET @ UART0 page offset from + @ virtual io area base. + .endm + + .macro uart_send, ry, rx + strb \ry, [\rx, #UART_DATA_OFFSET] + .endm + + .macro uart_wait, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 5 + bne 501b + .endm + + .macro uart_busy, ry, rx +501: + ldr \ry, [\rx, #0x18] + tst \ry, #1 << 3 + bne 501b + .endm + + .text +/* + * Useful debugging routines + */ +BEGIN_PROC(printhex8) + mov r1, #8 + b printhex + +BEGIN_PROC(printhex4) + mov r1, #4 + b printhex + +BEGIN_PROC(printhex2) + mov r1, #2 +printhex: adr r2, hexbuf +@printhex: ldr r2, =hexbuf + add r3, r2, r1 + mov r1, #0 + strb r1, [r3] +1: and r1, r0, #15 + mov r0, r0, lsr #4 + cmp r1, #10 + addlt r1, r1, #'0' + addge r1, r1, #'a' - 10 + strb r1, [r3, #-1]! + teq r3, r2 + bne 1b + mov r0, r2 + b printascii + + .ltorg + + + .align + + @ vmem-linked image has strings in vmem addresses. This replaces + @ the reference with corresponding physical address. Note this + @ won't work if memory offsets aren't clear cut values for + @ orr'ing and bic'ing. rm = mmu bits rs = string address. + .macro get_straddr rs, rm + mrc p15, 0, \rm, c1, c0 @ Get MMU bits. + tst \rm, #1 @ MMU enabled? + @subeq \rs, \rs, #KERNEL_AREA_START + biceq \rs, \rs, #KERNEL_AREA_START @ Clear Virtual mem offset. + @orreq \rs, \rs, #PHYS_ADDR_BASE @ Add Phy mem offset. + .endm + +BEGIN_PROC(printascii) + get_straddr r0, r1 + uart_address r3 + b 2f +1: uart_wait r2, r3 + uart_send r1, r3 + uart_busy r2, r3 + teq r1, #'\n' + moveq r1, #'\r' + beq 1b +2: teq r0, #0 + ldrneb r1, [r0], #1 + teqne r1, #0 + bne 1b + mov pc, lr +END_PROC(printascii) + +BEGIN_PROC(printch) + uart_address r3 + mov r1, r0 + mov r0, #0 + b 1b + +hexbuf: .space 16 + diff --git a/src/platform/pba8/timer.c b/src/platform/pba8/timer.c new file mode 100644 index 0000000..fc290af --- /dev/null +++ b/src/platform/pba8/timer.c @@ -0,0 +1,28 @@ +/* + * Ties up platform timer with generic timer api + * + * Copyright (C) 2007 Bahadir Balban + * + */ +#include +#include +#include INC_PLAT(platform.h) +#include +#include + +void timer_init(void) +{ + /* Set timer 0 to 1MHz */ + sp810_set_timclk(0, 1); + + /* Initialise timer */ + sp804_init(); +} + +void timer_start(void) +{ + irq_enable(IRQ_TIMER01); + sp804_set_irq(0, 1); /* Enable timer0 irq */ + sp804_enable(0, 1); /* Enable timer0 */ +} + diff --git a/src/platform/pba8/uart.c b/src/platform/pba8/uart.c new file mode 100644 index 0000000..35d94f5 --- /dev/null +++ b/src/platform/pba8/uart.c @@ -0,0 +1,28 @@ +/* + * Ties up platform's uart driver functions with generic API + * + * Copyright (C) 2007 Bahadir Balban + */ +#include +#include INC_PLAT(platform.h) + +#include + +extern struct pl011_uart uart; + +void uart_init() +{ + uart.base = PL011_BASE; + uart.ops.initialise(&uart); +} + +/* Generic uart function that lib/putchar.c expects to see implemented */ +void uart_putc(char c) +{ + int res; + /* Platform specific uart implementation */ + do { + res = uart.ops.tx_char(c); + } while (res < 0); +} +