#ifndef _HW_HTAB_C_
#define _HW_HTAB_C_
#include "device_table.h"
#include "bfd.h"
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);
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)
{
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,
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)) {
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 {
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,
pte,
sizeof(target_pte0),
1) != 4
|| device_dma_write_buffer(device_parent(me),
&target_pte1,
0,
pte + 4,
sizeof(target_pte1),
1) != 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;
}
}
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;
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;
if (memory != NULL)
claim_memory(me, memory, pte_ra, nr_bytes);
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);
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;
if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC))
return;
section_size = bfd_get_section_size_before_reloc(sec);
if (section_size == 0)
return;
if (! (bfd_get_section_flags(abfd, sec) & SEC_LOAD))
return;
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;
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" : ""
));
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 ,
section_ra,
section_size,
1 )
!= section_size)
device_error(me, "broken dma transfer");
zfree(section_init);
}
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;
image = bfd_openr(file_name, NULL);
if (image == NULL) {
bfd_perror("devices/pte");
device_error(me, "the file %s not loaded", file_name);
}
if (!bfd_check_format(image, bfd_object)) {
bfd_close(image);
device_error(me, "the file %s has an invalid binary format", file_name);
}
bfd_map_over_sections (image, htab_sum_binary, (PTR) &sizes);
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));
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"));
if ((sizes.text_base - sizes.text_ra)
!= (sizes.data_base - sizes.data_ra))
device_error(me, "overlapping but missaligned text and data segments");
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;
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));
}
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);
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");
if (device_find_property(me, "claim") != NULL)
memory = tree_find_ihandle_property(me, "/chosen/memory");
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");
}
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) {
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 {
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, },
{ NULL, },
{ passthrough_device_dma_read_buffer,
passthrough_device_dma_write_buffer, },
{ NULL, },
{ generic_device_unit_decode,
generic_device_unit_encode, },
};
const device_descriptor hw_htab_device_descriptor[] = {
{ "htab", NULL, &htab_callbacks },
{ "pte", NULL, &htab_callbacks },
{ NULL },
};
#endif