From a5a693a046bc2a106ec19cc665a149efd245bfb3 Mon Sep 17 00:00:00 2001 From: Kees Jongenburger Date: Tue, 11 Jun 2013 16:07:43 +0200 Subject: [PATCH] arm:no longer rely on a 1:1 phys mapping for device memory. Change-Id: Ie3f61069f882c37dbb81dee813fdfd883e7468cf --- kernel/arch/earm/include/arch_proto.h | 61 +++++++++++++ kernel/arch/earm/memory.c | 119 +++++++++++++++++++++++++- kernel/arch/earm/omap_intr.c | 19 +++- kernel/arch/earm/omap_serial.c | 15 ++++ kernel/arch/earm/omap_timer.c | 8 ++ kernel/arch/earm/pre_init.c | 3 +- 6 files changed, 220 insertions(+), 5 deletions(-) diff --git a/kernel/arch/earm/include/arch_proto.h b/kernel/arch/earm/include/arch_proto.h index f0ae853b9..cdf717ba9 100644 --- a/kernel/arch/earm/include/arch_proto.h +++ b/kernel/arch/earm/include/arch_proto.h @@ -53,6 +53,67 @@ extern void * k_stacks; + 2 * ((cpu) + 1) * K_STACK_SIZE)) +/* + * Definition of a callback used when a memory map changes it's base address + */ +typedef int (*kern_phys_map_mapped)(vir_bytes id, vir_bytes new_addr ); + +/* + * struct used internally by memory.c to keep a list of + * items to map. These should be staticaly allocated + * in the individual files and passed as argument. + * The data doesn't need to be initialized. See omap_serial for + * and example usage. + */ +typedef struct kern_phys_map{ + phys_bytes addr; /* The physical address to map */ + vir_bytes size; /* The size of the mapping */ + vir_bytes id; /* an id passed to the callback */ + kern_phys_map_mapped cb; /* the callback itself */ + phys_bytes vir; /* The virtual address once remapped */ + int index; /* index */ + struct kern_phys_map *next; /* pointer to the next */ +} kern_phys_map ; + + +/* + * Request an in kernel physical mapping. + * + * On ARM many devices are memory mapped and some of these devices + * are used in the kernel. These device can be things like serial + * lines, interrupt controller and clocks. The kernel needs to be + * able to access these devices at the various stages of booting. + * During startup, until arch_enable_paging is called, it is the + * kernel whom is controlling the mappings and it often needs to + * access the memory using a 1:1 mapping between virtual and + * physical memory. + * + * Once processes start to run it is no longer desirable for the + * kernel to have devices mapped in the middle of the process + * address space. + * + * This method requests the memory manager to map base_address/size + * in the kernel address space and call back the kernel when this + * mapping takes effect (after enable_paging). + * + * Before the callback is called it is up to the kernel to use it's + * own addressing. The callback will happen *after* the kernel lost + * it's initial mapping. It it therefore not safe to use the initial + * mapping in the callback. It also is not possible to use printf for + * the same reason. + */ +int kern_req_phys_map( phys_bytes base_address, vir_bytes io_size, + kern_phys_map * priv, kern_phys_map_mapped cb, + vir_bytes id); + +/* + * Request a physical mapping and put the result in the given prt + * Note that ptr will only be valid once the callback happend. + */ +int kern_phys_map_ptr( phys_bytes base_address, vir_bytes io_size, + kern_phys_map * priv, vir_bytes ptr); + + /* functions defined in architecture-independent kernel source. */ #include "kernel/proto.h" diff --git a/kernel/arch/earm/memory.c b/kernel/arch/earm/memory.c index 4d274d5b8..c7cdc146c 100644 --- a/kernel/arch/earm/memory.c +++ b/kernel/arch/earm/memory.c @@ -29,6 +29,8 @@ static int freepdes[MAXFREEPDES]; static u32_t phys_get32(phys_bytes v); +/* list of requested physical mapping */ +static kern_phys_map *kern_phys_map_head; void mem_clear_mapcache(void) { @@ -260,7 +262,7 @@ vir_bytes bytes; /* # of bytes to be copied */ phys = 0; } else { if(phys == 0) - panic("vm_lookup returned phys: %d", phys); + panic("vm_lookup returned phys: 0x%lx", phys); } if(phys == 0) { @@ -692,6 +694,8 @@ int arch_phys_map(const int index, int *flags) { static int first = 1; + kern_phys_map *phys_maps; + int freeidx = 0; u32_t glo_len = (u32_t) &usermapped_nonglo_start - (u32_t) &usermapped_start; @@ -709,6 +713,14 @@ int arch_phys_map(const int index, if(usermapped_glo_index != -1) first_um_idx = usermapped_glo_index; first = 0; + + /* list over the maps and index them */ + phys_maps = kern_phys_map_head; + while(phys_maps != NULL){ + phys_maps->index = freeidx++; + phys_maps = phys_maps->next; + } + } if(index == usermapped_glo_index) { @@ -750,6 +762,18 @@ int arch_phys_map(const int index, *len = ARM_PAGE_SIZE; *flags = VMMF_USER; return OK; + } + /* if this all fails loop over the maps */ + /* list over the maps and index them */ + phys_maps = kern_phys_map_head; + while(phys_maps != NULL){ + if(phys_maps->index == index){ + *addr = phys_maps->addr; + *len = phys_maps->size; + *flags = VMMF_UNCACHED | VMMF_WRITE; + return OK; + } + phys_maps = phys_maps->next; } return EINVAL; @@ -757,6 +781,8 @@ int arch_phys_map(const int index, int arch_phys_map_reply(const int index, const vir_bytes addr) { + kern_phys_map *phys_maps; + if(index == first_um_idx) { u32_t usermapped_offset; assert(addr > (u32_t) &usermapped_start); @@ -797,16 +823,46 @@ int arch_phys_map_reply(const int index, const vir_bytes addr) return OK; } + /* if this all fails loop over the maps */ + /* list over the maps and index them */ + phys_maps = kern_phys_map_head; + while(phys_maps != NULL){ + if(phys_maps->index == index){ + assert(phys_maps->cb != NULL); + /* only update the vir addr we are + going to call the callback in enable + paging + */ + phys_maps->vir = addr; + return OK; + } + phys_maps = phys_maps->next; + } + return EINVAL; } int arch_enable_paging(struct proc * caller) { + kern_phys_map *phys_maps; assert(caller->p_seg.p_ttbr); + /* load caller's page table */ switch_address_space(caller); + /* We have now switched address spaces and the mappings are + valid. We can now remap previous mappings. This is not a + good time to do printf as the initial massing is gone and + the new mapping is not in place */ + phys_maps = kern_phys_map_head; + while(phys_maps != NULL){ + assert(phys_maps->cb != NULL); + phys_maps->cb(phys_maps->id, phys_maps->vir); + phys_maps = phys_maps->next; + } + + device_mem = (char *) device_mem_vaddr; return OK; @@ -817,3 +873,64 @@ void release_address_space(struct proc *pr) pr->p_seg.p_ttbr_v = NULL; barrier(); } + + + +/* + * Request a physical mapping + */ +int kern_req_phys_map( phys_bytes base_address, vir_bytes io_size, + kern_phys_map * priv, kern_phys_map_mapped cb, + vir_bytes id) +{ + /* Assign the values to the given struct and add priv + to the list */ + assert(base_address != 0); + assert(io_size % ARM_PAGE_SIZE == 0); + assert(cb != NULL); + + priv->addr = base_address; + priv->size = io_size; + priv->cb = cb; + priv->id = id; + priv->index = -1; + priv->next = NULL; + + + if (kern_phys_map_head == NULL){ + /* keep a list of items this is the first one */ + kern_phys_map_head = priv; + kern_phys_map_head->next = NULL; + } else { + /* insert the item head but first keep track + of the current by putting it in next */ + priv->next = kern_phys_map_head; + /* replace the head */ + kern_phys_map_head = priv; + } + return 0; +} + +/* + * Callback implementation where the id given to the + * kern_phys_map is a pointer to the io map base address. + * this implementation will change that base address. + */ +int kern_phys_map_mapped_ptr(vir_bytes id, phys_bytes address){ + *((vir_bytes*)id) = address; + return 0; +} + +/* + * Request a physical mapping and put the result in the given prt + * Note that ptr will only be valid once the callback happend. + */ +int kern_phys_map_ptr( + phys_bytes base_address, + vir_bytes io_size, + kern_phys_map * priv, + vir_bytes ptr) +{ + return kern_req_phys_map(base_address,io_size,priv,kern_phys_map_mapped_ptr,ptr); +} + diff --git a/kernel/arch/earm/omap_intr.c b/kernel/arch/earm/omap_intr.c index 4cf778202..baa3a901a 100644 --- a/kernel/arch/earm/omap_intr.c +++ b/kernel/arch/earm/omap_intr.c @@ -2,20 +2,33 @@ #include #include #include -#include "omap_intr.h" +#include "kernel/kernel.h" +#include "kernel/proc.h" +#include "kernel/vm.h" +#include "kernel/proto.h" +#include "arch_proto.h" + +#include "omap_intr.h" static struct omap_intr { vir_bytes base; + int size; } omap_intr; + +static kern_phys_map intr_phys_map; + int intr_init(const int auto_eoi) { #ifdef DM37XX - omap_intr.base = OMAP3_DM37XX_INTR_BASE; + omap_intr.base = OMAP3_DM37XX_INTR_BASE; #endif #ifdef AM335X - omap_intr.base = OMAP3_AM335X_INTR_BASE; + omap_intr.base = OMAP3_AM335X_INTR_BASE; #endif + omap_intr.size = 0x1000 ; /* 4K */ + + kern_phys_map_ptr(omap_intr.base,omap_intr.size,&intr_phys_map,&omap_intr.base); return 0; } diff --git a/kernel/arch/earm/omap_serial.c b/kernel/arch/earm/omap_serial.c index 9113edb2a..8196275fc 100644 --- a/kernel/arch/earm/omap_serial.c +++ b/kernel/arch/earm/omap_serial.c @@ -3,16 +3,27 @@ #include #include #include + +#include "kernel/kernel.h" +#include "kernel/proc.h" +#include "kernel/vm.h" +#include "kernel/proto.h" +#include "arch_proto.h" + #include "omap_serial.h" + struct omap_serial { vir_bytes base; + vir_bytes size; }; static struct omap_serial omap_serial = { .base = 0, }; +static kern_phys_map serial_phys_map; + /* * In kernel serial for the omap. The serial driver like most other * drivers needs to be started early and even before the MMU is turned on. @@ -37,6 +48,10 @@ void omap3_ser_init(){ #ifdef AM335X omap_serial.base = OMAP3_AM335X_DEBUG_UART_BASE; #endif + omap_serial.size = 0x1000 ; /* 4k */ + + + kern_phys_map_ptr(omap_serial.base,omap_serial.size,&serial_phys_map,&omap_serial.base); assert(omap_serial.base); } diff --git a/kernel/arch/earm/omap_timer.c b/kernel/arch/earm/omap_timer.c index 2cb2c1815..ca458a438 100644 --- a/kernel/arch/earm/omap_timer.c +++ b/kernel/arch/earm/omap_timer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -141,10 +142,16 @@ int omap3_register_timer_handler(const irq_handler_t handler) return 0; } + +/* meta data for remapping */ +static kern_phys_map timer_phys_map; +static kern_phys_map fr_timer_phys_map; + void omap3_frclock_init(void) { u32_t tisr; + kern_phys_map_ptr(fr_timer.base,ARM_PAGE_SIZE,&fr_timer_phys_map,&fr_timer.base); /* enable the clock */ #ifdef AM335X /* Disable the module and wait for the module to be disabled */ @@ -202,6 +209,7 @@ void omap3_frclock_stop() void omap3_timer_init(unsigned freq) { u32_t tisr; + kern_phys_map_ptr(timer.base,ARM_PAGE_SIZE,&timer_phys_map,&timer.base); #ifdef AM335X /* disable the module and wait for the module to be disabled */ set32(CM_WKUP_TIMER1_CLKCTRL, CM_MODULEMODE_MASK,CM_MODULEMODE_DISABLED); diff --git a/kernel/arch/earm/pre_init.c b/kernel/arch/earm/pre_init.c index b61363526..646fb4556 100644 --- a/kernel/arch/earm/pre_init.c +++ b/kernel/arch/earm/pre_init.c @@ -296,4 +296,5 @@ int send_sig(endpoint_t proc_nr, int sig_nr) { return 0; } void minix_shutdown(timer_t *t) { arch_shutdown(RBT_PANIC); } void busy_delay_ms(int x) { } int raise(int n) { panic("raise(%d)\n", n); } - +int kern_phys_map_ptr( phys_bytes base_address, vir_bytes io_size, + struct kern_phys_map * priv, vir_bytes ptr) {};