diff options
Diffstat (limited to 'sim/ppc/hw_htab.c')
-rw-r--r-- | sim/ppc/hw_htab.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/sim/ppc/hw_htab.c b/sim/ppc/hw_htab.c new file mode 100644 index 00000000000..35aa5711371 --- /dev/null +++ b/sim/ppc/hw_htab.c @@ -0,0 +1,683 @@ +/* This file is part of the program psim. + + Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + + +#ifndef _HW_HTAB_C_ +#define _HW_HTAB_C_ + +#include "device_table.h" + +#include "bfd.h" + + +/* DEVICE + + + htab - pseudo-device describing a PowerPC hash table + + + DESCRIPTION + + + During the initialization of the device tree, the pseudo-device + <<htab>>, in conjunction with any child <<pte>> pseudo-devices, + will create a PowerPC hash table in memory. The hash table values + are written using dma transfers. + + The size and address of the hash table are determined by properties + of the htab node. + + By convention, the htab device is made a child of the + <</openprom/init>> node. + + By convention, the real address of the htab is used as the htab + nodes unit address. + + + PROPERTIES + + + real-address = <address> (required) + + The physical address of the hash table. The PowerPC architecture + places limitations on what is a valid hash table real-address. + + + nr-bytes = <size> (required) + + The size of the hash table (in bytes) that is to be created at + <<real-address>>. The PowerPC architecture places limitations on + what is a valid hash table size. + + + claim = <anything> (optional) + + If this property is present, the memory used to construct the hash + table will be claimed from the memory device. The memory device + being specified by the <</chosen/memory>> ihandle property. + + + EXAMPLES + + Enable tracing. + + | $ psim -t htab-device \ + + + Create a htab specifying the base address and minimum size. + + | -o '/openprom/init/htab@0x10000/real-address 0x10000' \ + | -o '/openprom/init/htab@0x10000/claim 0' \ + | -o '/openprom/init/htab@0x10000/nr-bytes 65536' \ + + + BUGS + + + See the <<pte>> device. + + + */ + + +/* DEVICE + + + pte - pseudo-device describing a htab entry + + + DESCRIPTION + + + The <<pte>> pseudo-device, which must be a child of a <<htabl>> + node, describes a virtual to physical mapping that is to be entered + into the parents hash table. + + Two alternative specifications of the mapping are allowed. Either + a section of physical memory can be mapped to a virtual address, or + the header of an executible image can be used to define the + mapping. + + By convention, the real address of the map is specified as the pte + devices unit address. + + + PROPERTIES + + + real-address = <address> (required) + + The starting physical address that is to be mapped by the hash + table. + + + wimg = <int> (required) + pp = <int> (required) + + The value of hash table protection bits that are to be used when + creating the virtual to physical address map. + + + claim = <anything> (optional) + + If this property is present, the real memory that is being mapped by the + hash table will be claimed from the memory node (specified by the + ihandle <</chosen/memory>>). + + + virtual-address = <integer> [ <integer> ] (option A) + nr-bytes = <size> (option A) + + Option A - Virtual virtual address (and size) at which the physical + address is to be mapped. If multiple values are specified for the + virtual address then they are concatenated to gether to form a + longer virtual address. + + + file-name = <string> (option B) + + Option B - An executable image that is to be loaded (starting at + the physical address specified above) and then mapped in using + informatioin taken from the executables header. information found + in the files header. + + + EXAMPLES + + + Enable tracing (note that both the <<htab>> and <<pte>> device use the + same trace option). + + | -t htab-device \ + + + Map a block of physical memory into a specified virtual address: + + | -o '/openprom/init/htab/pte@0x0/real-address 0' \ + | -o '/openprom/init/htab/pte@0x0/nr-bytes 4096' \ + | -o '/openprom/init/htab/pte@0x0/virtual-address 0x1000000' \ + | -o '/openprom/init/htab/pte@0x0/claim 0' \ + | -o '/openprom/init/htab/pte@0x0/wimg 0x7' \ + | -o '/openprom/init/htab/pte@0x0/pp 0x2' \ + + + Map a file into memory. + + | -o '/openprom/init/htab/pte@0x10000/real-address 0x10000' \ + | -o '/openprom/init/htab/pte@0x10000/file-name "netbsd.elf' \ + | -o '/openprom/init/htab/pte@0x10000/wimg 0x7' \ + | -o '/openprom/init/htab/pte@0x10000/pp 0x2' \ + + + BUGS + + + For an ELF executable, the header defines both the virtual and real + address at which each file section should be loaded. At present, the + real addresses that are specified in the header are ignored, the file + instead being loaded in to physical memory in a linear fashion. + + When claiming memory, this device assumes that the #address-cells + and #size-cells is one. For future implementations, this may not + be the case. + + */ + + + +static void +htab_decode_hash_table(device *me, + unsigned32 *htaborg, + unsigned32 *htabmask) +{ + unsigned_word htab_ra; + unsigned htab_nr_bytes; + unsigned n; + device *parent = device_parent(me); + /* determine the location/size of the hash table */ + if (parent == NULL + || strcmp(device_name(parent), "htab") != 0) + device_error(parent, "must be a htab device"); + htab_ra = device_find_integer_property(parent, "real-address"); + htab_nr_bytes = device_find_integer_property(parent, "nr-bytes"); + for (n = htab_nr_bytes; n > 1; n = n / 2) { + if (n % 2 != 0) + device_error(parent, "htab size 0x%x not a power of two", + htab_nr_bytes); + } + *htaborg = htab_ra; + *htabmask = MASKED32(htab_nr_bytes - 1, 7, 31-6); + if ((htab_ra & INSERTED32(*htabmask, 7, 15)) != 0) { + device_error(parent, "htaborg 0x%lx not aligned to htabmask 0x%lx", + (unsigned long)*htaborg, (unsigned long)*htabmask); + } + DTRACE(htab, ("htab - htaborg=0x%lx htabmask=0x%lx\n", + (unsigned long)*htaborg, (unsigned long)*htabmask)); +} + +static void +htab_map_page(device *me, + unsigned_word ra, + unsigned64 va, + unsigned wimg, + unsigned pp, + unsigned32 htaborg, + unsigned32 htabmask) +{ + /* keep everything left shifted so that the numbering is easier */ + unsigned64 vpn = va << 12; + unsigned32 vsid = INSERTED32(EXTRACTED64(vpn, 0, 23), 0, 23); + unsigned32 vpage = INSERTED32(EXTRACTED64(vpn, 24, 39), 0, 15); + unsigned32 hash = INSERTED32(EXTRACTED32(vsid, 5, 23) + ^ EXTRACTED32(vpage, 0, 15), + 7, 31-6); + int h; + for (h = 0; h < 2; h++) { + unsigned32 pteg = (htaborg | (hash & htabmask)); + int pti; + for (pti = 0; pti < 8; pti++) { + unsigned32 pte = pteg + 8 * pti; + unsigned32 current_target_pte0; + unsigned32 current_pte0; + if (device_dma_read_buffer(device_parent(me), + ¤t_target_pte0, + 0, /*space*/ + pte, + sizeof(current_target_pte0)) != 4) + device_error(me, "failed to read a pte at 0x%lx", (unsigned long)pte); + current_pte0 = T2H_4(current_target_pte0); + if (MASKED32(current_pte0, 0, 0)) { + /* full pte, check it isn't already mapping the same virtual + address */ + unsigned32 curr_vsid = INSERTED32(EXTRACTED32(current_pte0, 1, 24), 0, 23); + unsigned32 curr_api = INSERTED32(EXTRACTED32(current_pte0, 26, 31), 0, 5); + unsigned32 curr_h = EXTRACTED32(current_pte0, 25, 25); + if (curr_h == h + && curr_vsid == vsid + && curr_api == MASKED32(vpage, 0, 5)) + device_error(me, "duplicate map - va=0x%08lx ra=0x%lx vsid=0x%lx h=%d vpage=0x%lx hash=0x%lx pteg=0x%lx+%2d pte0=0x%lx", + (unsigned long)va, + (unsigned long)ra, + (unsigned long)vsid, + h, + (unsigned long)vpage, + (unsigned long)hash, + (unsigned long)pteg, + pti * 8, + (unsigned long)current_pte0); + } + else { + /* empty pte fill it */ + unsigned32 pte0 = (MASK32(0, 0) + | INSERTED32(EXTRACTED32(vsid, 0, 23), 1, 24) + | INSERTED32(h, 25, 25) + | INSERTED32(EXTRACTED32(vpage, 0, 5), 26, 31)); + unsigned32 target_pte0 = H2T_4(pte0); + unsigned32 pte1 = (INSERTED32(EXTRACTED32(ra, 0, 19), 0, 19) + | INSERTED32(wimg, 25, 28) + | INSERTED32(pp, 30, 31)); + unsigned32 target_pte1 = H2T_4(pte1); + if (device_dma_write_buffer(device_parent(me), + &target_pte0, + 0, /*space*/ + pte, + sizeof(target_pte0), + 1/*ro?*/) != 4 + || device_dma_write_buffer(device_parent(me), + &target_pte1, + 0, /*space*/ + pte + 4, + sizeof(target_pte1), + 1/*ro?*/) != 4) + device_error(me, "failed to write a pte a 0x%lx", (unsigned long)pte); + DTRACE(htab, ("map - va=0x%08lx ra=0x%lx vsid=0x%lx h=%d vpage=0x%lx hash=0x%lx pteg=0x%lx+%2d pte0=0x%lx pte1=0x%lx\n", + (unsigned long)va, + (unsigned long)ra, + (unsigned long)vsid, + h, + (unsigned long)vpage, + (unsigned long)hash, + (unsigned long)pteg, + pti * 8, + (unsigned long)pte0, + (unsigned long)pte1)); + return; + } + } + /* re-hash */ + hash = MASKED32(~hash, 0, 18); + } +} + +static unsigned_word +claim_memory(device *me, + device_instance *memory, + unsigned_word ra, + unsigned_word size) +{ + unsigned32 args[3]; + unsigned32 results[1]; + int status; + args[0] = 0; /* alignment */ + args[1] = size; + args[2] = ra; + status = device_instance_call_method(memory, "claim", 3, args, 1, results); + if (status != 0) + device_error(me, "failed to claim memory"); + return results[0]; +} + +static void +htab_map_region(device *me, + device_instance *memory, + unsigned_word pte_ra, + unsigned64 pte_va, + unsigned nr_bytes, + unsigned wimg, + unsigned pp, + unsigned32 htaborg, + unsigned32 htabmask) +{ + unsigned_word ra; + unsigned64 va; + /* claim the memory */ + if (memory != NULL) + claim_memory(me, memory, pte_ra, nr_bytes); + /* go through all pages and create a pte for each */ + for (ra = pte_ra, va = pte_va; + ra < pte_ra + nr_bytes; + ra += 0x1000, va += 0x1000) { + htab_map_page(me, ra, va, wimg, pp, htaborg, htabmask); + } +} + +typedef struct _htab_binary_sizes { + unsigned_word text_ra; + unsigned_word text_base; + unsigned_word text_bound; + unsigned_word data_ra; + unsigned_word data_base; + unsigned data_bound; + device *me; +} htab_binary_sizes; + +static void +htab_sum_binary(bfd *abfd, + sec_ptr sec, + PTR data) +{ + htab_binary_sizes *sizes = (htab_binary_sizes*)data; + unsigned_word size = bfd_get_section_size_before_reloc (sec); + unsigned_word vma = bfd_get_section_vma (abfd, sec); +#define bfd_get_section_lma(abfd, sec) ((sec)->lma + 0) + unsigned_word ra = bfd_get_section_lma (abfd, sec); + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) + return; + + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) + || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) { + if (sizes->text_bound < vma + size) + sizes->text_bound = ALIGN_PAGE(vma + size); + if (sizes->text_base > vma) + sizes->text_base = FLOOR_PAGE(vma); + if (sizes->text_ra > ra) + sizes->text_ra = FLOOR_PAGE(ra); + } + else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA) + || (bfd_get_section_flags (abfd, sec) & SEC_ALLOC)) { + if (sizes->data_bound < vma + size) + sizes->data_bound = ALIGN_PAGE(vma + size); + if (sizes->data_base > vma) + sizes->data_base = FLOOR_PAGE(vma); + if (sizes->data_ra > ra) + sizes->data_ra = FLOOR_PAGE(ra); + } +} + +static void +htab_dma_binary(bfd *abfd, + sec_ptr sec, + PTR data) +{ + htab_binary_sizes *sizes = (htab_binary_sizes*)data; + void *section_init; + unsigned_word section_vma; + unsigned_word section_size; + unsigned_word section_ra; + device *me = sizes->me; + + /* skip the section if no memory to allocate */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) + return; + + /* check/ignore any sections of size zero */ + section_size = bfd_get_section_size_before_reloc(sec); + if (section_size == 0) + return; + + /* if nothing to load, ignore this one */ + if (! (bfd_get_section_flags(abfd, sec) & SEC_LOAD)) + return; + + /* find where it is to go */ + section_vma = bfd_get_section_vma(abfd, sec); + section_ra = 0; + if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) + || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) + section_ra = (section_vma - sizes->text_base + sizes->text_ra); + else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA)) + section_ra = (section_vma - sizes->data_base + sizes->data_ra); + else + return; /* just ignore it */ + + DTRACE(htab, + ("load - name=%-7s vma=0x%.8lx size=%6ld ra=0x%.8lx flags=%3lx(%s%s%s%s%s )\n", + bfd_get_section_name(abfd, sec), + (long)section_vma, + (long)section_size, + (long)section_ra, + (long)bfd_get_section_flags(abfd, sec), + bfd_get_section_flags(abfd, sec) & SEC_LOAD ? " LOAD" : "", + bfd_get_section_flags(abfd, sec) & SEC_CODE ? " CODE" : "", + bfd_get_section_flags(abfd, sec) & SEC_DATA ? " DATA" : "", + bfd_get_section_flags(abfd, sec) & SEC_ALLOC ? " ALLOC" : "", + bfd_get_section_flags(abfd, sec) & SEC_READONLY ? " READONLY" : "" + )); + + /* dma in the sections data */ + section_init = zalloc(section_size); + if (!bfd_get_section_contents(abfd, + sec, + section_init, 0, + section_size)) { + bfd_perror("devices/pte"); + device_error(me, "no data loaded"); + } + if (device_dma_write_buffer(device_parent(me), + section_init, + 0 /*space*/, + section_ra, + section_size, + 1 /*violate_read_only*/) + != section_size) + device_error(me, "broken dma transfer"); + zfree(section_init); /* only free if load */ +} + +/* create a memory map from a binaries virtual addresses to a copy of + the binary laid out linearly in memory */ + +static void +htab_map_binary(device *me, + device_instance *memory, + unsigned_word ra, + unsigned wimg, + unsigned pp, + const char *file_name, + unsigned32 htaborg, + unsigned32 htabmask) +{ + htab_binary_sizes sizes; + bfd *image; + sizes.text_ra = -1; + sizes.data_ra = -1; + sizes.text_base = -1; + sizes.data_base = -1; + sizes.text_bound = 0; + sizes.data_bound = 0; + sizes.me = me; + + /* open the file */ + image = bfd_openr(file_name, NULL); + if (image == NULL) { + bfd_perror("devices/pte"); + device_error(me, "the file %s not loaded", file_name); + } + + /* check it is valid */ + if (!bfd_check_format(image, bfd_object)) { + bfd_close(image); + device_error(me, "the file %s has an invalid binary format", file_name); + } + + /* determine the size of each of the files regions */ + bfd_map_over_sections (image, htab_sum_binary, (PTR) &sizes); + + /* if needed, determine the real addresses of the sections */ + if (ra != -1) { + sizes.text_ra = ra; + sizes.data_ra = ALIGN_PAGE(sizes.text_ra + + (sizes.text_bound - sizes.text_base)); + } + + DTRACE(htab, ("text map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.text_base, + (unsigned long)sizes.text_bound, + (unsigned long)sizes.text_ra)); + DTRACE(htab, ("data map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.data_base, + (unsigned long)sizes.data_bound, + (unsigned long)sizes.data_ra)); + + /* check for and fix a botched image (text and data segments + overlap) */ + if ((sizes.text_base <= sizes.data_base + && sizes.text_bound >= sizes.data_bound) + || (sizes.data_base <= sizes.text_base + && sizes.data_bound >= sizes.data_bound) + || (sizes.text_bound > sizes.data_base + && sizes.text_bound <= sizes.data_bound) + || (sizes.text_base >= sizes.data_base + && sizes.text_base < sizes.data_bound)) { + DTRACE(htab, ("text and data segment overlaped - using just data segment\n")); + /* check va->ra linear */ + if ((sizes.text_base - sizes.text_ra) + != (sizes.data_base - sizes.data_ra)) + device_error(me, "overlapping but missaligned text and data segments"); + /* enlarge the data segment */ + if (sizes.text_base < sizes.data_base) + sizes.data_base = sizes.text_base; + if (sizes.text_bound > sizes.data_bound) + sizes.data_bound = sizes.text_bound; + if (sizes.text_ra < sizes.data_ra) + sizes.data_ra = sizes.text_ra; + /* zap the text segment */ + sizes.text_base = 0; + sizes.text_bound = 0; + sizes.text_ra = 0; + DTRACE(htab, ("common map - base=0x%lx bound=0x%lx-1 ra=0x%lx\n", + (unsigned long)sizes.data_base, + (unsigned long)sizes.data_bound, + (unsigned long)sizes.data_ra)); + } + + /* set up virtual memory maps for each of the regions */ + htab_map_region(me, memory, sizes.text_ra, sizes.text_base, + sizes.text_bound - sizes.text_base, + wimg, pp, + htaborg, htabmask); + + htab_map_region(me, memory, sizes.data_ra, sizes.data_base, + sizes.data_bound - sizes.data_base, + wimg, pp, + htaborg, htabmask); + + /* dma the sections into physical memory */ + bfd_map_over_sections (image, htab_dma_binary, (PTR) &sizes); +} + +static void +htab_init_data_callback(device *me) +{ + device_instance *memory = NULL; + if (WITH_TARGET_WORD_BITSIZE != 32) + device_error(me, "only 32bit targets currently suported"); + + /* find memory device */ + if (device_find_property(me, "claim") != NULL) + memory = tree_find_ihandle_property(me, "/chosen/memory"); + + /* for the htab, just allocate space for it */ + if (strcmp(device_name(me), "htab") == 0) { + unsigned_word address = device_find_integer_property(me, "real-address"); + unsigned_word length = device_find_integer_property(me, "nr-bytes"); + unsigned_word base = claim_memory(me, memory, address, length); + if (base == -1 || base != address) + device_error(me, "cannot allocate hash table"); + } + + /* for the pte, do all the real work */ + if (strcmp(device_name(me), "pte") == 0) { + unsigned32 htaborg; + unsigned32 htabmask; + + htab_decode_hash_table(me, &htaborg, &htabmask); + + if (device_find_property(me, "file-name") != NULL) { + /* map in a binary */ + unsigned pte_wimg = device_find_integer_property(me, "wimg"); + unsigned pte_pp = device_find_integer_property(me, "pp"); + const char *file_name = device_find_string_property(me, "file-name"); + if (device_find_property(me, "real-address") != NULL) { + unsigned32 pte_ra = device_find_integer_property(me, "real-address"); + DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, file-name=%s\n", + (unsigned long)pte_ra, + (unsigned long)pte_wimg, + (long)pte_pp, + file_name)); + htab_map_binary(me, memory, pte_ra, pte_wimg, pte_pp, file_name, + htaborg, htabmask); + } + else { + DTRACE(htab, ("pte - wimg=%ld, pp=%ld, file-name=%s\n", + (unsigned long)pte_wimg, + (long)pte_pp, + file_name)); + htab_map_binary(me, memory, -1, pte_wimg, pte_pp, file_name, + htaborg, htabmask); + } + } + else { + /* handle a normal mapping definition */ + unsigned64 pte_va = 0; + unsigned32 pte_ra = device_find_integer_property(me, "real-address"); + unsigned pte_nr_bytes = device_find_integer_property(me, "nr-bytes"); + unsigned pte_wimg = device_find_integer_property(me, "wimg"); + unsigned pte_pp = device_find_integer_property(me, "pp"); + signed_cell partial_va; + int i; + for (i = 0; + device_find_integer_array_property(me, "virtual-address", i, &partial_va); + i++) { + pte_va = (pte_va << WITH_TARGET_WORD_BITSIZE) | (unsigned_cell)partial_va; + } + DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, va=0x%lx, nr_bytes=%ld\n", + (unsigned long)pte_ra, + (long)pte_wimg, + (long)pte_pp, + (unsigned long)pte_va, + (long)pte_nr_bytes)); + htab_map_region(me, memory, pte_ra, pte_va, pte_nr_bytes, pte_wimg, pte_pp, + htaborg, htabmask); + } + } +} + + +static device_callbacks const htab_callbacks = { + { NULL, htab_init_data_callback, }, + { NULL, }, /* address */ + { NULL, }, /* IO */ + { passthrough_device_dma_read_buffer, + passthrough_device_dma_write_buffer, }, + { NULL, }, /* interrupt */ + { generic_device_unit_decode, + generic_device_unit_encode, }, +}; + +const device_descriptor hw_htab_device_descriptor[] = { + { "htab", NULL, &htab_callbacks }, + { "pte", NULL, &htab_callbacks }, /* yep - uses htab's table */ + { NULL }, +}; + +#endif /* _HW_HTAB_C_ */ |