| /* This file is part of the program psim. |
| |
| Copyright 1994, 1995, 1996, 2003, 2004 Andrew Cagney |
| |
| 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 3 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, see <http://www.gnu.org/licenses/>. |
| |
| */ |
| |
| |
| #ifndef _HW_HTAB_C_ |
| #define _HW_HTAB_C_ |
| |
| #include "device_table.h" |
| #include "device.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"); |
| if (htab_nr_bytes < 0x10000) { |
| device_error(parent, "htab size 0x%x less than 0x1000", |
| htab_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; |
| /* Position the HTABMASK ready for use against a hashed address and |
| not ready for insertion into SDR1.HTABMASK. */ |
| *htabmask = MASKED32(htab_nr_bytes - 1, 7, 31-6); |
| /* Check that the MASK and ADDRESS do not overlap. */ |
| if ((htab_ra & (*htabmask)) != 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_section_size (sec); |
| unsigned_word vma = bfd_section_vma (sec); |
| unsigned_word ra = bfd_section_lma (sec); |
| |
| /* skip the section if no memory to allocate */ |
| if (! (bfd_section_flags (sec) & SEC_ALLOC)) |
| return; |
| |
| if ((bfd_section_flags (sec) & SEC_CODE) |
| || (bfd_section_flags (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_section_flags (sec) & SEC_DATA) |
| || (bfd_section_flags (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_section_flags (sec) & SEC_ALLOC)) |
| return; |
| |
| /* check/ignore any sections of size zero */ |
| section_size = bfd_section_size (sec); |
| if (section_size == 0) |
| return; |
| |
| /* if nothing to load, ignore this one */ |
| if (! (bfd_section_flags (sec) & SEC_LOAD)) |
| return; |
| |
| /* find where it is to go */ |
| section_vma = bfd_section_vma (sec); |
| section_ra = 0; |
| if ((bfd_section_flags (sec) & SEC_CODE) |
| || (bfd_section_flags (sec) & SEC_READONLY)) |
| section_ra = (section_vma - sizes->text_base + sizes->text_ra); |
| else if ((bfd_section_flags (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_section_name (sec), |
| (long)section_vma, |
| (long)section_size, |
| (long)section_ra, |
| (long)bfd_section_flags (sec), |
| bfd_section_flags (sec) & SEC_LOAD ? " LOAD" : "", |
| bfd_section_flags (sec) & SEC_CODE ? " CODE" : "", |
| bfd_section_flags (sec) & SEC_DATA ? " DATA" : "", |
| bfd_section_flags (sec) & SEC_ALLOC ? " ALLOC" : "", |
| bfd_section_flags (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"); |
| free(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.text_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 */ |
| if (sizes.text_bound - sizes.text_base > 0) { |
| 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_ */ |